You are here

resmushit.inc in reSmush.it image style optimizer 7

Same filename and directory in other branches
  1. 7.2 resmushit.inc

Additional functions.

File

resmushit.inc
View source
<?php

/**
 * @file
 * Additional functions.
 */

/**
 * @return array
 * Define the new image style effect.
 */
function resmushit_image_effect_info() {
  return array(
    'reSmush.it' => array(
      'label' => t('reSmush.it'),
      'help' => t('Optimize images using reSmush.it.'),
      'effect callback' => 'resmushit_effect',
    ),
  );
}

/**
 * @param $imagesource
 * @param $error
 * @param $sizebefore
 * @param $sizeafter
 * @param $gain
 * Keep a log of last few optimalizations.
 */
function resmushit_log($imagesource, $error, $sizebefore, $sizeafter, $gain, $temp_path = FALSE) {
  $recent_optimalizations = resmushit_trimmed_log();
  $this_operation = array(
    date("Y-m-d H:i:s", REQUEST_TIME),
    $imagesource,
    $error,
    number_format(filesize($imagesource)),
    $sizebefore,
    $sizeafter,
    $gain,
  );
  $recent_optimalizations[] = $this_operation;
  variable_set('resmushit_recent_optimalizations', $recent_optimalizations);
  if ($temp_path) {

    // Since logging is the last operation in resmushit_effect() we may as well delete the temporary file.
    resmushit_delete_temp_file($temp_path);
  }
}

/**
 * @param $temp_path
 * Delete temp file from the temporary directory.
 */
function resmushit_delete_temp_file($temp_path) {
  $debug = FALSE;
  if (variable_get('resmushit_debug', 0) == 1) {
    $debug = TRUE;
  }
  $deleted = file_unmanaged_delete($temp_path);
  if ($debug) {
    $success = 'NO success!';
    if ($deleted) {
      $success = 'SUCCESS!';
    }

    // dpm('Deleting temporary file: ' . $temp_path);
    watchdog('reSmush.it', 'Deleting temporary file: %temp_path ... %success', array(
      '%temp_path' => $temp_path,
      '%success' => $success,
    ), WATCHDOG_INFO);
  }
}

/**
 * @param \stdClass $image
 * @return bool
 * Execute image optimalization through reSmush.it.
 */
