Git Product home page Git Product logo

libgdiplus's Issues

Update build instructions for macOS

The current build instructions for builds with X11 support cause an outdated cairo to be used. There's Cairo 1.14.6 distributed with XQuartz and the suggested build command (in README.md) picks it up instead of the cairo build from Homebrew.

To pick-up the Homebrew Cairo it needs to be changed from

PKG_CONFIG_PATH=/opt/X11/lib/pkgconfig ./autogen.sh --prefix=YOUR_PREFIX

to

PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig  ./autogen.sh --prefix=YOUR_PREFIX

Unfortunately the default cairo Homebrew build is built without X11 support, so furthermore the following is needed:

brew uninstall cairo
brew install --with-x11 cairo

Support WMF files where left > right

In test_valid in testwmfcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE leftGreaterThanRight[] = {
		/* Placeable Header */ 0xD7, 0xCD, 0xC6, 0x9A, 0x00, 0x00, 0x00, 0x01, 0xCE, 0xF2, 0x00, 0x00, 0x32, 0x0d, 0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, 0xAA,
		/* Metafile Header */  0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
		/* META_EOF */         0x03, 0x00, 0x00, 0x00, 0x00, 0x00
	};
#endif

	// FIXME: GDI+ uses abs(width).
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (leftGreaterThanRight, 0, -3378, 256, 6756, 650.239929f, 17160.2383f);
#endif

Support 2bpp grayscale PNGs

