Table of Contents

PHP RFC: Update ext/gd to latest features

Introduction

Add and expose libgd's 2.4 features:

* New codecs and codecs completeness

* New 2D vector APIs * Perceptual Diff (can finally be used for more arch resilient tests in php)

Proposal

Expose new libgd features and enhancement to ext/gd

Examples

2D Vector APIs

Gradient modes and compositions

Some drawings

Backward Incompatible Changes

Color match fix breaks BC as the correct behavior is implemented now

Correct FT rendering and BBox on angle, baseline etc. affects slightly bounding box on axis aligned texts, and significantly more on rotated texts. The bug in the FT2 implementations actually prevented accurate rendering.

Codecs

PNG

<?php
 
/**
 * @generate-class-entries
 * @generate-c-enums
 */
 
namespace Gd\Codec {
    /** @strict-properties */
    class CodecException extends \RuntimeException
    {
    }
}
 
namespace Gd\Png {
#ifdef HAVE_GD_PNG_WRITE_OPTIONS
    enum Filter
    {
        case None;
        case Sub;
        case Up;
        case Average;
        case Paeth;
    }
 
    enum CompressionStrategy
    {
        case Default;
        case Filtered;
        case HuffmanOnly;
        case Rle;
        case Fixed;
    }
 
    /** @strict-properties */
    final readonly class WriteOptions
    {
        public int $compressionLevel;
        public array $filters;
        public CompressionStrategy $compressionStrategy;
 
        /** @param list<Filter> $filters */
        public function __construct(
            int $compressionLevel = -1,
            array $filters = [],
            CompressionStrategy $compressionStrategy = CompressionStrategy::Default,
        ) {}
    }
 
    final class Codec
    {
        private function __construct() {}
 
        public static function toFile(
            \GdImage $image,
            string $path,
            WriteOptions $options = new WriteOptions(),
        ): void {}
 
        /** @param resource $stream */
        public static function toStream(
            \GdImage $image,
            $stream,
            WriteOptions $options = new WriteOptions(),
        ): void {}
 
        public static function toString(
            \GdImage $image,
            WriteOptions $options = new WriteOptions(),
        ): string {}
    }
#endif
}

QOI

<?php
 
/**
 * @generate-class-entries
 * @generate-c-enums
 */
 
namespace Gd\Codec {
    /** @strict-properties */
    class CodecException extends \RuntimeException
    {
    }
}
 
namespace Gd\Qoi {
#ifdef HAVE_GD_QOI
    enum Colorspace
    {
        case SRGB;
        case Linear;
    }
 
    final class Codec
    {
        private function __construct() {}
 
        public static function fromFile(string $path): \GdImage {}
 
        /** @param resource $stream */
        public static function fromStream($stream): \GdImage {}
 
        public static function fromString(string $bytes): \GdImage {}
 
        public static function toFile(
            \GdImage $image,
            string $path,
            Colorspace $colorspace = Colorspace::SRGB,
        ): void {}
 
        /** @param resource $stream */
        public static function toStream(
            \GdImage $image,
            $stream,
            Colorspace $colorspace = Colorspace::SRGB,
        ): void {}
 
        public static function toString(
            \GdImage $image,
            Colorspace $colorspace = Colorspace::SRGB,
        ): string {}
    }
#endif
}

TIFF

<?php
 
/**
 * @generate-class-entries
 * @generate-c-enums
 */
 
namespace Gd\Codec {
    /** @strict-properties */
    class CodecException extends \RuntimeException
    {
    }
}
 
namespace Gd\Tiff {
#if defined(HAVE_GD_TIFF_WRITE_API) || defined(HAVE_GD_TIFF_READ_API)
    enum ColorSpace
    {
        case Rgb;
        case Rgba;
        case Gray;
    }
 
    enum Compression
    {
        case None;
        case Lzw;
        case PackBits;
        case Deflate;
    }
 
    enum ResolutionUnit
    {
        case None;
        case Inch;
        case Centimeter;
    }
#endif

#ifdef HAVE_GD_TIFF_WRITE_API
    /** @strict-properties */
    final readonly class WriteOptions
    {
        public Compression $compression;
        public ColorSpace $colorSpace;
        public bool $minIsWhite;
        public ResolutionUnit $resolutionUnit;
        public ?float $xResolution;
        public ?float $yResolution;
 
        public function __construct(
            Compression $compression = Compression::Deflate,
            ColorSpace $colorSpace = ColorSpace::Rgba,
            bool $minIsWhite = false,
            ResolutionUnit $resolutionUnit = ResolutionUnit::Inch,
            ?float $xResolution = null,
            ?float $yResolution = null,
        ) {}
    }
 
