cms/drupal/modules/system/image.gd.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * GD2 toolkit for image manipulation within Drupal.
       
     6  */
       
     7 
       
     8 /**
       
     9  * @addtogroup image
       
    10  * @{
       
    11  */
       
    12 
       
    13 /**
       
    14  * Retrieve settings for the GD2 toolkit.
       
    15  */
       
    16 function image_gd_settings() {
       
    17   if (image_gd_check_settings()) {
       
    18     $form['status'] = array(
       
    19       '#markup' => t('The GD toolkit is installed and working properly.')
       
    20     );
       
    21 
       
    22     $form['image_jpeg_quality'] = array(
       
    23       '#type' => 'textfield',
       
    24       '#title' => t('JPEG quality'),
       
    25       '#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'),
       
    26       '#size' => 10,
       
    27       '#maxlength' => 3,
       
    28       '#default_value' => variable_get('image_jpeg_quality', 75),
       
    29       '#field_suffix' => t('%'),
       
    30     );
       
    31     $form['#element_validate'] = array('image_gd_settings_validate');
       
    32 
       
    33     return $form;
       
    34   }
       
    35   else {
       
    36     form_set_error('image_toolkit', t('The GD image toolkit requires that the GD module for PHP be installed and configured properly. For more information see <a href="@url">PHP\'s image documentation</a>.', array('@url' => 'http://php.net/image')));
       
    37     return FALSE;
       
    38   }
       
    39 }
       
    40 
       
    41 /**
       
    42  * Validate the submitted GD settings.
       
    43  */
       
    44 function image_gd_settings_validate($form, &$form_state) {
       
    45   // Validate image quality range.
       
    46   $value = $form_state['values']['image_jpeg_quality'];
       
    47   if (!is_numeric($value) || $value < 0 || $value > 100) {
       
    48     form_set_error('image_jpeg_quality', t('JPEG quality must be a number between 0 and 100.'));
       
    49   }
       
    50 }
       
    51 
       
    52 /**
       
    53  * Verify GD2 settings (that the right version is actually installed).
       
    54  *
       
    55  * @return
       
    56  *   A boolean indicating if the GD toolkit is available on this machine.
       
    57  */
       
    58 function image_gd_check_settings() {
       
    59   // GD2 support is available.
       
    60   return function_exists('imagegd2');
       
    61 }
       
    62 
       
    63 /**
       
    64  * Scale an image to the specified size using GD.
       
    65  *
       
    66  * @param $image
       
    67  *   An image object. The $image->resource, $image->info['width'], and
       
    68  *   $image->info['height'] values will be modified by this call.
       
    69  * @param $width
       
    70  *   The new width of the resized image, in pixels.
       
    71  * @param $height
       
    72  *   The new height of the resized image, in pixels.
       
    73  * @return
       
    74  *   TRUE or FALSE, based on success.
       
    75  *
       
    76  * @see image_resize()
       
    77  */
       
    78 function image_gd_resize(stdClass $image, $width, $height) {
       
    79   $res = image_gd_create_tmp($image, $width, $height);
       
    80 
       
    81   if (!imagecopyresampled($res, $image->resource, 0, 0, 0, 0, $width, $height, $image->info['width'], $image->info['height'])) {
       
    82     return FALSE;
       
    83   }
       
    84 
       
    85   imagedestroy($image->resource);
       
    86   // Update image object.
       
    87   $image->resource = $res;
       
    88   $image->info['width'] = $width;
       
    89   $image->info['height'] = $height;
       
    90   return TRUE;
       
    91 }
       
    92 
       
    93 /**
       
    94  * Rotate an image the given number of degrees.
       
    95  *
       
    96  * @param $image
       
    97  *   An image object. The $image->resource, $image->info['width'], and
       
    98  *   $image->info['height'] values will be modified by this call.
       
    99  * @param $degrees
       
   100  *   The number of (clockwise) degrees to rotate the image.
       
   101  * @param $background
       
   102  *   An hexadecimal integer specifying the background color to use for the
       
   103  *   uncovered area of the image after the rotation. E.g. 0x000000 for black,
       
   104  *   0xff00ff for magenta, and 0xffffff for white. For images that support
       
   105  *   transparency, this will default to transparent. Otherwise it will
       
   106  *   be white.
       
   107  * @return
       
   108  *   TRUE or FALSE, based on success.
       
   109  *
       
   110  * @see image_rotate()
       
   111  */
       
   112 function image_gd_rotate(stdClass $image, $degrees, $background = NULL) {
       
   113   // PHP installations using non-bundled GD do not have imagerotate.
       
   114   if (!function_exists('imagerotate')) {
       
   115     watchdog('image', 'The image %file could not be rotated because the imagerotate() function is not available in this PHP installation.', array('%file' => $image->source));
       
   116     return FALSE;
       
   117   }
       
   118 
       
   119   // PHP 5.5 GD bug: https://bugs.php.net/bug.php?id=65148: To prevent buggy
       
   120   // behavior on negative multiples of 90 degrees we convert any negative
       
   121   // angle to a positive one between 0 and 360 degrees.
       
   122   $degrees -= floor($degrees / 360) * 360;
       
   123 
       
   124   // Convert the hexadecimal background value to a RGBA array.
       
   125   if (isset($background)) {
       
   126     $background = array(
       
   127       'red' => $background >> 16 & 0xFF,
       
   128       'green' => $background >> 8 & 0xFF,
       
   129       'blue' => $background & 0xFF,
       
   130       'alpha' => 0,
       
   131     );
       
   132   }
       
   133   else {
       
   134     // Background color is not specified: use transparent white as background.
       
   135     $background = array(
       
   136       'red' => 255,
       
   137       'green' => 255,
       
   138       'blue' => 255,
       
   139       'alpha' => 127
       
   140     );
       
   141   }
       
   142 
       
   143   // Store the color index for the background as that is what GD uses.
       
   144   $background_idx = imagecolorallocatealpha($image->resource, $background['red'], $background['green'], $background['blue'], $background['alpha']);
       
   145 
       
   146   // Images are assigned a new color palette when rotating, removing any
       
   147   // transparency flags. For GIF images, keep a record of the transparent color.
       
   148   if ($image->info['extension'] == 'gif') {
       
   149     // GIF does not work with a transparency channel, but can define 1 color
       
   150     // in its palette to act as transparent.
       
   151 
       
   152     // Get the current transparent color, if any.
       
   153     $gif_transparent_id = imagecolortransparent($image->resource);
       
   154     if ($gif_transparent_id !== -1) {
       
   155       // The gif already has a transparent color set: remember it to set it on
       
   156       // the rotated image as well.
       
   157       $transparent_gif_color = imagecolorsforindex($image->resource, $gif_transparent_id);
       
   158 
       
   159       if ($background['alpha'] >= 127) {
       
   160         // We want a transparent background: use the color already set to act
       
   161         // as transparent, as background.
       
   162         $background_idx = $gif_transparent_id;
       
   163       }
       
   164     }
       
   165     else {
       
   166       // The gif does not currently have a transparent color set.
       
   167       if ($background['alpha'] >= 127) {
       
   168         // But as the background is transparent, it should get one.
       
   169         $transparent_gif_color = $background;
       
   170       }
       
   171     }
       
   172   }
       
   173 
       
   174   $image->resource = imagerotate($image->resource, 360 - $degrees, $background_idx);
       
   175 
       
   176   // GIFs need to reassign the transparent color after performing the rotate.
       
   177   if (isset($transparent_gif_color)) {
       
   178     $background = imagecolorexactalpha($image->resource, $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']);
       
   179     imagecolortransparent($image->resource, $background);
       
   180   }
       
   181 
       
   182   $image->info['width'] = imagesx($image->resource);
       
   183   $image->info['height'] = imagesy($image->resource);
       
   184   return TRUE;
       
   185 }
       
   186 
       
   187 /**
       
   188  * Crop an image using the GD toolkit.
       
   189  *
       
   190  * @param $image
       
   191  *   An image object. The $image->resource, $image->info['width'], and
       
   192  *   $image->info['height'] values will be modified by this call.
       
   193  * @param $x
       
   194  *   The starting x offset at which to start the crop, in pixels.
       
   195  * @param $y
       
   196  *   The starting y offset at which to start the crop, in pixels.
       
   197  * @param $width
       
   198  *   The width of the cropped area, in pixels.
       
   199  * @param $height
       
   200  *   The height of the cropped area, in pixels.
       
   201  * @return
       
   202  *   TRUE or FALSE, based on success.
       
   203  *
       
   204  * @see image_crop()
       
   205  */
       
   206 function image_gd_crop(stdClass $image, $x, $y, $width, $height) {
       
   207   $res = image_gd_create_tmp($image, $width, $height);
       
   208 
       
   209   if (!imagecopyresampled($res, $image->resource, 0, 0, $x, $y, $width, $height, $width, $height)) {
       
   210     return FALSE;
       
   211   }
       
   212 
       
   213   // Destroy the original image and return the modified image.
       
   214   imagedestroy($image->resource);
       
   215   $image->resource = $res;
       
   216   $image->info['width'] = $width;
       
   217   $image->info['height'] = $height;
       
   218   return TRUE;
       
   219 }
       
   220 
       
   221 /**
       
   222  * Convert an image resource to grayscale.
       
   223  *
       
   224  * Note that transparent GIFs loose transparency when desaturated.
       
   225  *
       
   226  * @param $image
       
   227  *   An image object. The $image->resource value will be modified by this call.
       
   228  * @return
       
   229  *   TRUE or FALSE, based on success.
       
   230  *
       
   231  * @see image_desaturate()
       
   232  */
       
   233 function image_gd_desaturate(stdClass $image) {
       
   234   // PHP installations using non-bundled GD do not have imagefilter.
       
   235   if (!function_exists('imagefilter')) {
       
   236     watchdog('image', 'The image %file could not be desaturated because the imagefilter() function is not available in this PHP installation.', array('%file' => $image->source));
       
   237     return FALSE;
       
   238   }
       
   239 
       
   240   return imagefilter($image->resource, IMG_FILTER_GRAYSCALE);
       
   241 }
       
   242 
       
   243 /**
       
   244  * GD helper function to create an image resource from a file.
       
   245  *
       
   246  * @param $image
       
   247  *   An image object. The $image->resource value will populated by this call.
       
   248  * @return
       
   249  *   TRUE or FALSE, based on success.
       
   250  *
       
   251  * @see image_load()
       
   252  */
       
   253 function image_gd_load(stdClass $image) {
       
   254   $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
       
   255   $function = 'imagecreatefrom' . $extension;
       
   256   if (function_exists($function) && $image->resource = $function($image->source)) {
       
   257     if (imageistruecolor($image->resource)) {
       
   258       return TRUE;
       
   259     }
       
   260     else {
       
   261       // Convert indexed images to truecolor, copying the image to a new
       
   262       // truecolor resource, so that filters work correctly and don't result
       
   263       // in unnecessary dither.
       
   264       $resource = image_gd_create_tmp($image, $image->info['width'], $image->info['height']);
       
   265       if ($resource) {
       
   266         imagecopy($resource, $image->resource, 0, 0, 0, 0, imagesx($resource), imagesy($resource));
       
   267         imagedestroy($image->resource);
       
   268         $image->resource = $resource;
       
   269       }
       
   270     }
       
   271     return (bool) $image->resource;
       
   272   }
       
   273   return FALSE;
       
   274 }
       
   275 
       
   276 /**
       
   277  * GD helper to write an image resource to a destination file.
       
   278  *
       
   279  * @param $image
       
   280  *   An image object.
       
   281  * @param $destination
       
   282  *   A string file URI or path where the image should be saved.
       
   283  * @return
       
   284  *   TRUE or FALSE, based on success.
       
   285  *
       
   286  * @see image_save()
       
   287  */
       
   288 function image_gd_save(stdClass $image, $destination) {
       
   289   $scheme = file_uri_scheme($destination);
       
   290   // Work around lack of stream wrapper support in imagejpeg() and imagepng().
       
   291   if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
       
   292     // If destination is not local, save image to temporary local file.
       
   293     $local_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
       
   294     if (!isset($local_wrappers[$scheme])) {
       
   295       $permanent_destination = $destination;
       
   296       $destination = drupal_tempnam('temporary://', 'gd_');
       
   297     }
       
   298     // Convert stream wrapper URI to normal path.
       
   299     $destination = drupal_realpath($destination);
       
   300   }
       
   301 
       
   302   $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
       
   303   $function = 'image' . $extension;
       
   304   if (!function_exists($function)) {
       
   305     return FALSE;
       
   306   }
       
   307   if ($extension == 'jpeg') {
       
   308     $success = $function($image->resource, $destination, variable_get('image_jpeg_quality', 75));
       
   309   }
       
   310   else {
       
   311     // Always save PNG images with full transparency.
       
   312     if ($extension == 'png') {
       
   313       imagealphablending($image->resource, FALSE);
       
   314       imagesavealpha($image->resource, TRUE);
       
   315     }
       
   316     $success = $function($image->resource, $destination);
       
   317   }
       
   318   // Move temporary local file to remote destination.
       
   319   if (isset($permanent_destination) && $success) {
       
   320     return (bool) file_unmanaged_move($destination, $permanent_destination, FILE_EXISTS_REPLACE);
       
   321   }
       
   322   return $success;
       
   323 }
       
   324 
       
   325 /**
       
   326  * Create a truecolor image preserving transparency from a provided image.
       
   327  *
       
   328  * @param $image
       
   329  *   An image object.
       
   330  * @param $width
       
   331  *   The new width of the new image, in pixels.
       
   332  * @param $height
       
   333  *   The new height of the new image, in pixels.
       
   334  * @return
       
   335  *   A GD image handle.
       
   336  */
       
   337 function image_gd_create_tmp(stdClass $image, $width, $height) {
       
   338   $res = imagecreatetruecolor($width, $height);
       
   339 
       
   340   if ($image->info['extension'] == 'gif') {
       
   341     // Find out if a transparent color is set, will return -1 if no
       
   342     // transparent color has been defined in the image.
       
   343     $transparent = imagecolortransparent($image->resource);
       
   344 
       
   345     if ($transparent >= 0) {
       
   346       // Find out the number of colors in the image palette. It will be 0 for
       
   347       // truecolor images.
       
   348       $palette_size = imagecolorstotal($image->resource);
       
   349       if ($palette_size == 0 || $transparent < $palette_size) {
       
   350         // Set the transparent color in the new resource, either if it is a
       
   351         // truecolor image or if the transparent color is part of the palette.
       
   352         // Since the index of the transparency color is a property of the
       
   353         // image rather than of the palette, it is possible that an image
       
   354         // could be created with this index set outside the palette size (see
       
   355         // http://stackoverflow.com/a/3898007).
       
   356         $transparent_color = imagecolorsforindex($image->resource, $transparent);
       
   357         $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
       
   358 
       
   359         // Flood with our new transparent color.
       
   360         imagefill($res, 0, 0, $transparent);
       
   361         imagecolortransparent($res, $transparent);
       
   362       }
       
   363       else {
       
   364         imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
       
   365       }
       
   366     }
       
   367   }
       
   368   elseif ($image->info['extension'] == 'png') {
       
   369     imagealphablending($res, FALSE);
       
   370     $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
       
   371     imagefill($res, 0, 0, $transparency);
       
   372     imagealphablending($res, TRUE);
       
   373     imagesavealpha($res, TRUE);
       
   374   }
       
   375   else {
       
   376     imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
       
   377   }
       
   378 
       
   379   return $res;
       
   380 }
       
   381 
       
   382 /**
       
   383  * Get details about an image.
       
   384  *
       
   385  * @param $image
       
   386  *   An image object.
       
   387  * @return
       
   388  *   FALSE, if the file could not be found or is not an image. Otherwise, a
       
   389  *   keyed array containing information about the image:
       
   390  *   - "width": Width, in pixels.
       
   391  *   - "height": Height, in pixels.
       
   392  *   - "extension": Commonly used file extension for the image.
       
   393  *   - "mime_type": MIME type ('image/jpeg', 'image/gif', 'image/png').
       
   394  *
       
   395  * @see image_get_info()
       
   396  */
       
   397 function image_gd_get_info(stdClass $image) {
       
   398   $details = FALSE;
       
   399   $data = @getimagesize($image->source);
       
   400 
       
   401   if (isset($data) && is_array($data)) {
       
   402     $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
       
   403     $extension = isset($extensions[$data[2]]) ?  $extensions[$data[2]] : '';
       
   404     $details = array(
       
   405       'width'     => $data[0],
       
   406       'height'    => $data[1],
       
   407       'extension' => $extension,
       
   408       'mime_type' => $data['mime'],
       
   409     );
       
   410   }
       
   411 
       
   412   return $details;
       
   413 }
       
   414 
       
   415 /**
       
   416  * @} End of "addtogroup image".
       
   417  */