In test_valid2bpp in testpngcodec.

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE grayscale2bpp1x1Interlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x01, 0x07, 0xC9, 0xB3, 0x62,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x70, 0x00, 0x00, 0x00, 0x42, 0x00, 0x41, 0xF9, 0xFB, 0x3C, 0x49,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE grayscale2bpp6x4NotInterlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xDF, 0x09, 0x3E,
		/* IDAT */      0x00, 0x00, 0x00, 0x14, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x08, 0x0D, 0x60, 0xC8, 0x9A, 0xC0, 0x50, 0x70, 0x81, 0x81, 0x81, 0x01, 0x00, 0x13, 0x83, 0x02, 0xE0, 0x20, 0x93, 0x79, 0xCF,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE grayscale2bpp1x1WithPalette[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x01, 0x07, 0xC9, 0xB3, 0x62,
		/* PLTE */      0x00, 0x00, 0x00, 0x09, 'P', 'L', 'T', 'E', 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xE6, 0xE6, 0xFA, 0x0D, 0xB2, 0xEB, 0x46,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x70, 0x00, 0x00, 0x00, 0x42, 0x00, 0x41, 0xF9, 0xFB, 0x3C, 0x49,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
#endif

	// FIXME: this causes an AV in libgdiplus when trying to convert this image to 32bpp as we assume
	// the image has a palette.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (grayscale2bpp1x1Interlaced, PixelFormat32bppARGB, 1, 1, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (grayscale2bpp6x4NotInterlaced, PixelFormat32bppARGB, 6, 4, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (grayscale2bpp1x1WithPalette, PixelFormat32bppARGB, 1, 1, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
#endif

Support giflib on windows

This is the only missing library on Windows libgdiplus

I can't see a nuget package anywhere, maybe I'll create one :D

GdipFillRegion produces an incorrect result in excluded area

Steps to Reproduce

#ifdef _WIN32
#   include <Windows.h>
#   include <gdiplus.h>
    using namespace Gdiplus;
    using namespace Gdiplus::DllExports;
#   pragma comment(lib, "gdiplus.lib")
#endif

#include <GdiPlusFlat.h>
#include <string>

#ifdef _WIN32
#   define WIDEN( x ) L##x
using ucs2_string = std::wstring;
#else
#   define WIDEN( x ) u##x
using ucs2_string = std::u16string;
#endif

bool get_encoder_clsid( const ucs2_string& format, CLSID* clsid );

int main()
{
    ULONG_PTR           token = 0;
    GdiplusStartupInput input = { 0 };
    input.GdiplusVersion      = 1;
    GdiplusStartup( &token, &input, nullptr );

    GpBitmap* bitmap = nullptr;
    GdipCreateBitmapFromScan0( 400, 400, 0, PixelFormat32bppARGB, nullptr, &bitmap );

    GpGraphics* graphics = nullptr;
    GdipGetImageGraphicsContext( bitmap, &graphics );

    GdipGraphicsClear( graphics, 0xFF808080 );

    GpPath* rectPath = nullptr;
    GdipCreatePath( FillModeAlternate, &rectPath );
    GdipAddPathRectangleI( rectPath, 50, 50, 300, 300 );

    GpPath* polyPath = nullptr;
    GdipCreatePath( FillModeAlternate, &polyPath );
    GpPoint polyPoints[] = { { 100, 100 },
                             { 200,  75 },
                             { 300, 100 },
                             { 325, 200 },
                             { 300, 300 },
                             { 200, 325 },
                             { 100, 300 },
                             {  75, 200 } };
    GdipAddPathPolygonI( polyPath, polyPoints, sizeof( polyPoints ) / sizeof( polyPoints[0] ) );

    GpRegion* region = nullptr;
    GdipCreateRegion( &region );
    GdipSetEmpty( region );
    GdipCombineRegionPath( region, rectPath, CombineModeUnion );
    GdipCombineRegionPath( region, polyPath, CombineModeExclude );

    GpSolidFill* brush = nullptr;
    GdipCreateSolidFill( 0xFF00FF00, &brush );

    GdipFillRegion( graphics, brush, region );

    GdipDeleteGraphics( graphics );
    GdipDeletePath( rectPath );
    GdipDeletePath( polyPath );
    GdipDeleteRegion( region );
    GdipDeleteBrush( brush );

    CLSID clsid = { 0 };
    get_encoder_clsid( WIDEN( "image/png" ), &clsid );

    GdipSaveImageToFile( bitmap, (const WCHAR*)WIDEN( "test-image.png" ), &clsid, nullptr );

    GdipDisposeImage( bitmap );

    return 0;
}

bool get_encoder_clsid( const ucs2_string& format, CLSID* clsid )
{
    UINT numEncoders = 0;
    UINT size = 0;
    GdipGetImageEncodersSize( &numEncoders, &size );

    ImageCodecInfo* encoders = (ImageCodecInfo*)malloc( size );
    GdipGetImageEncoders( numEncoders, size, encoders );

    for( UINT j = 0; j < numEncoders; ++j )
    {
        if( format == (const ucs2_string::value_type*)encoders[j].MimeType )
        {
            *clsid = encoders[j].Clsid;
            free( encoders );
            return true;
        }
    }

    free( encoders );
    return false;
}

Current Behavior

Fills the excluded area with some brighter color.
produced

Expected Behavior

Should not fill the excluded area at all.
expected

Version Used:

libgdiplus 5.6 (7e5300b)

Metafile play defaults are incorrect

  • Brush is set to -1 so is null, which causes playing to fail
  • Pen is set to -1, so is null, which causes playing to fail
  • The map mode is set to MM_TEXT but it should be like MM_TWIPS

GdipCreateFontFromLogfont does not set the font's family

In test_createFontFromLogFontA/test_createFontFromLogFontW in testfont.c:

	logfont.lfHeight = 10;
	logfont.lfWidth = 11;
	logfont.lfEscapement = 0;
	logfont.lfOrientation = 1;
	logfont.lfWeight = 700;
	logfont.lfItalic = TRUE;
	logfont.lfUnderline = TRUE;
	logfont.lfStrikeOut = TRUE;
	logfont.lfCharSet = 0;
	logfont.lfOutPrecision = 1;
	logfont.lfClipPrecision = 2;
	logfont.lfQuality = 4;
	logfont.lfPitchAndFamily = 0x50;
	strcpy (logfont.lfFaceName, "Times New Roman");

	status = GdipCreateFontFromLogfontA (hdc, &logfont, &font);
	assertEqualInt (status, Ok);

	GdipGetFontStyle (font, &style);
	assertEqualInt (style, 15);

	GdipGetFontUnit(font, &unit);
	assertEqualInt (unit, UnitWorld);

	status = GdipGetFamily (font, &family);
	// FIXME: this fails with libgdiplus.
#if defined(USE_WINDOWS_LIBGDIPLUS)
	assertEqualInt (status, Ok);
	assert (family);
#endif

Need to investigate what happens if the font family doesn't exist

gdip_property_get_[short/long/srational/rational] functions ignore offset parameter

These functions are used with non-zero offset in the TIFF code, eg.

		gdip_property_get_short(0, bitmap_data->property[index].value, &s);
		gdip_property_get_short(2, bitmap_data->property[index].value, &s2);

or

		float	chromacities[6];
		for (j = 0; j < 6; j++) {
			gdip_property_get_long(j * 8, bitmap_data->property[index].value, &i);
			chromacities[j] = (float)i / 1000000;
		}

Investigate BI_RLE4/BI_RLE8 behaviour for 16bpp images to match GDI+ behaviour

In test_validImage16bppBitmapInfoHeader in testbmpcodec.c

	// FIXME: this returns OutOfMemory libgdiplus.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (rle4Compression16bpp, PixelFormat32bppRGB);
	createFileSuccess (rle8Compression16bpp, PixelFormat32bppRGB);
#endif

Does GDI+ try to decompress these images, or does it just ignore the compression?

Implement GdipGetEncoderParameterListSize for tif/gif/png/jpeg

In test_getEncoderParameterListSize in testgpimage.c

	status = GdipGetEncoderParameterListSize (image, &tifEncoderClsid, &size);
	// FIXME: this returns NotImplemented with libgdiplus.
#if defined(USE_WINDOWS_GDIPLUS)
	assertEqualInt (status, Ok);
	assertEqualInt (size, is_32bit() ? 164 : 184);
#endif

	status = GdipGetEncoderParameterListSize (image, &gifEncoderClsid, &size);
	// FIXME: this returns FileNotFound with libgdiplus.
#if defined(USE_WINDOWS_GDIPLUS)
	assertEqualInt (status, Ok);
	assertEqualInt (size, is_32bit() ? 64 : 80);
#endif

	status = GdipGetEncoderParameterListSize (image, &pngEncoderClsid, &size);
	// FIXME: this returns FileNotFound with libgdiplus.
#if defined(USE_WINDOWS_GDIPLUS)
	assertEqualInt (status, Ok);
	assertEqualInt (size, is_32bit() ? 32 : 40);
#endif

	status = GdipGetEncoderParameterListSize (image, &jpegEncoderClsid, &size);
	assertEqualInt (status, Ok);
	// FIXME: this returns 44 with libgdiplus.
#if defined(USE_WINDOWS_GDIPLUS)
	assertEqualInt (size, is_32bit() ? 172 : 200);
#endif

Support WMF files where inches is zero

In test_valid in testwmfcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE zeroInches[] = {
		/* Placeable Header */ 0xD7, 0xCD, 0xC6, 0x9A, 0x00, 0x00, 0x58, 0xF0, 0xCE, 0xF2, 0xA8, 0x0F, 0x32, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x57,
		/* Metafile Header */  0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
		/* META_EOF */         0x03, 0x00, 0x00, 0x00, 0x00, 0x00
	};
#endif

	// FIXME: GDI+ uses a default inches value if it is zero.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (zeroInches, -4008, -3378, 8016, 6756, 14139.333008f, 11916.833008f);
#endif

GdipGetFontHeightGivenDPI/GdipGetFontHeight returns different values than GDI+

In test_getFontHeight in testfont.c

	status = GdipGetFontHeight (font, NULL, &height);
	assertEqualInt (status, Ok);
	// FIXME: this returns a different value with libgdiplus.
#if defined(USE_WINDOWS_LIBGDIPLUS)
	assertEqualFloat (height, 11.3183594f);
#endif

Maybe this is due to GdipGetGenericFontFamilySansSerif returning different fonts in Linux/Windows - so this could be a test bug

Validate `emSize` in `GdipCreateFont`

In test_createFont in testfont.c

	// FIXME: there are several places that indirectly call GdipCreateFont in
	// libgdiplus but allow negative em sizes on GDI+ because the GDI+
	// implementation does not call GdipCreateFont. An example is GdipAddPathString.
	// A fix for this will need to carefully account for places where this
	// API is called.
#if defined(USE_WINDOWS_LIBGDIPLUS)
	status = GdipCreateFont (family, -1, 10, UnitPixel, &font);
	assertEqualInt (status, InvalidParameter);

	status = GdipCreateFont (family, 0, 10, UnitPixel, &font);
	assertEqualInt (status, InvalidParameter);
#endif

Investigate how GDI+ validates WMF record when decoding

In test_invaldImageData in testwmfcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE noSuchFunction[] = {
		/* Placeable Header */ 0xD7, 0xCD, 0xC6, 0x9A, 0x00, 0x00, 0x58, 0xF0, 0xCE, 0xF2, 0xA8, 0x0F, 0x32, 0x0d, 0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF5, 0x54,
		/* Metafile Header */  0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
		/* Random */           0x03, 0x00, 0x00, 0x00, 0x00, 0x01
	};
#endif

	// FIXME: seems like GDI+ validates records more than libgdiplus.
#if defined(USE_WINDOWS_GDIPLUS)
	createFile (noSuchFunction, OutOfMemory);
#endif

Investigate how GDI+ parses short emf header extensions

In test_valid in testemfcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE shortMillimetres[] = {
		/* EMR_HEADER */    0x01, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
		/* Bounds */        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */         0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */     0x20, 0x45, 0x4D, 0x46,
		/* Version */       0x00, 0x00, 0x01, 0x00,
		/* Bytes */         0x6C, 0x00, 0x00, 0x00,
		/* Records */       0x02, 0x00, 0x00, 0x00,
		/* Handles */       0x01, 0x00,
		/* Reserved */      0x00, 0x00,
		/* nDescription */  0x00, 0x00, 0x00, 0x00,
		/* offDescription*/ 0x00, 0x00, 0x00, 0x00,
		/* palEntries */    0x00, 0x00, 0x00, 0x00,
		/* Device */        0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */   0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00
	};
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	BYTE noRecords[] = {
		/* EMR_HEADER */    0x01, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
		/* Bounds */        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */         0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */     0x20, 0x45, 0x4D, 0x46,
		/* Version */       0x00, 0x00, 0x01, 0x00,
		/* Bytes */         0x70, 0x00, 0x00, 0x00,
		/* Records */       0x02, 0x00, 0x00, 0x00,
		/* Handles */       0x01, 0x00,
		/* Reserved */      0x00, 0x00,
		/* nDescription */  0x00, 0x00, 0x00, 0x00,
		/* offDescription*/ 0x00, 0x00, 0x00, 0x00,
		/* palEntries */    0x00, 0x00, 0x00, 0x00,
		/* Device */        0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */   0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00
	};
	BYTE noCbPixelFormat[] = {
		/* EMR_HEADER */     0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
		/* Bounds */         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */          0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */      0x20, 0x45, 0x4D, 0x46,
		/* Version */        0x00, 0x00, 0x01, 0x00,
		/* Bytes */          0x78, 0x00, 0x00, 0x00,
		/* Records */        0x02, 0x00, 0x00, 0x00,
		/* Handles */        0x01, 0x00,
		/* Reserved */       0x00, 0x00,
		/* nDescription */   0x00, 0x00, 0x00, 0x00,
		/* offDescription*/  0x00, 0x00, 0x00, 0x00,
		/* palEntries */     0x00, 0x00, 0x00, 0x00,
		/* Device */         0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */    0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00
	};
	BYTE shortCbPixelFormat[] = {
		/* EMR_HEADER */     0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
		/* Bounds */         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */          0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */      0x20, 0x45, 0x4D, 0x46,
		/* Version */        0x00, 0x00, 0x01, 0x00,
		/* Bytes */          0x78, 0x00, 0x00, 0x00,
		/* Records */        0x02, 0x00, 0x00, 0x00,
		/* Handles */        0x01, 0x00,
		/* Reserved */       0x00, 0x00,
		/* nDescription */   0x00, 0x00, 0x00, 0x00,
		/* offDescription*/  0x00, 0x00, 0x00, 0x00,
		/* palEntries */     0x00, 0x00, 0x00, 0x00,
		/* Device */         0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */    0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
		/* cbPixelFormat */  0x00, 0x00, 0x00
	};
	BYTE noOffPixelFormat[] = {
		/* EMR_HEADER */     0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
		/* Bounds */         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */          0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */      0x20, 0x45, 0x4D, 0x46,
		/* Version */        0x00, 0x00, 0x01, 0x00,
		/* Bytes */          0x78, 0x00, 0x00, 0x00,
		/* Records */        0x02, 0x00, 0x00, 0x00,
		/* Handles */        0x01, 0x00,
		/* Reserved */       0x00, 0x00,
		/* nDescription */   0x00, 0x00, 0x00, 0x00,
		/* offDescription*/  0x00, 0x00, 0x00, 0x00,
		/* palEntries */     0x00, 0x00, 0x00, 0x00,
		/* Device */         0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */    0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
		/* cbPixelFormat */  0x00, 0x00, 0x00, 0x00,
	};
	BYTE shortOffPixelFormat[] = {
		/* EMR_HEADER */     0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
		/* Bounds */         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */          0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */      0x20, 0x45, 0x4D, 0x46,
		/* Version */        0x00, 0x00, 0x01, 0x00,
		/* Bytes */          0x78, 0x00, 0x00, 0x00,
		/* Records */        0x02, 0x00, 0x00, 0x00,
		/* Handles */        0x01, 0x00,
		/* Reserved */       0x00, 0x00,
		/* nDescription */   0x00, 0x00, 0x00, 0x00,
		/* offDescription*/  0x00, 0x00, 0x00, 0x00,
		/* palEntries */     0x00, 0x00, 0x00, 0x00,
		/* Device */         0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */    0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
		/* cbPixelFormat */  0x00, 0x00, 0x00, 0x00,
		/* offPixelFormat */ 0x00, 0x00, 0x00
	};
	BYTE noOpenGL[] = {
		/* EMR_HEADER */     0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
		/* Bounds */         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */          0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */      0x20, 0x45, 0x4D, 0x46,
		/* Version */        0x00, 0x00, 0x01, 0x00,
		/* Bytes */          0x78, 0x00, 0x00, 0x00,
		/* Records */        0x02, 0x00, 0x00, 0x00,
		/* Handles */        0x01, 0x00,
		/* Reserved */       0x00, 0x00,
		/* nDescription */   0x00, 0x00, 0x00, 0x00,
		/* offDescription*/  0x00, 0x00, 0x00, 0x00,
		/* palEntries */     0x00, 0x00, 0x00, 0x00,
		/* Device */         0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */    0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
		/* cbPixelFormat */  0x00, 0x00, 0x00, 0x00,
		/* offPixelFormat */ 0x00, 0x00, 0x00, 0x00,
	};
	BYTE shortOpenGL[] = {
		/* EMR_HEADER */     0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
		/* Bounds */         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */          0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */      0x20, 0x45, 0x4D, 0x46,
		/* Version */        0x00, 0x00, 0x01, 0x00,
		/* Bytes */          0x78, 0x00, 0x00, 0x00,
		/* Records */        0x02, 0x00, 0x00, 0x00,
		/* Handles */        0x01, 0x00,
		/* Reserved */       0x00, 0x00,
		/* nDescription */   0x00, 0x00, 0x00, 0x00,
		/* offDescription*/  0x00, 0x00, 0x00, 0x00,
		/* palEntries */     0x00, 0x00, 0x00, 0x00,
		/* Device */         0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */    0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
		/* cbPixelFormat */  0x00, 0x00, 0x00, 0x00,
		/* offPixelFormat */ 0x00, 0x00, 0x00, 0x00,
		/* bOpenGL */        0x00, 0x00, 0x00
	};
	BYTE noMicrometers[] = {
		/* EMR_HEADER */     0x01, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
		/* Bounds */         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */          0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */      0x20, 0x45, 0x4D, 0x46,
		/* Version */        0x00, 0x00, 0x01, 0x00,
		/* Bytes */          0x80, 0x00, 0x00, 0x00,
		/* Records */        0x02, 0x00, 0x00, 0x00,
		/* Handles */        0x01, 0x00,
		/* Reserved */       0x00, 0x00,
		/* nDescription */   0x00, 0x00, 0x00, 0x00,
		/* offDescription*/  0x00, 0x00, 0x00, 0x00,
		/* palEntries */     0x00, 0x00, 0x00, 0x00,
		/* Device */         0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */    0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
		/* cbPixelFormat */  0x00, 0x00, 0x00, 0x00,
		/* offPixelFormat */ 0x00, 0x00, 0x00, 0x00,
		/* bOpenGL */        0x00, 0x00, 0x00, 0x00
	};
	BYTE shortMicrometers[] = {
		/* EMR_HEADER */     0x01, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
		/* Bounds */         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
		/* Frame */          0xD2, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xB1, 0x03, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
		/* Signature */      0x20, 0x45, 0x4D, 0x46,
		/* Version */        0x00, 0x00, 0x01, 0x00,
		/* Bytes */          0x80, 0x00, 0x00, 0x00,
		/* Records */        0x02, 0x00, 0x00, 0x00,
		/* Handles */        0x01, 0x00,
		/* Reserved */       0x00, 0x00,
		/* nDescription */   0x00, 0x00, 0x00, 0x00,
		/* offDescription*/  0x00, 0x00, 0x00, 0x00,
		/* palEntries */     0x00, 0x00, 0x00, 0x00,
		/* Device */         0xA0, 0x05, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00,
		/* Millimetres */    0xD8, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
		/* cbPixelFormat */  0x00, 0x00, 0x00, 0x00,
		/* offPixelFormat */ 0x00, 0x00, 0x00, 0x00,
		/* bOpenGL */        0x00, 0x00, 0x00, 0x00,
		/* Micrometers */    0xC0, 0x4B, 0x03, 0x00, 0xD8, 0x41, 0x04
	};
#endif

	// FIXME: GDI+ allows 4 bytes off the size of Millimetres.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (shortMillimetres, 14, 20, 50, 18, 750, 216);
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (noRecords, 14, 20, 50, 18, 750, 216);
	createFileSuccess (noCbPixelFormat, 14, 20, 50, 18, 750, 216);
	createFileSuccess (shortCbPixelFormat, 14, 20, 50, 18, 750, 216);
	createFileSuccess (noOffPixelFormat, 14, 20, 50, 18, 750, 216);
	createFileSuccess (shortOffPixelFormat, 14, 20, 50, 18, 750, 216);
	createFileSuccess (noOpenGL, 14, 20, 50, 18, 750, 216);
	createFileSuccess (shortOpenGL, 14, 20, 50, 18, 750, 216);
	createFileSuccess (noMicrometers, 14, 20, 50, 18, 750, 216);
	createFileSuccess (shortMicrometers, 14, 20, 50, 18, 750, 216);
#endif

Undefined behaviour in GdipGetMetafileHeaderFromWmf causes fluctuating values of DpiX etc.

In test_getMetafileHeaderFromWmf in testmetafile.c

    // Get from WMF file.
    status = GdipGetMetafileHeaderFromWmf (wmfMetafile, &wmfPlaceableFileHeader, &header);
    assertEqualInt (status, Ok);
    assertEqualInt (header.Type , 2);
    assertEqualInt (header.Size, 68142);
    assertEqualInt (header.Version, 768);
    assertEqualInt (header.EmfPlusFlags, 0);

    // FIXME: these values constantly fluctuate with libgdiplus - potential UB?
#if defined(USE_WINDOWS_GDIPLUS)
    assertEqualInt (header.DpiX, 0);
    assertEqualInt (header.DpiY, 0);
    assertEqualInt (header.X, 0);
    assertEqualInt (header.Y, 0);
    assertEqualInt (header.Width, 0);
    assertEqualInt (header.Height, 0);
#endif

    // Get from EMF file.
    status = GdipGetMetafileHeaderFromWmf (emfMetafile, &wmfPlaceableFileHeader, &header);
    assertEqualInt (status, Ok);
    assertEqualInt (header.Type , 2);
    assertEqualInt (header.Size, 0);
    assertEqualInt (header.Version,  108);
    assertEqualInt (header.EmfPlusFlags, 0);
    // FIXME: these values constantly fluctuate with libgdiplus - potential UB?
#if defined(USE_WINDOWS_GDIPLUS)
    assert (header.DpiX > 0);
    assert (header.DpiY > 0);
    assertEqualInt (header.X, 0);
    assertEqualInt (header.Y, 0);
    assertEqualInt (header.Width, 0);
    assertEqualInt (header.Height, 0);
#endif

Update libgdiplus in Mono repositories, Linux Distributions & Homebrew

With all the work @hughbe is doing it sure looks like libgdiplus is living its renaissance 😄 .

We rely on libgdiplus in our product and we try to make sure our users can install libgdiplus using the normal package management solutions on their platform (Linux distros + Homebrew).

Right now we build the packages ourselves but it would make sense to update the 'official' repositories.

Some thoughts:

  • Mono already has Ubuntu, Debian and CentOS repositories which contain libgdiplus. The latest version of libgdiplus in there is 4.2 which dates from Dec. 2015. Is there an easy way to get more recent version of libgdiplus packaged in the Mono repositories?
  • Is there any set process to get the Linux distro's to update libgdiplus? I think @akoeplinger mentioned most maintainers are members of the Xamarin team so perhaps this is an easy step 😄 .
  • Homebrew seems to track the tags in the git repo, so that seems to be under control.

Convert 4bpp grayscale with alpha PNGs to 32bppARGB to match GDI+

In test_valid4bpp in testpngcodec.c

	BYTE grayscaleWithAlpha1x1Interlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x01, 0x88, 0x89, 0x46, 0xC2,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x11, 0x75, 0x9A, 0x0F, 0xE8,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE grayscaleWithAlpha6x4NotInterlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x9F, 0xFC, 0x9E,
		/* IDAT */      0x00, 0x00, 0x00, 0x18, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x10, 0x14, 0x14, 0x64, 0x10, 0x71, 0x71, 0x64, 0x90, 0x2F, 0xFF, 0xC8, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x10, 0xFF, 0x02, 0x54, 0xB8, 0xE8, 0x8D, 0xCD,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE grayscaleWithAlpha1x1WithPalette[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x01, 0x88, 0x89, 0x46, 0xC2,
		/* PLTE */      0x00, 0x00, 0x00, 0x18, 'P', 'L', 'T', 'E', 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x80, 0x00, 0xE6, 0xE6, 0xFA, 0xF5, 0xF5, 0xDC, 0xFF, 0xFF, 0xFF, 0x9A, 0xCD, 0x32, 0xEE, 0x82, 0xEE, 0xBD, 0xEB, 0xF4, 0x2B,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x11, 0x75, 0x9A, 0x0F, 0xE8,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};

	// FIXME: GDI+ converts grayscale alpha 4bpp images to 32bpp.
#if defined(USE_WINDOWS_GDIPLUS)
	PixelFormat expectedGrayscalePixelFormat = PixelFormat32bppARGB;
#else
	PixelFormat expectedGrayscalePixelFormat = PixelFormat4bppIndexed;
#endif
	createFileSuccess (grayscaleWithAlpha1x1Interlaced, expectedGrayscalePixelFormat, 1, 1, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (grayscaleWithAlpha6x4NotInterlaced, expectedGrayscalePixelFormat, 6, 4, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (grayscaleWithAlpha1x1WithPalette, expectedGrayscalePixelFormat, 1, 1, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (indexed4bpp1x1PaletteFirst, PixelFormat4bppIndexed, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);

Validate icons better to match GDI+ behaviour

In test_invalidImage in testicocodec.c

#if defined(USE_WINDOWS_GDIPLUS)
  BYTE invalidData[]    = {0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 22, 0, 0, 0, 40, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 128, 0, 0, 0, 0, 0, 0, 0};
#endif

  // FIXME: this returns Ok with libgdiplus.
#if defined(USE_WINDOWS_GDIPLUS)
  createFile (invalidData, OutOfMemory);
#endif

x64 Windows: access violation in FcInit()

Exception thrown at 0x00007FF8AF470B1D (ntdll.dll) in libgdiplus.tests.exe: 0xC0000005: Access violation writing location 0xFFFFFFFFF6D1C290.

fontconfig.dll!readdir(DIR * dir) Line 100 C Symbols loaded.
fontconfig.dll!FcDirScanConfig(_FcFontSet * set, _FcStrSet * dirs, _FcBlanks * blanks, const unsigned char * dir, int force, _FcConfig * config) Line 186 C Symbols loaded.
fontconfig.dll!FcDirCacheScan(const unsigned char * dir, _FcConfig * config) Line 270 C Symbols loaded.
fontconfig.dll!FcDirCacheRead(const unsigned char * dir, int force, _FcConfig * config) Line 319 C Symbols loaded.
fontconfig.dll!FcConfigAddDirList(_FcConfig * config, _FcSetName set, _FcStrSet * dirSet) Line 355 C Symbols loaded.
fontconfig.dll!FcConfigBuildFonts(_FcConfig * config) Line 388 C Symbols loaded.
[Inline Frame] fontconfig.dll!FcInitLoadConfigAndFonts() Line 106 C Symbols loaded.
fontconfig.dll!FcInit() Line 124 C Symbols loaded.
libgdiplus.dll!GdiplusStartup(unsigned __int64 * token, const GdiplusStartupInput * input, GdiplusStartupOutput * output) Line 51 C Symbols loaded.

Problematic code:

        if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
        {
            result         = &dir->result;
            result->d_name = dir->info.name;
        }

Allow gifs with missing graphics control block extension terminator to match GDI+ behaviour

In test_validData in testgifcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
  BYTE graphicsControlBlockMissingTerminator[] = {'G', 'I', 'F', '8', '9', 'a', 3, 0, 5, 0, 0, 0, 0, '!', 0xF9, 0x04, 0, 0, 0, 0, ',', 0, 0, 0, 0, 3, 0, 5, 0, B8(10000000), 0, 0, 0, 255, 255, 255, 0x02, 0x06, 0x84, 0x03, 0x81, 0x9a, 0x06, 0x05, 0x00, ';'};
#endif

  // FIXME: it appears that GDI+ allows a graphics control extension block without a
  // terminating 0 byte.
#if defined(USE_WINDOWS_GDIPLUS)
  createFileSuccess (graphicsControlBlockMissingTerminator, 3, 5);
#endif

This may not be possible to fix, as we rely on giflib

Investigate matching GDI+ behaviour where WMF file size is too large

In test_invalidFileSize in testwmfcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE tooLargeFileSize[] = {
		/* Placeable Header */ 0xD7, 0xCD, 0xC6, 0x9A, 0x00, 0x00, 0x58, 0xF0, 0xCE, 0xF2, 0xA8, 0x0F, 0x32, 0x0d, 0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF5, 0x54,
		/* Metafile Header */  0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
		/* META_EOF */         0x03, 0x00, 0x00, 0x00, 0x00, 0x00
	};
#endif

	// FIXME: GDI+ seems to allow overly large file sizes.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (tooLargeFileSize, -4008, -3378, 8016, 6756, 20360.638672f, 17160.2383f);
#endif

Convert 16bpp grayscale/truecolor PNG images to 32bppARGB

In test_valid16bpp in testpngcodec.c

	BYTE grayscale1x1Interlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x01, 0x1D, 0xE9, 0x77, 0x80,
		/* IDAT */      0x00, 0x00, 0x00, 0x0B, 'I', 'D', 'A', 'T', 0x18, 0x57, 0x63, 0xA8, 0xFF, 0x0F, 0x00, 0x02, 0x00, 0x01, 0x7F, 0x2F, 0x5A, 0xAA, 0x27,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE grayscale6x4NotInterlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xFF, 0xCD, 0xDC,
		/* IDAT */      0x00, 0x00, 0x00, 0x21, 'I', 'D', 'A', 'T', 0x18, 0x57, 0x63, 0xFC, 0xFF, 0x9F, 0x01, 0x0E, 0x80, 0x9C, 0x06, 0x28, 0xB3, 0x81, 0x81, 0x85, 0x81, 0xE1, 0x5A, 0x18, 0x94, 0xB7, 0x0A, 0x4A, 0x83, 0x00, 0x03, 0x03, 0x00, 0xF4, 0x31, 0x06, 0xD9, 0x82, 0x8E, 0x6A, 0xA9,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE grayscale1x1WithPalette[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x01, 0x1D, 0xE9, 0x77, 0x80,
		/* PLTE */      0x00, 0x00, 0x00, 0x18, 'P', 'L', 'T', 'E', 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x80, 0x00, 0xE6, 0xE6, 0xFA, 0xF5, 0xF5, 0xDC, 0xFF, 0xFF, 0xFF, 0x9A, 0xCD, 0x32, 0xEE, 0x82, 0xEE, 0xBD, 0xEB, 0xF4, 0x2B,
		/* IDAT */      0x00, 0x00, 0x00, 0x0B, 'I', 'D', 'A', 'T', 0x18, 0x57, 0x63, 0xA8, 0xFF, 0x0F, 0x00, 0x02, 0x00, 0x01, 0x7F, 0x2F, 0x5A, 0xAA, 0x27,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE trueColor1x1Interlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x02, 0x00, 0x00, 0x01, 0xB7, 0xE0, 0xBF, 0x0B,
		/* IDAT */      0x00, 0x00, 0x00, 0x0F, 'I', 'D', 'A', 'T', 0x18, 0x57, 0x63, 0xF8, 0xFF, 0x9F, 0x81, 0x81, 0x81, 0x01, 0x00, 0x0A, 0xFC, 0x01, 0xFF, 0xC0, 0x18, 0x7E, 0x3E,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE trueColor6x4NotInterlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x10, 0x02, 0x00, 0x00, 0x00, 0x72, 0xF6, 0x05, 0x57,
		/* IDAT */      0x00, 0x00, 0x00, 0x36, 'I', 'D', 'A', 'T', 0x18, 0x57, 0x63, 0xFC, 0xFF, 0x9F, 0x81, 0x20, 0x80, 0x2A, 0x7A, 0xFE, 0xFC, 0xD9, 0xB3, 0x5F, 0xBF, 0xC0, 0x22, 0x28, 0x40, 0x52, 0x52, 0x4A, 0x8A, 0x8D, 0x8D, 0x01, 0xD9, 0xA4, 0x77, 0xEF, 0x9A, 0x9A, 0xDE, 0x01, 0x01, 0x88, 0x84, 0x88, 0x40, 0x64, 0x19, 0x11, 0x4C, 0x5C, 0x80, 0x81, 0x01, 0x00, 0x87, 0xD4, 0x19, 0x72, 0xFD, 0x0D, 0x0C, 0x10,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE trueColor1x1WithPalette[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x02, 0x00, 0x00, 0x01, 0xB7, 0xE0, 0xBF, 0x0B,
		/* PLTE */      0x00, 0x00, 0x00, 0x18, 'P', 'L', 'T', 'E', 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x80, 0x00, 0xE6, 0xE6, 0xFA, 0xF5, 0xF5, 0xDC, 0xFF, 0xFF, 0xFF, 0x9A, 0xCD, 0x32, 0xEE, 0x82, 0xEE, 0xBD, 0xEB, 0xF4, 0x2B,
		/* IDAT */      0x00, 0x00, 0x00, 0x0F, 'I', 'D', 'A', 'T', 0x18, 0x57, 0x63, 0xF8, 0xFF, 0x9F, 0x81, 0x81, 0x81, 0x01, 0x00, 0x0A, 0xFC, 0x01, 0xFF, 0xC0, 0x18, 0x7E, 0x3E,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};

	// FIXME: GDI+ converts grayscale 16bpp images to 32bpp.
#if defined(USE_WINDOWS_GDIPLUS)
	PixelFormat expectedGrayscalePixelFormat = PixelFormat32bppARGB;
	PixelFormat expectedTrueColorPixelFormat = PixelFormat32bppARGB;
#else
	PixelFormat expectedGrayscalePixelFormat = PixelFormat8bppIndexed ;
	PixelFormat expectedTrueColorPixelFormat = PixelFormat24bppRGB ;
#endif

	createFileSuccess (grayscale1x1Interlaced, expectedGrayscalePixelFormat, 1, 1, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (grayscale6x4NotInterlaced, expectedGrayscalePixelFormat, 6, 4, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (grayscale1x1WithPalette, expectedGrayscalePixelFormat, 1, 1, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (trueColor1x1Interlaced, expectedTrueColorPixelFormat, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (trueColor6x4NotInterlaced, expectedTrueColorPixelFormat, 6, 4, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (trueColor1x1WithPalette, expectedTrueColorPixelFormat, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);

Grayscale PNG image flags are incorrect

In testpngcodec.c

#define createFileSuccess(buffer, format, width, height, flags, propertyCount) \
{ \
	createFile (buffer, Ok); \
	/* FIXME: grayscale image flags are incorrect. */ \
	verifyImage (image, ImageTypeBitmap, pngRawFormat, format, 0, 0, width, height, (REAL) width, (REAL) height, flags, propertyCount, FALSE); \
	GdipDisposeImage (image); \
}

Convert 8bpp grayscale PNG images to 32bppARGB

In test_valid8bpp in testpngcodec.c

	BYTE grayscale1x1Interlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x01, 0x4D, 0x79, 0xAB, 0xC3,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0x57, 0x63, 0x60, 0x04, 0x00, 0x00, 0x03, 0x00, 0x02, 0xA0, 0x80, 0x44, 0x0F,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
	BYTE grayscale6x4NotInterlaced[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R',0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x88, 0x6F, 0x11, 0x9F,
		/* IDAT */      0x00, 0x00, 0x00, 0x1D, 'I', 'D', 'A', 'T',0x18, 0x57, 0x63, 0x64, 0x64, 0x00, 0x01, 0x46, 0x46, 0x26, 0x20, 0xF9, 0x8F, 0x89, 0xE1, 0x0F, 0x0B, 0xCB, 0x1F, 0xB0, 0x08, 0x03, 0x03, 0x03, 0x00, 0x24, 0xA0, 0x03, 0x07, 0xA7, 0x85, 0xE0, 0xA2,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D',0xAE, 0x42, 0x60, 0x82
	};
	BYTE grayscale1x1WithPalette[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x01, 0x4D, 0x79, 0xAB, 0xC3,
		/* PLTE */      0x00, 0x00, 0x00, 0x18, 'P', 'L', 'T', 'E', 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x80, 0x00, 0xE6, 0xE6, 0xFA, 0xF5, 0xF5, 0xDC, 0xFF, 0xFF, 0xFF, 0x9A, 0xCD, 0x32, 0xEE, 0x82, 0xEE, 0xBD, 0xEB, 0xF4, 0x2B,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0x57, 0x63, 0x60, 0x04, 0x00, 0x00, 0x03, 0x00, 0x02, 0xA0, 0x80, 0x44, 0x0F,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};

	// FIXME: GDI+ converts grayscale 8bpp images to 32bpp.
#if defined(USE_WINDOWS_GDIPLUS)
	PixelFormat expectedGrayscalePixelFormat = PixelFormat32bppARGB;
#else
	PixelFormat expectedGrayscalePixelFormat = PixelFormat8bppIndexed;
#endif
	createFileSuccess (grayscale1x1Interlaced, expectedGrayscalePixelFormat, 1, 1, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (grayscale6x4NotInterlaced, expectedGrayscalePixelFormat, 6, 4, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
	createFileSuccess (grayscale1x1WithPalette, expectedGrayscalePixelFormat, 1, 1, ImageFlagsColorSpaceGRAY | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);

16bpp pixel values do not match GDI+

In test_validImage16bppBitmapV3Header in testbmpcodec.c

static void test_validImage16bppBitmapV3Header ()
{
	BYTE image1x1RGB565[] = {
		// -- BITMAPCOREHEADER -- //
		/* Signature */ 0x42, 0x4D,
		/* File Size */ 0x4A, 0x00, 0x00, 0x00,
		/* Reserved */  0x00, 0x00, 0x00, 0x00,
		/* Offset */    0x46, 0x00, 0x00, 0x00,
		// -- BITMAPINFOHEADER
		/* Header Size */      0x38, 0x00, 0x00, 0x00,
		/* Width */            0x01, 0x00, 0x00, 0x00,
		/* Height */           0x01, 0x00, 0x00, 0x00,
		/* Planes */           0x01, 0x00,
		/* Bit Count */        0x10, 0x00,
		/* Compression */      0x00, 0x00, 0x00, 0x00,
		/* Image Size */       0x00, 0x00, 0x00, 0x00,
		/* Horizontal */       0x00, 0x00, 0x00, 0x00,
		/* Vertical */         0x00, 0x00, 0x00, 0x00,
		/* Colors Used */      0x00, 0x00, 0x00, 0x00,
		/* Important Colors */ 0x00, 0x00, 0x00, 0x00,
		// -- BITMAPV3INFOHEADER -- //
		/* Red Mask */         0x00, 0xF8, 0x00, 0x00,
		/* Green Mask */       0xE0, 0x07, 0x00, 0x00,
		/* Blue Mask */        0x1F, 0x00, 0x00, 0x00,
		/* Alpha Mask */       0x00, 0x00, 0x00, 0x00,
		// -- Image Data --/
		0xD7, 0xFE, 0x00, 0x00
	};
	BYTE image1x1RGB555[] = {
		// -- BITMAPCOREHEADER -- //
		/* Signature */ 0x42, 0x4D,
		/* File Size */ 0x4A, 0x00, 0x00, 0x00,
		/* Reserved */  0x00, 0x00, 0x00, 0x00,
		/* Offset */    0x46, 0x00, 0x00, 0x00,
		// -- BITMAPINFOHEADER
		/* Header Size */      0x38, 0x00, 0x00, 0x00,
		/* Width */            0x01, 0x00, 0x00, 0x00,
		/* Height */           0x01, 0x00, 0x00, 0x00,
		/* Planes */           0x01, 0x00,
		/* Bit Count */        0x10, 0x00,
		/* Compression */      0x00, 0x00, 0x00, 0x00,
		/* Image Size */       0x00, 0x00, 0x00, 0x00,
		/* Horizontal */       0x00, 0x00, 0x00, 0x00,
		/* Vertical */         0x00, 0x00, 0x00, 0x00,
		/* Colors Used */      0x00, 0x00, 0x00, 0x00,
		/* Important Colors */ 0x00, 0x00, 0x00, 0x00,
		// -- BITMAPV3INFOHEADER -- //
		/* Red Mask */         0x7C, 0x00, 0x00, 0x00,
		/* Green Mask */       0xE0, 0x03, 0x00, 0x00,
		/* Blue Mask */        0x1F, 0x00, 0x00, 0x00,
		/* Alpha Mask */       0x00, 0x00, 0x00, 0x00,
		// -- Image Data --/
		0xD7, 0xFE, 0x00, 0x00
	};

	createFileSuccessDispose (image1x1RGB565, PixelFormat32bppRGB, 1, 1, bmpFlags, FALSE);
	// FIXME: match GDI+ behaviour.
#if defined(USE_WINDOWS_GDIPLUS)
	verifyPixels (image, {(ARGB) -19011});
#endif
	GdipDisposeImage (image);

	createFileSuccessDispose (image1x1RGB555, PixelFormat32bppRGB, 1, 1, bmpFlags, FALSE);
	// FIXME: match GDI+ behaviour.
#if defined(USE_WINDOWS_GDIPLUS)
	verifyPixels (image, {(ARGB) -19011});
#endif
	GdipDisposeImage (image);
}

Bitmap images are allowed to have a final line missing with libgdiplus, not with GDI+

In test_invalidImageData in testbmpcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE noImageData[]            = {'B', 'M', 62, 0, 0, 0, 0, 0, 0, 0, 0x3E, 0, 0, 0, 40, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0};
#endif
	// FIXME: this should fail with OutOfMemory in libgdiplus.
#if defined(USE_WINDOWS_GDIPLUS)
	createFile (noImageData, OutOfMemory);
#endif

The fault is in

			if (size_read < size) {
				/* special case for missing data in the last line */
				if (line == result->active_bitmap->height - 1) {
					/* MS produce such files for storing ImageList bitmaps, see bug #80797 */
					int missing_size = size - size_read;
					memset (data_read + size_read, 0, missing_size);
				} else {
					status = OutOfMemory;
					goto error;
				}
			}

@akoeplinger is there a compat concern? This would disallow images that we previously allowed, but also increase compatability with GDI+...

It seems that the bug https://bugzilla.xamarin.com/show_bug.cgi?id=80797 doesn't exist as well? Maybe this is an old relic

Indexed PNG images with palettes last cannot be decoded

In test_valid1bpp in testpngcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE indexed1bpp1x1PaletteLast[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, 0xDB, 0x56, 0xCA,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x68, 0x00, 0x00, 0x00, 0x82, 0x00, 0x81, 0xA7, 0x01, 0xBA, 0x10,
		/* PLTE */		0x00, 0x00, 0x00, 0x06, 'P', 'L', 'T', 'E', 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xA5, 0xD9, 0x9F, 0xDD,
		/* IEND*/       0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
#endif

	// FIXME: GDI+ allows indexed images with palettes last.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (indexed1bpp1x1PaletteLast, PixelFormat1bppIndexed, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif

In test_valid2bpp in testpngcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE indexed2bpp6x4PaletteLast[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x02, 0x03, 0x00, 0x00, 0x01, 0xA7, 0x6D, 0x96, 0x46,
		/* IDAT */      0x00, 0x00, 0x00, 0x16, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x70, 0x00, 0x43, 0x1F, 0x86, 0x10, 0x86, 0x23, 0x0C, 0x59, 0x13, 0x18, 0x18, 0x18, 0x00, 0x1B, 0x38, 0x03, 0x1F, 0xE2, 0xF7, 0x9D, 0x3B,
		/* PLTE */      0x00, 0x00, 0x00, 0x09, 'P', 'L', 'T', 'E', 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xE6, 0xE6, 0xFA, 0x0D, 0xB2, 0xEB, 0x46,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
#endif

	// FIXME: GDI+ allows indexed images with palettes last.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (indexed2bpp6x4PaletteLast, PixelFormat32bppARGB, 6, 4, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsHasAlpha | ImageFlagsReadOnly, 3);
#endif

In testvalid4bpp in testpngcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE indexed4bpp6x4PaletteLast[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x04, 0x03, 0x00, 0x00, 0x01, 0x28, 0x2D, 0x63, 0xE6,
		/* IDAT */      0x00, 0x00, 0x00, 0x1B, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x10, 0x00, 0x43, 0xF1, 0x0F, 0x0C, 0x82, 0x02, 0x0C, 0xDF, 0x05, 0x18, 0x44, 0x5C, 0x1C, 0x19, 0x18, 0x18, 0x18, 0x00, 0x22, 0x93, 0x02, 0xF9, 0x27, 0x14, 0x52, 0x58,
		/* PLTE */      0x00, 0x00, 0x00, 0x18, 'P', 'L', 'T', 'E', 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x80, 0x00, 0xE6, 0xE6, 0xFA, 0xF5, 0xF5, 0xDC, 0xFF, 0xFF, 0xFF, 0x9A, 0xCD, 0x32, 0xEE, 0x82, 0xEE, 0xBD, 0xEB, 0xF4, 0x2B,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
#endif

	// FIXME: GDI+ allows indexed images with palettes last.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (indexed4bpp6x4PaletteLast, PixelFormat4bppIndexed, 6, 4, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif

In test_valid8bpp in testpngcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE indexed6x4PaletteLast[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x08, 0x03, 0x00, 0x00, 0x01, 0xED, 0xDD, 0x8E, 0xE7,
		/* IDAT */      0x00, 0x00, 0x00, 0x1E, 'I', 'D', 'A', 'T', 0x18, 0x57, 0x63, 0x60, 0x84, 0x40, 0xF6, 0xFF, 0x0C, 0x8C, 0x8C, 0x8C, 0x0C, 0xFF, 0xD9, 0x81, 0x6C, 0x66, 0x20, 0x60, 0x64, 0x00, 0x01, 0x06, 0x06, 0x00, 0x2A, 0x5E, 0x02, 0x23, 0xF7, 0x26, 0x7C, 0xAC,
		/* PLTE */      0x00, 0x00, 0x00, 0x18, 'P', 'L', 'T', 'E', 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x80, 0x00, 0xE6, 0xE6, 0xFA, 0xF5, 0xF5, 0xDC, 0xFF, 0xFF, 0xFF, 0x9A, 0xCD, 0x32, 0xEE, 0x82, 0xEE, 0xBD, 0xEB, 0xF4, 0x2B,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
#endif

	// FIXME: GDI+ allows indexed images with palettes last.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (indexed6x4PaletteLast, PixelFormat8bppIndexed, 6, 4, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif

Might not be possible, as we rely on libpng

Support GdipGetImageGraphicsContext for 8bpp indexed bitmaps

In test_getImageGraphicsContext in testbmpcodec.

	// FIXME: libgdiplus doesn't support PixelFormat8bppIndexed.
#if defined(USE_WINDOWS_GDIPLUS)
	// ImageTypeBitmap - PixelFormat8bppIndexed.
	status = GdipGetImageGraphicsContext (gifImage, &graphics);
	assertEqualInt (status, Ok);
	assert (graphics);
	GdipDeleteGraphics (graphics);
#endif

Support 16bpp BITMAPV4HEADER bitmaps

In test_validImage16bppBitmapV4Header in testbmpcodec.c

// FIXME: this causes a stack buffer overflow (crash) in libgdiplus.

16bpp BITMAPV4HEADER cause libgdiplus to crash

Support BITMAPV3/V4/V5HEADER for bitmaps

We currently just assume that if the header is not OS/2, then it must be BITMAPINFOHEADER which is wrong. Need to add tests for these headers also verifying the pixel values of the image, as previously we'd read data from the palette or header as image data

Jpg image flags are wrong

In test_loadImageFromFileJpg in testgpimage.c

static void test_loadImageFromFileJpg ()
{
	GpImage *image = getImage ("test.jpg");

	// FIXME: jpg image flags are wrong in libgdiplus.
	verifyBitmap (image, jpegRawFormat, PixelFormat24bppRGB, 100, 68, 73744, 2, FALSE);

	GdipDisposeImage (image);
}

In test_cloneImage in testgpimage.c

	// ImageTypeBitmap - jpg.
	status = GdipCloneImage (jpgImage, &clonedImage);
	assertEqualInt (status, Ok);
	assert (clonedImage && clonedImage != jpgImage);
	// FIXME: jpg image flags are wrong in libgdiplus.
	verifyBitmap (clonedImage, jpegRawFormat, PixelFormat24bppRGB, 100, 68, 73744, 2, FALSE);
	GdipDisposeImage (clonedImage);

Don't validate as much PNG information

In test_valid in testpngcodec.c

#if defined(USE_WINDOWS_GDIPLUS)
	BYTE longIhdrLength[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0E, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0x00, 0x00, 0x00, 0x00
	};
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	BYTE multipleIhdrs[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x04, 0x03, 0x00, 0x00, 0x01, 0x28, 0x2D, 0x63, 0xE6,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x40, 0x69, 0xC9, 0xB2,
		/* PLTE */      0x00, 0x00, 0x00, 0x0C, 'P', 'L', 'T', 'E', 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0xFF, 0xC1, 0x7F, 0x62, 0xD1,
		/* IDAT */      0x00, 0x00, 0x00, 0x0C, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0xC0, 0x06, 0x18, 0x18, 0x00, 0x00, 0x17, 0x00, 0x01, 0x47, 0xB7, 0x91, 0x37,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	BYTE multipleIdats[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x40, 0x69, 0xC9, 0xB2,
		/* PLTE */      0x00, 0x00, 0x00, 0x0C, 'P', 'L', 'T', 'E', 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0xFF, 0xC1, 0x7F, 0x62, 0xD1,
		/* IDAT */      0x00, 0x00, 0x00, 0x0C, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0xC0, 0x06, 0x18, 0x18, 0x00, 0x00, 0x17, 0x00, 0x01, 0x47, 0xB7, 0x91, 0x37,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x98, 0x63, 0x6C, 0xD7,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	BYTE oneEmptyIdat[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
		/* IDAT */      0x00, 0x00, 0x00, 0x00, 'I', 'D', 'A', 'T', 0x00, 0x00, 0x00, 0x00,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0x00, 0x00, 0x00, 0x00
	};
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	BYTE multiplePalettes[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x04, 0x03, 0x00, 0x00, 0x01, 0x28, 0x2D, 0x63, 0xE6,
		/* PLTE */      0x00, 0x00, 0x00, 0x0C, 'P', 'L', 'T', 'E', 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0xFF, 0xC1, 0x7F, 0x62, 0xD1,
		/* PLTE */      0x00, 0x00, 0x00, 0x0C, 'P', 'L', 'T', 'E', 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0xFF, 0xC1, 0x7F, 0x62, 0xD1,
		/* IDAT */      0x00, 0x00, 0x00, 0x0C, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0xC0, 0x06, 0x18, 0x18, 0x00, 0x00, 0x17, 0x00, 0x01, 0x47, 0xB7, 0x91, 0x37,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	BYTE invalidCrc[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0x00, 0x00, 0x00, 0x00
	};
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	BYTE invalidCompression[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0x00, 0x00, 0x00, 0x00
	};
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	BYTE unknownChunk[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x04, 0x03, 0x00, 0x00, 0x01, 0x28, 0x2D, 0x63, 0xE6,
		/* UNKNOWN */   0x00, 0x00, 0x00, 0x02, 'U', 'N', 'K', 'N', 0xFE, 0xEF, 0xAE, 0xCE, 0x1C, 0xE9,
		/* PLTE */      0x00, 0x00, 0x00, 0x0C, 'P', 'L', 'T', 'E', 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0xFF, 0xC1, 0x7F, 0x62, 0xD1,
		/* IDAT */      0x00, 0x00, 0x00, 0x0C, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0xC0, 0x06, 0x18, 0x18, 0x00, 0x00, 0x17, 0x00, 0x01, 0x47, 0xB7, 0x91, 0x37,
		/* IEND */      0x00, 0x00, 0x00, 0x00, 'I', 'E', 'N', 'D', 0xAE, 0x42, 0x60, 0x82
	};
#endif
#if defined(USE_WINDOWS_GDIPLUS)
	BYTE iendWithLength[] = {
		/* Signature */ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
		/* IHDR */      0x00, 0x00, 0x00, 0x0D, 'I', 'H', 'D', 'R', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x40, 0x69, 0xC9, 0xB2,
		/* IDAT */      0x00, 0x00, 0x00, 0x0A, 'I', 'D', 'A', 'T', 0x18, 0xD3, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x98, 0x63, 0x6C, 0xD7,
		/* IEND */      0x00, 0x00, 0x00, 0x01, 'I', 'E', 'N', 'D', 0x00, 0xAE, 0x42, 0x60, 0x82,
		/* sRGB */      0x00, 0x00, 0x00, 0x01, 's', 'R', 'G', 'B', 0xFF, 0xAE, 0xCE, 0x1C, 0xE9
	};
#endif

	// FIXME: GDI+ allows long IHDR lengths.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (longIhdrLength, PixelFormat1bppIndexed, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif
	// FIXME: GDI+ allows multiple IHDRs.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (multipleIhdrs, PixelFormat4bppIndexed, 6, 4, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif
	//FIXME: GDI+ allows multiple IDATs.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (multipleIdats, PixelFormat1bppIndexed, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif
	// FIXME: GDI+ allows empty IDATs.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (oneEmptyIdat, PixelFormat1bppIndexed, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif
	// FIXME: GDI+ allows multiple palettes
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (multiplePalettes, PixelFormat4bppIndexed, 6, 4, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif
	createFileSuccess (noIend, PixelFormat1bppIndexed, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
	// FIXME: GDI+ does not validate the CRC.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (invalidCrc, PixelFormat1bppIndexed, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif
	// FIXME: GDI+ does not validate the compression.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (invalidCompression, PixelFormat1bppIndexed, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif
	// FIXME: GDI+ does not validate the CRC.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (unknownChunk, PixelFormat4bppIndexed, 6, 4, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif
	// FIXME: GDI+ does not validate the CRC.
#if defined(USE_WINDOWS_GDIPLUS)
	createFileSuccess (iendWithLength, PixelFormat1bppIndexed, 1, 1, ImageFlagsColorSpaceRGB | ImageFlagsHasRealPixelSize | ImageFlagsReadOnly, 3);
#endif

This may not be possible as we rely on libpng

Implement GdipSetImageAttributesToIdentity

In test_setImageAttributesToIdentity in testimageattributes.c

	// FIXME: GdipSetImageAttributesToIdentity is NotImplemented.
#if defined(USE_WINDOWS_GDIPLUS)
	status = GdipSetImageAttributesToIdentity (attributes, ColorAdjustTypeDefault);
	assertEqualInt (status, Ok);
	
	status = GdipSetImageAttributesToIdentity (attributes, ColorAdjustTypeBitmap);
	assertEqualInt (status, Ok);
	
	status = GdipSetImageAttributesToIdentity (attributes, ColorAdjustTypeBrush);
	assertEqualInt (status, Ok);
	
	status = GdipSetImageAttributesToIdentity (attributes, ColorAdjustTypePen);
	assertEqualInt (status, Ok);
	
	status = GdipSetImageAttributesToIdentity (attributes, ColorAdjustTypeText);
	assertEqualInt (status, Ok);
#endif

BMP image flags are wrong

In test_loadImageFromFileBmp in testgpimage.c

static void test_loadImageFromFileBmp ()
{
	GpImage *image = getImage ("test.bmp");

	// FIXME: bmp image flags are wrong in libgdiplus.
	verifyBitmap (image, bmpRawFormat, PixelFormat24bppRGB, 100, 68, 77840, 0, FALSE);

	GdipDisposeImage (image);
}

In test_cloneImage in testgpimage.c

	// ImageTypeBitmap - bmp.
	status = GdipCloneImage (bitmapImage, &clonedImage);
	assertEqualInt (status, Ok);
	assert (clonedImage && clonedImage != bitmapImage);
	// FIXME: bmp image flags are wrong in libgdiplus.
	verifyBitmap (clonedImage, bmpRawFormat, PixelFormat24bppRGB, 100, 68, 77840, 0, FALSE);
	GdipDisposeImage (clonedImage);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.