loops/gdimage 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

loops/gdimage

Composer 安装命令:

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

README 文档

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

loops/gdimage 适用场景与选型建议

loops/gdimage 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 110 次下载、GitHub Stars 达 0, 最近一次更新时间为 2014 年 11 月 06 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「image」 「thumbnail」 「gd」 「resizing」 「animated gif」 「color filter」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 loops/gdimage 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 loops/gdimage 我们能提供哪些服务?
定制开发 / 二次开发

基于 loops/gdimage 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 110
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 25
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2014-11-06