function resmushit_effect(&$image, $data) {

  // https://api.drupal.org/api/examples/image_example%21image_example.module/function/image_example_colorize_effect/7.x-1.x
  // Image manipulation should be done to the $image->resource, which will be automatically saved as a new image once all effects have been applied.
  // If your effect makes changes to the $image->resource that relate to any information stored in the $image->info array (width, height, etc.) you should update that information as well.
  // Note that the size of the file here is probably smaller than the original image already because of any preceding image styles (e.g. resizing)
  // Any manipulation must be applied to the $image object. Working with $image->source would negate any results of preceding effects.
  $debug = FALSE;
  if (variable_get('resmushit_debug', 0) == 1) {
    $debug = TRUE;
  }
  $temp_path = FALSE;

  // dpm('image coming in: ');
  // dpm($image);
  // dpm('$image->resource: ' . $image->resource);
  // First we have to get a string corresponding to the what $image->resource points to.
  // Unfortunately we have to use imagejpeg or similar function, which will apply the quality settings set in admin/config/media/image-toolkit and thereby ALWAYS reduce the size even before we send it to reSmush.it
  // This is then what reSmush.it will then get as its source image.
  ob_start();
  resmushit_image_gd_save($image);
  $image_contents = ob_get_contents();
  ob_end_clean();

  // dpm('obstarted size good: ' . strlen($image_contents));
  $apiurl = 'http://api.resmush.it/ws.php';

  // See https://www.resmush.it/api
  if ($debug) {

    // dpm('Using drupal_http_request().');
    watchdog('reSmush.it', 'Using drupal_http_request().', array(), WATCHDOG_INFO);
  }
  $boundary = 'A0sFSD';
  $filename = basename($image->source);
  $mimetype = "application/octet-stream";
  $fileencoded = "--{$boundary}\r\n";
  $fileencoded .= "Content-Disposition: form-data; name=\"files\"; filename=\"{$filename}\"\r\n";
  $fileencoded .= "Content-Transfer-Encoding: binary\r\n";
  $fileencoded .= "Content-Type: {$mimetype}\r\n\r\n";

  // $fileencoded .= file_get_contents(drupal_realpath($image->source)) . "\r\n";
  $fileencoded .= $image_contents . "\r\n";
  $fileencoded .= "--{$boundary}--";
  $options = array(
    'headers' => array(
      "Content-Type" => "multipart/form-data; boundary={$boundary}",
    ),
    'method' => 'POST',
    'timeout' => variable_get('resmushit_timeout', RESMUSHIT_TIMEOUT),
    'data' => $fileencoded,
  );

  // dpm($options);
  $gotten = drupal_http_request($apiurl, $options);

  // dpm($gotten);
  if ($gotten->code != 200) {

    // Important to test for an error immediately here. If no response from WP or other error, end immediately.
    watchdog('reSmush.it', 'Could not optimize image %dst -- the reSmush.it response code was: %gottencode.', array(
      '%dst' => $image->source,
      '%gottencode' => $gotten->code,
    ), WATCHDOG_ERROR);
    resmushit_log($image->source, 'API request error: ' . $gotten->code, '', '', '', $temp_path);
    return FALSE;
  }
  $jsondecoded = json_decode($gotten->data);

  // dpm('$jsondecoded: ');
  // dpm($jsondecoded);
  if (@$jsondecoded->error > 200) {

    // reSmush.it responded but has the image had NOT been optimized?
    // dpm('reSmush.it could not optimize image ' . $image->source . ' (response code was: ... ' . $jsondecoded->error . ' : ' . $jsondecoded->error_long);
    watchdog('reSmush.it', 'Could not optimize image %dst -- the reSmush.it response code was: %gottencode1: %gottencode2.', array(
      '%dst' => $image->source,
      '%gottencode1' => $jsondecoded->error,
      '%gottencode2' => $jsondecoded->error_long,
    ), WATCHDOG_ERROR);
    resmushit_log($image->source, $jsondecoded->error . ' : ' . $jsondecoded->error_long, '', '', '', $temp_path);
    return FALSE;
  }

  // reSmush.it web service did the job and we can download the optimized image
  $result = drupal_http_request($jsondecoded->dest);

  // dpm($jsondecoded->dest);
  // dpm($result);
  if (!isset($result->error)) {

    // dpm('result of http request for optimized image: ');
    // dpm($result);
    // dpm('strlen: ' . strlen($result->data));
    $optimized_image_resource = imagecreatefromstring($result->data);

    // dpm('optimized_image_resource: ');
    // dpm($optimized_image_resource);
    // dpm($image->resource);
    imagedestroy($image->resource);

    // Destroy old first, as in https://api.drupal.org/api/drupal/modules%21system%21image.gd.inc/function/image_gd_load/7.x
    $image->resource = $optimized_image_resource;

    // dpm($image->resource);
    // dpm('modified old image: ');
    // dpm($image);
    // dpm('$image->resource: ' . $image->resource);
    if ($debug) {

      // dpm("reSmush.it has successfully optimized image " . $image->source . ". Size reduction " . number_format($jsondecoded->src_size) . " bytes -> " . number_format($jsondecoded->dest_size) . " bytes ($jsondecoded->percent%).");
      watchdog('reSmush.it', 'Successfully optimized image %dst. Size reduction %before bytes -> %after bytes (%percent%).', array(
        '%dst' => $image->source,
        '%before' => number_format($jsondecoded->src_size),
        '%after' => number_format($jsondecoded->dest_size),
        '%percent' => $jsondecoded->percent,
      ), WATCHDOG_INFO);
    }

    // Increase the counter of successful optimalizations.
    $total_successes = variable_get('resmushit_total_successes', 0);
    $total_successes++;
    variable_set('resmushit_total_successes', $total_successes);
    resmushit_log($image->source, 'SUCCESS', number_format($jsondecoded->src_size) . ' bytes', number_format($jsondecoded->dest_size) . ' bytes', $jsondecoded->percent . '%', $temp_path);
    return TRUE;
  }
  else {

    // We did not manage to download the optimized image from reSmush.it.
    watchdog('reSmush.it', 'We were unable to retrieve the optimalized image %dst from reSmush.it.', array(
      '%dst' => $image->source,
    ), WATCHDOG_ERROR);
    resmushit_log($image->source, 'Error retrieving from reSmush.it: ' . $result->error, '', '', '', $temp_path);
    return FALSE;
  }
}

/**
 * @param \stdClass $image
 * @param $destination
 * @return bool
 * A variant of https://api.drupal.org/api/drupal/modules%21system%21image.gd.inc/function/image_gd_save/7.x
 * except that it outputs the image directly, not saving it.
 */
