Giter VIP home page Giter VIP logo

libgdiplus's Introduction

libgdiplus: An Open Source implementation of the GDI+ API.

This is part of the Mono project.

Build status:

Build Status

Requirements:

This requires the libraries used by the Cairo vector graphics library to build (freetype2, fontconfig, Xft2 and libpng).

On OSX you can use Homebrew to install the dependencies:

brew install glib cairo libexif libjpeg giflib libtiff autoconf libtool automake pango pkg-config
brew link gettext --force

On Debian-based Linux distributions you can use apt-get to install the dependencies:

sudo apt-get install libgif-dev autoconf libtool automake build-essential gettext libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev

On Windows you can use Vcpkg to install the dependencies. Run the following commands from the root of the repository from an admin command prompt:

bootstrap-vcpkg.bat
vcpkg.exe install giflib libjpeg-turbo libpng cairo glib tiff libexif pango --triplet x86-windows
vcpkg.exe install giflib libjpeg-turbo libpng cairo glib tiff libexif pango --triplet x64-windows
vcpkg.exe integrate install

Build instructions

To build on OSX without X11:

./autogen.sh --without-x11 --prefix=YOUR_PREFIX
make

To build on OSX with X11 (e.g. from XQuartz):

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

To build on Linux:

./autogen.sh --prefix=YOUR_PREFIX
make

To build on Windows, open libgdiplus.sln.

Running the unit tests

Run the following command from the root of the repository:

make check

To run the tests with Clang sanitizers, run the following command from the root of the repository:

./autogen.sh --enable-asan
make check

To run the unit tests with leak sanitizers, run the following command from the root of the repository:

./autogen.sh --enable-asan
export ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0
export LSAN_OPTIONS=suppressions=lsansuppressions.txt
make check

Code coverage

Code coverage stats are generated with lcov. You can use Homebrew on OSX to install the dependencies:

brew install lcov

To run the tests with code coverage, run the following commands from the root of the repository:

./autogen.sh --enable-coverage
make check
lcov --capture --directory src --output-file coverage.info
genhtml coverage.info --output-directory coverage

To view the coverage report, navigate to the coverage directory in the root of the repository and open index.html.

Installing libgdiplus

Run the following command from the root of the repository:

make install

Optional build options

--with-pango

This builds libgdiplus using Pango to render (measure and draw) 
all of it's text. This requires Pango version 1.38 (or later).

libgdiplus's People

Contributors

akoeplinger avatar dickp avatar directhex avatar duncanmak avatar ermshiperete avatar filipnavara avatar gonzalop avatar harinath avatar hughbe avatar illupus avatar jstedfast avatar knocte avatar kumpera avatar mailaender avatar markusbeth avatar mhosken avatar migueldeicaza avatar n432sean avatar nattynarwhal avatar nealef avatar neilmayhew avatar preferlinux avatar qmfrederik avatar ranma42 avatar shibaev avatar spouliot avatar stephenmcconnel avatar uweigand avatar vargaz avatar wtfrank avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libgdiplus's Issues

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

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

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);

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);

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

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

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

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);

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

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

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

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 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?

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

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); \
}

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

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.

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);

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

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

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

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

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

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

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 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

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);

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

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)

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

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

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

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 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

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

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.