loops/gdimage

Import, transform, export images helping GD. This library provides: GIF support, JPEG support, PNG24 support, PNG8 support, animated GIF support, APNG support, HTTP support, data URI support, palette color enhancement (especially on GD transparent color leaks), resizing (crop, fit, filled), color fi

2.9.1-RC 2016-04-12 14:02 UTC

This package is auto-updated.

Last update: 2024-04-09 12:34:41 UTC


README

Requirements:

At least PHP 5.3. GD library required.

How to use it:

Autoloading

If you do not have composer, call bootstrap:

:::php
  require $path_to_library.'/bootstrap.inc.php';

This library use PSR-0 with one namespace, and will probably never go to PSR-4.

Image format

This library supports GIF, JPEG, PNG24, PNG8, AGIF (animated GIF) and APNG.

Import:

Basic usage

You can import an image from many sources: file, binary, base64, data URI…

All of them are handle by \GDImage\Factory::import() method.

:::php
  $image = \GDImage\Factory::import( $stuff );

By-pass

To optimize performance, you may want to by-pass the factory layer. In that case, you can by-pass file or binary import with the corresponding method of any \GDImage\Image_Interface.

:::php
  // import file as GIF
  $image = new \GDImage\Image_Gif();
  $image->fromFile( $filepath );

  // import binary as PNG
  $image = new \GDImage\Image_Png();
  $image->fromBinary( $filepath );

You must know the MIME Type of the image to by-pass factory layer on import.

Export:

Basic usage

You can export an image to many formats: file, binary data, base64 data, data URI scheme…

All of them are handle in \GDImage\Factory::export() method.

:::php
  // export to file
  // on file export, if no extension is specified on the filepath, it will be 
  // automatically added
  $final_filepath = \GDImage\Factory::export( $image , $filepath );

  // export to data URI
  $data_uri = \GDImage\Factory::export( $image , 'data' );

  // export to base 64
  $data_uri = \GDImage\Factory::export( $image , 'base64' );

  // export to binary
  $data_uri = \GDImage\Factory::export( $image , 'binary' );

  // export to output
  \GDImage\Factory::export( $image , 'output' );

Force MIME Type

If you wish, you can force the image to export to an expected MIME Type.

:::php
  // export to file with PNG MIME Type
  $final_filepath = \GDImage\Factory::export( $image , $filepath , 'image/png' );

Export parameters

If you need extra parameters to the export, the second argument can be an array.

:::php
  // export to data URI without base 64 encoding and MIME Type
  $data_uri = \GDImage\Factory::export( $image , array(
    'driver' => 'DataUri' ,
    'base64' => false ,
    'mimetype' => false ,
  ) );

Unnamed parameters will be passed to the final GD function — imagejpeg(), imagepng()… — but you must know which function will be used.

:::php
  // export to file with JPEG MIME Type and 75 quality
  $final_filepath = \GDImage\Factory::export( $image , array(
    'driver' => 'File' ,
    'file' => $filepath ,
    95 ,
  ) , 'image/jpeg' );

  // export to file with PNG MIME Type, 8 compression level and all PNG filters
  $final_filepath = \GDImage\Factory::export( $image , array(
    'driver' => 'File' ,
    'file' => $filepath ,
    8 ,
    PNG_ALL_FILTERS ,
  ) , 'image/png' );

By-pass

To optimize performance, you may want to by-pass the factory layer. In that case, you can by-pass file or binary export with the corresponding method of any \GDImage\Image_Interface.

:::php
  // export to file
  $success = $image->toFile( $filepath );

  // export to binary
  $binary = $image->toBinary();

Transformations:

The idea

A transformation is something to apply on an image instance in order to modify its ressource, or its ressources in case of animated images.

By separating image manipulation and image resource, the same transformation can be safely applied to any images.

:::php
  $image = \GDImage\Factory::import( $import_filepath );
  $transform = new \GDImage\Transform_GaussianBlur();
  $image->apply( $transform );
  $final_filepath = \GDImage\Factory::export( $image , $export_filepath );

Main transformations

Below is the list of main transformations:

\GDImage\Transform_Resize_Fit: Scale the image to the largest size such that both its width and its height can fit inside the area. One of final width or height may be smaller than expected, not both can be smaller.

:::php
  $image = \GDImage\Factory::import( $import_filepath );
  $transform = new \GDImage\Transform_Resize_Fit( 300 , 300 );
  $image->apply( $transform );
  $final_filepath = \GDImage\Factory::export( $image , $export_filepath );

If you want the image to fit an expected width only, do it like this:

:::php
  $image = \GDImage\Factory::import( $import_filepath );
  $transform = new \GDImage\Transform_Resize_Fit( 300 , \PHP_INT_MAX );
  $image->apply( $transform );
  $final_filepath = \GDImage\Factory::export( $image , $export_filepath );

\GDImage\Transform_Resize_Crop: Scale the image to be as large as possible so that the area is completely covered by the image. Some parts of the image will be cropped and the final width and height will be exactly as expected.