function resmushit_image_gd_save(stdClass $image, $destination = NULL) {
  $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
  $function = 'image' . $extension;
  if (!function_exists($function)) {
    return FALSE;
  }
  if ($extension == 'jpeg') {
    $success = $function($image->resource, $destination, variable_get('image_jpeg_quality', 75));
  }
  else {

    // Always save PNG images with full transparency.
    if ($extension == 'png') {
      imagealphablending($image->resource, FALSE);
      imagesavealpha($image->resource, TRUE);
    }
    $success = $function($image->resource, $destination);
  }
  return $success;
}

/**
 * @return string
 * Information relevant to the reSmush.it module activity on this site.
 */
function resmushit_dashboard() {
  $result = '';
  $result .= t('<p><h2>reSmush.it module dashboard</h2>');
  $result .= '<p>' . t('' . l(t('reSmush.it'), 'https://www.resmush.it/', array(
    'external' => TRUE,
    'attributes' => array(
      'target' => '_blank',
    ),
  )) . ' is a free image optimalization API that seamlessly replaced the excellent but now defunct Yahoo Smush.it web service. It compresses the size of your images, making them and therefore your website significantly faster to load.');
  $result .= t('<p><h3>Image styles</h3>');
  $result .= '<p>' . t('Effect reSmush.it is currently used by the following image styles on this site:');
  $image_styles = image_styles();
  $configured_image_styles = array();
  foreach ($image_styles as $style) {
    foreach ($style['effects'] as $arr) {
      if ($arr['label'] == 'reSmush.it') {
        $configured_image_styles[$style['name']] = $style['label'];
        break;
      }
    }
  }
  $list_image_styles = array();
  foreach ($configured_image_styles as $key => $val) {
    $list_image_styles[] = l($val, '/admin/config/media/image-styles/edit/' . $key);
  }
  if (empty($list_image_styles)) {
    $list_image_styles[] = 'None yet. Configure them at ' . l(t('admin/config/media/image-styles'), '/admin/config/media/image-styles') . '.';
  }
  $result .= theme_item_list(array(
    'items' => $list_image_styles,
    'title' => '',
    //'List of image styles using the reSmush.it effect',
    'type' => 'ul',
    'attributes' => array(),
  ));
  $result .= t('<p><h3>Successes</h3>');
  $result .= '<p>' . t('Total number of successful optimalizations on this site: ' . variable_get('resmushit_total_successes', 0)) . '.';
  $result .= t('<p><h3>JPEG image quality</h3>');
  $result .= '<p>' . t('JPEG image quality is set to ' . variable_get('image_jpeg_quality', 75) . '. reSmush.it recommends a value of at least 92 (see ' . l(t('here'), 'https://www.resmush.it/api', array(
    'external' => TRUE,
    'attributes' => array(
      'target' => '_blank',
    ),
  )) . '). That you can change at ' . l(t('/admin/config/media/image-toolkit'), '/admin/config/media/image-toolkit') . '.');
  $result .= '<p><h3>' . t('Recent %logsize operations:', array(
    '%logsize' => variable_get('resmushit_log_size', RESMUSHIT_TIMEOUT),
  )) . '</h3>';
  $recent_optimalizations = variable_get('resmushit_recent_optimalizations', array());
  $recent_optimalizations = array_reverse($recent_optimalizations);

  // dpm($recent_optimalizations);
  if (!empty($recent_optimalizations)) {
    $result .= '<table>';
    $result .= '<tr>';
    $result .= '<th>#</th>';
    $result .= '<th>Time</th>';
    $result .= '<th>Image</th>';
    $result .= '<th>Result</th>';
    $result .= '<th>Original size</th>';
    $result .= '<th>Input size</th>';
    $result .= '<th>Output size</th>';
    $result .= '<th>Reduction in size</th>';
    $result .= '</tr>';
    foreach ($recent_optimalizations as $keyrow => $row) {
      $result .= '<tr>';
      $result .= '<td>' . ($keyrow + 1) . '.</td>';
      foreach ($row as $col) {
        $result .= '<td>';
        $result .= $col;
        $result .= '</td>';
      }
      $result .= '</tr>';
    }
    $result .= '</table>';
  }
  return $result;
}

/**
 * @return null
 * Return the log trimmed to the correct length.
 */
function resmushit_trimmed_log() {
  $recent_optimalizations = variable_get('resmushit_recent_optimalizations', array());
  if (sizeof($recent_optimalizations) >= variable_get('resmushit_log_size', RESMUSHIT_LOG_SIZE)) {
    array_shift($recent_optimalizations);
  }
  return $recent_optimalizations;
}