<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Compare images
 *
 * Checks the differences between 2 images
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Image
 * @package   Diff
 * @author    Christian Weiske <cweiske@php.net>
 * @author    Dave Hall <info@davehall.com.au>
 * @copyright 2009 The PHP Group
 * @license   http://www.gnu.org/licenses/lgpl.txt  LGPL v3 or later
 * @version   SVN: $Id$
 * @link      http://pear.php.net/package/Image_Diff
 */

/**
 * @see Image_Diff_Exception
 */
require_once 'Image/Diff/Exception.php';

/**
 * Compare Images 
 * 
 * @category  Image
 * @package   Diff
 * @author    Christian Weiske <cweiske@php.net>
 * @author    Dave Hall <info@davehall.com.au>
 * @copyright 2009 The PHP Group
 * @license   http://www.gnu.org/licenses/lgpl.txt  LGPL v3 or later
 * @version   Release: @package_version@
 * @link      http://pear.php.net/package/Image_Diff
 */
class Image_Diff
{
    
/**
     * Temporary error handler for file errors
     *
     * @param integer $errno      Error level
     * @param string  $errstr     Error message
     * @param string  $errfile    File the error occurred in
     * @param string  $errline    Line the error occurred at
     * @param array   $errcontext Backtrace for the error
     * 
     * @throws Image_Diff_Exception
     * 
     * @return void
     */
    
public static function handleFileErrors($errno$errstr$errfile null
        
$errline null, array $errcontext null)
    {
        
restore_error_handler();
        throw new 
Image_Diff_Exception("File error: {$errstr}");
    }
    
    
/**
     * Check to see if 2 image files are exactly the same
     * 
     * @param string $filename1 Base file for comaparison
     * @param string $filename2 Other file for comparison
     * 
     * @return boolean Are both files equal in size and are the pixels arranged the same?
     */
    
public static function exactlySameFile($filename1$filename2)
    {
        return 
self::nearlySameFile($filename1$filename20);
    }

    
/**
     * Check to see if 2 image files are nearly the same
     * 
     * @param string  $filename1       Base file for comaparison
     * @param string  $filename2       Other file for comparison
     * @param integer $nMaxAverageDiff The maximum average deviation (fuzz) permitted
     * 
     * @return boolean Are the files similar enough?
     */
    
public static function nearlySameFile($filename1$filename2$nMaxAverageDiff)
    {
        if (!
file_exists($filename1)) {
            throw new 
Image_Diff_Exception('File 1 does not exist: ' $filename1);
        }
        if (!
file_exists($filename2)) {
            throw new 
Image_Diff_Exception('File 2 does not exist: ' $filename2);
        }

        return 
self::nearlySame(file_get_contents($filename1),
            
file_get_contents($filename2),
            
$nMaxAverageDiff);
    }

    
/**
    * Checks if two images contain the same image, pixel by pixel
    *
    * @param mixed $file1 GD image resource OR content as string
    * @param mixed $file2 GD image resource OR content as string
    *
    * @return boolean True if they are the same, false if not
    */
    
public static function exactlySame($file1$file2)
    {
        return 
self::nearlySame($file1$file20);
    }

    
/**
    * Checks if two images contain the same image, pixel by pixel
    *
    * @param mixed $file1           GD image resource OR content as string
    * @param mixed $file2           GD image resource OR content as string
    * @param int   $nMaxAverageDiff Maximum rgb-average difference to the given color
    *
    * @return boolean True if they are both equal enough, false if not
    */
    
public static function nearlySame($file1$file2$nMaxAverageDiff)
    {
        
set_error_handler(array(self'handleFileErrors'));

        if (
is_string($file1)) {
            
$i1 imagecreatefromstring($file1);
        } else {
            
$i1 $file1;
        }

        if (
is_string($file2)) {
            
$i2 imagecreatefromstring($file2);
        } else {
            
$i2 $file2;
        }
        
        
restore_error_handler();

        
$sx1 imagesx($i1);
        
$sy1 imagesy($i1);
        if (
$sx1 != imagesx($i2) || $sy1 != imagesy($i2)) {
            
//image size does not match
            
return false;
        }

        
$bOk      true;
        
$nMaxDiff 0;

        for (
$x 0$x $sx1$x++) {
            for (
$y 0$y $sy1$y++) {
    
                
$rgb1  imagecolorat($i1$x$y);
                
$pix1a = array(
                    
'r' => ($rgb1 >> 16) & 0xFF,
                    
'g' => ($rgb1 >> 8) & 0xFF,
                    
'b' =>  $rgb1 0xFF
                
);
                
$pix1  imagecolorsforindex($i1$rgb1);
    
                
$rgb2  imagecolorat($i2$x$y);
                
$pix2a = array(
                    
'r' => ($rgb2 >> 16) & 0xFF,
                    
'g' => ($rgb2 >> 8) & 0xFF,
                    
'b' =>  $rgb2 0xFF
                
);
                
$pix2  imagecolorsforindex($i2$rgb2);
    
                if (
$pix1 != $pix2) {
                    
$bOk false;
                    if (
$nMaxAverageDiff == 0) {
                        break 
2;
                    } else {
                        
$nThisDiff array_sum(array(
                                
abs($pix1a['r'] - $pix2a['r']),
                                
abs($pix1a['g'] - $pix2a['g']),
                                
abs($pix1a['b'] - $pix2a['b'])
                            )) / 
3;
                        if (
$nThisDiff $nMaxDiff) {
                            
$nMaxDiff $nThisDiff;
                        }
                    }
                }
            }
        }

        
imagedestroy($i1);
        
imagedestroy($i2);

        if (
$bOk || $nMaxAverageDiff == 0) {
            return 
$bOk;
        }
        
        return 
$nMaxDiff <= $nMaxAverageDiff;
    }
}
?>