:::php
  $image = \GDImage\Factory::import( $import_filepath );
  $transform = new \GDImage\Transform_Resize_Crop( 300 , 300 );
  $image->apply( $transform );
  $final_filepath = \GDImage\Factory::export( $image , $export_filepath );

\GDImage\Transform_Resize_FitNFill: Scale the image to the largest size such that both its width and its height can fit inside the area. The empty areas will be filled by transparent color so the final width and height will be exactly as expected.

:::php
  $image = \GDImage\Factory::import( $import_filepath );
  $transform = new \GDImage\Transform_Resize_FitNFill( 300 , 300 );
  $image->apply( $transform );
  $final_filepath = \GDImage\Factory::export( $image , $export_filepath );

You can specify a color to fill with:

:::php
  $image = \GDImage\Factory::import( $import_filepath );
  // fill with white
  $transform = new \GDImage\Transform_Resize_FitNFill( 300 , 300 , 'FFFFFF' );
  $image->apply( $transform );
  $final_filepath = \GDImage\Factory::export( $image , $export_filepath );

There is a lot of other transformations so do no hesitate to look on test cases or the Transform folder.

Multiple transformations

You can combine several transformations in one.

:::php
  $image = \GDImage\Factory::import( $import_filepath );
  $transform = new \GDImage\Transform_Multiple(
    new \GDImage\Transform_Grayscale() ,
    new \GDImage\Transform_Negate()
  );
  $image->apply( $transform );
  $final_filepath = \GDImage\Factory::export( $image , $export_filepath );

Register transformations

You can register a transformation, even a multiple transformation, to use it later. By this way, all your transformations can stand in a single place, wherever you use them.

:::php
  $resize = new \GDImage\Transform_Resize_Fit( 300 , 300 );
  \GDImage\Transform_Collection::set( 'a_key_for_the_transformation' , clone $resize );
    (...)
  $image = \GDImage\Factory::import( $import_filepath );
  $image->apply( \GDImage\Transform_Collection::get( 'a_key_for_the_transformation' ) );
  $final_filepath = \GDImage\Factory::export( $image , $export_filepath );

Custom transformations

Each transformation must implements \GDImage\Transform_Interface.

Note that transformation are applied on resource instance, not on image.

:::php
  class MyTransform implements \GDImage\Transform_Interface
  {
    /**
     * Apply transformation to a resource.
     * Return false if the transformation fails.
     *
     * @param \GDImage\Resource_Abstract &$rsc
     * @return boolean Success flag
     * @access public
     */
    public function __invoke( \GDImage\Resource_Abstract &$rsc )
    {
      // your stuff here

      return true || false;
    }
  }

gd_image() helper:

In order to simplify all the image import, transformation and export process, a single function can handle all the mess for you: gd_image().

Call method \GDImage\Config::helper() anywhere in your code and gd_image() function will be available on global namespace.

This function accept three arguments:

  1. $import: Stuff to import, mandatory;
  2. $export: Export parameters, mandatory but can be null;
  3. $transform: Transformation to apply, optional;
  4. $failure: Callback to execute or value to return on failure, optional.

$import

As expected, $import argument can be anything that \GDImage\Factory can import: file path, data URI, base 64 data, binary data…

The only difference with \GDImage\Factory::import() method, is that there is no MIME Type cast.

$export

As expected, $export argument can be anything that \GDImage\Factory can use to export: file path, driver name, export parameters, driver class name, driver instance…

The first difference with \GDImage\Factory::export() method, is that there is no MIME Type cast.

This $export argument also has extra behaviors:

  • a null export will result to a file export;

  • if $export is a string starting with "-" or "_", this string will be considered as a suffix for a file export;

  • if $import is a file path and if a file export does not specify a file name, file export will use same file name;

  • if $import is not a file path and if a file export does not specify a file name, file export will use MD5 hash of $import as file name;

  • if $transform is not null and if a file export does not specify a file name or a suffix, the transformation key — or the underscored class name — prefixed by an underscore "_" will be used as suffix for the file name;

  • if $import is a file path and if file export does not specify a directory, file export will use same directory;

  • if $import is not a file path and if file export does not specify a directory, file export will use system temporary directory;

  • if $import is a file path with creation date — or Last-modified header in HTTP cases — and if file export result to a file that already exists, the new file will not be generated if its creation date is higher than $import.

These behaviors can interact so look at Usage section below for more informations.

$transform

$transform can be a key for a registered transformation, a transformation class name (fully qualified or in \GDImage namespace), a \GDImage transformation class suffix (\GDImage\Transform_*) or a transformation instance.

Note that transformation class name that do not start with "\" may correspond to a \GDImage transformation instead of a global one. Watch out.

$failure

If something went wrong, a E_USER_WARNING error will be triggered and false will be returned. This behavior can be customize helping $failure argument:

  • if $failure is callable, it will be executed and its result will be sent back by the gd_image() helper — arguments of this callback are: the \GDImage\Exception instance thrown, the $stuff argument, the $export argument and the $transform argument;

  • if $failure is not callable and is not null, it will be used as value to return in case of failure — the error will still triggered.

Usage

Export to same file path with suffix:

:::php
  $final_filepath = gd_image( $import_filepath , '_suffix' [, $transform] );