    /**
     * @strict-properties
     * @not-serializable
     */
    final class Writer
    {
        private function __construct() {}
 
        public static function toFile(
            string $path,
            WriteOptions $options = new WriteOptions(),
        ): \Gd\Tiff\Writer {}
 
        /** @param resource $stream */
        public static function toStream(
            $stream,
            WriteOptions $options = new WriteOptions(),
        ): \Gd\Tiff\Writer {}
 
        public static function toMemory(
            WriteOptions $options = new WriteOptions(),
        ): \Gd\Tiff\Writer {}
 
        public function addPage(\GdImage $image): static {}
 
        public function finish(): ?string {}
    }
#endif

#ifdef HAVE_GD_TIFF_READ_API
    /** @strict-properties */
    final readonly class Info
    {
        public int $width;
        public int $height;
        public int $pageCount;
        public int $bitsPerSample;
        public int $samplesPerPixel;
        public ?Compression $compression;
        public ?ColorSpace $colorSpace;
        public bool $minIsWhite;
        public ?float $xResolution;
        public ?float $yResolution;
        public ?ResolutionUnit $resolutionUnit;
 
        public function __construct(
            int $width,
            int $height,
            int $pageCount,
            int $bitsPerSample,
            int $samplesPerPixel,
            ?Compression $compression,
            ?ColorSpace $colorSpace,
            bool $minIsWhite,
            ?float $xResolution,
            ?float $yResolution,
            ?ResolutionUnit $resolutionUnit,
        ) {}
    }
 
    /** @strict-properties */
    final readonly class Page
    {
        public \GdImage $image;
        public int $pageIndex;
        public int $width;
        public int $height;
        public int $bitsPerSample;
        public int $samplesPerPixel;
        public ?Compression $compression;
        public ?ColorSpace $colorSpace;
        public bool $minIsWhite;
        public bool $hasAlpha;
        public bool $isTiled;
        public ?float $xResolution;
        public ?float $yResolution;
        public ?ResolutionUnit $resolutionUnit;
 
        public function __construct(
            \GdImage $image,
            int $pageIndex,
            int $width,
            int $height,
            int $bitsPerSample,
            int $samplesPerPixel,
            ?Compression $compression,
            ?ColorSpace $colorSpace,
            bool $minIsWhite,
            bool $hasAlpha,
            bool $isTiled,
            ?float $xResolution,
            ?float $yResolution,
            ?ResolutionUnit $resolutionUnit,
        ) {}
    }
 
    /**
     * @strict-properties
     * @not-serializable
     */
    final class Reader
    {
        private function __construct() {}
 
        public static function fromFile(string $path): \Gd\Tiff\Reader {}
 
        public static function fromString(string $bytes): \Gd\Tiff\Reader {}
 
        /** @param resource $stream */
        public static function fromStream($stream): \Gd\Tiff\Reader {}
 
        public function info(): Info {}
 
        public function next(): ?Page {}
    }
#endif
}
 
}

Proposed PHP Version(s)

PHP 8.6.0

RFC Impact

To the Ecosystem

New APIs exposed, no new syntax.

To Existing Extensions

ext/gd

Open Issues

Make sure there are no open issues when the vote starts!

Future Scope

latest libgd prepares the road to libgd 3.0 which adds more internal buffer formats and ability to handle HDR natively. Fromats like actual 32bits ARGB, floating points buffers, etc.

Voting Choices

Pick a title that reflects the concrete choice people will vote on.

Please consult the php/policies repository for the current voting guidelines.


Primary Vote requiring a 2/3 majority to accept the RFC:

Implement $feature as outlined in the RFC?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

Patches and Tests

libgd sync only

Implementation

There are two separate PRs:

    1. The winbuilds already include the new dependencyes
  1. The additions and changes to ext/gd once the sync is merged is not yet pushed

References

Rejected Features

Keep this updated with features that were discussed on the mail lists.

Changelog

If there are major changes to the initial proposal, please include a short summary with a date or a link to the mailing list announcement here, as not everyone has access to the wikis' version history.