Export to same file path with suffix from transformation:

:::php
  $final_filepath = gd_image( $import_filepath , null , 'transform_key' );
  $final_filepath = gd_image( $import_filepath , null , $transform );
  $final_filepath = gd_image( $import_filepath , null , 'Sepia' );
  $final_filepath = gd_image( $import_filepath , null , 'Transform_Negate' );
  $final_filepath = gd_image( $import_filepath , null , '\\GDImage\\Transform_Grayscale' );

Export to another directory with suffix from transformation:

:::php
  $final_filepath = gd_image( $import , $directory , $transform );

Export to base64:

:::php
  $final_filepath = gd_image( $import , 'base64' [, $transform] );

Export to data URI without MIME Type:

:::php
  $final_filepath = gd_image( $import , array(
    'driver' => 'data' ,
    'mimetype' => false ,
  ) [, $transform] );

AGIF and APNG:

This library support animated GIF (AGIF) and animated PNG (APNG).

"Visible" state

Most of concrete animated pictures are the result of some optimizations: every redudant pixels information are removed and frames are reduced to their minimal size.

In these optimized animated pictures, frame at their real state are just noisy pixels and there is no way to exploit them correctly.

That's why this library always provide frames at a "visible" state: each decomposed frames fit picture size, and if another frame should take place behind to complete transparent pixels, it is done.

This is also true for APNG frame with Blend Option to 1 (blend over): in that case the decomposed frame is the result of the merge of the two frames and the Blend Option is deactivated to avoid recomposition error.

Note that on AGIF, the merge of two frames with distinct color palettes may result to a true color frame if the merge of the two palette exceed 256 colors.

Naming

For convenience, frame properties get their names from specification. AGIF use Delay Time (get/setDelayTime()) and Disposal Method (get/setDisposalMethod()) while APNG use Delay Numerator (get/setDelayNumerator()), Delay Denominator (get/setDelayDenominator()) and Dispose Option (get/setDisposeOption()).

There is no uniformization for now, and it is not planned.

Optimization

Animated picture optimization is operational but a very long process: each frame has to be analyzed, compared to the previous frame then reduced to its minimal size.

By default, optimization is disabled, but it can be enabled using \GDImage\Config::setAGifComposerOptimization() or \GDImage\Config::setAPngComposerOptimization() settings.

These settings are bitwise of these bits:

  • 1: remove transparent pixels on frame borders;
  • 2: remove redundant pixels — from disposed frame to current one — on frame
     borders;
    
  • 4: replace redundant pixels — from disposed frame to current one — of the
     frame by transparent color.
    

We strongly advice to enable animated picture optimization when the animated picture is generated on a background task and disable it when the generation is done on demand.

Examples:

Put all the package to a gdimage folder on your local server, then you will be able to see GDImage in action at http://localhost/gdimage/demo/index.php.

If you want to test a particular image, just put it in demo/samples folder and run the demo.

Also, there is no unit testing on this project, but major test cases are available from this demo.

What's next?

Chromatic transform

It should be cool to apply chromatic transformation on picture (bi-chromatic to N-chromatic).
A picture with X colors will result to an picture with N user-defined colors (no automatic color determination, it is annoying).

Why not Imagick?

A History of Hosting

The library has been created for hosting that does have Imagick extension — yes, there is some.

This library may not have sense on hosting solutions that provide Imagick, since a lot of libraries implement it.

Divide and Rule

Also, I do not appreciate the way Imagick has been designed: having all the stuff in a single huge class sounds like: "This class do resizing, color filtering, file export, coffee, foot massage…"

On GDImage library, there is four basic layers: Resource handle GD resource, Transform manipulate Resource, Image process Resource and Factory manage Image. It may not be the best way to achieve image manipulation, but it is relatively easy to maintain and expand.

POOP implementation:

What's this POOP?

POOP (Permissive Oriented Object Programming) is a recommendation that advocates to voluntarily omit protected and private visibilities to use only public.

  • POOP is not dirty
  • POOP is not useless
  • POOP is not dangerous
  • POOP is not hard

POOP is not poop.

Pseudo-protected properties/methods

This library follows POOP pattern, that means that every property and method is public, even if it should be considered as protected.

In order to distinguish properties/methods that are really public and properties/methods that should be considered as protected, the following conventions have to be considered:

  • If a property/method is prefixed by a single underscore "_", it should be considered as pseudo-protected: do not use it if you do not know what you are doing.

  • If a property is prefixed by two underscores "__", it should be considered as pseudo-protected with special care: do not manipulate it if you are not sure about what you are doing because it can cause big issues.

  • If a method is prefixed by two underscores "__", it is a "magic" PHP method and it can be used considered as public.

POOP on \GDImage\Transform_*

You may notice that all \GDImage\Transform_* classes have only setters.

In fact, having getters on transformations seems meaningless because transformations only needs parameters, and when you assign parameters, you already know them, don't you?

Of course, some of these parameters may be manipulated before assignment, so helping POOP, you are still able to get them directly if something went wrong.

Contributors:

Wanna contribute?

That's here: https://bitbucket.org/lxxps/gdimage/src