<?php
/**
* Class that converts JPEG,PNG and GIF images to 24bit BMPs

* PHP version 5

* LICENSE:

* Copyright (c) 2008 Srecko Struk

* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.

* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

* @category     Images
* @package      Image2bmp
* @author       Srecko Struk <srecko(dot)struk(at)zg(dot)t-com(dot)hr>
* @license      http://opensource.org/licenses/bsd-license.php New BSD License
* @copyright    2008 Srecko Struk
* @version      0.1.1

*/

/**
* PEAR_Exception class
*/
require_once 'PEAR/Exception.php';

/**#@+
* Constants for DIB Header type
*/
define('IMAGE2BMP_HEADER_V1'1true);
define('IMAGE2BMP_HEADER_V3'3true);
/**#@-*/
/**#@+
* Constants for image2bmp errors
*/
define('IMAGE2BMP_ERROR_INVALID_DIB',        1001true);
define('IMAGE2BMP_ERROR_FILE_NOT_FOUND',     1002true);
define('IMAGE2BMP_ERROR_UNSUPORTED_FORMAT',  1003true);
define('IMAGE2BMP_ERROR_IMAGE_LOAD_ERROR',   1004true);
define('IMAGE2BMP_ERROR_UNKNOWN_DIMENSIONS'1000true);
define('IMAGE2BMP_ERROR_INT_HEADER',         2001true);
define('IMAGE2BMP_ERROR_UNKNOWN_HEADER',     2002true);
/**#@-*/

/**
* Main class - Image2bmp
* @package Images
* @subpackage classes
*/

class Image2bmp 
{
/**
* Private variable that will contain the BMP image binary data
* @access private
* @var string|binary
*/
    
private $data;
    
    
/**
    * Internal private function that converts big-endian data into little-endian data
    * @access private
    * @param  array $data
    * @throws PEAR_error
    * @return string|binary
    */
    
private function be2le($data
    {
        
define('TYPE_UNK'0true);
        
define('TYPE_INT'1true);
        
define('TYPE_STR'2true);
        
$word $data['d'];
        
$size $data['s'];
        
$type = (is_integer($word)?TYPE_INT:(is_string($word)?TYPE_STR:TYPE_UNK));
        
        switch (
$type) {
            case 
TYPE_INT:
            {
                
$pt = (($size == 2)?'v':(($size==4)?'V':NULL));
                if (!
$pt)
                {
                    throw new 
PEAR_Exception('Invalid header entry length for integer type [len='.$size.']',IMAGE2BMP_ERROR_INT_HEADER);
                }
                return 
pack($pt$word);
                break;
            }
            case 
TYPE_STR:
            {
                
$word str_rev(str_pad($word,$size,"\0",STR_PAD_LEFT));
                return 
$word;
                break;
            }
            default:
            {
                throw new 
PEAR_Exception('Unknown header data type',IMAGE2BMP_ERROR_UNKNOWN_HEADER);
                break;
            }
        }
    }
    
    
/**
    * Constructor for the class
    * @access public
    * @param string $file
    * @param const  $dib_type
    * @return img2bmp
    */
    
function __construct($file$dib_type IMAGE2BMP_HEADER_V3){
        if (!((
$dib_type == IMAGE2BMP_HEADER_V1) || ($dib_type == IMAGE2BMP_HEADER_V3))) 
        { 
            throw new 
PEAR_Exception('Invalid DIB header type'IMAGE2BMP_ERROR_INVALID_DIB);
        }
        if (!
file_exists($file)) 
        { 
            throw new 
PEAR_Exception('File not found['.$file.']',IMAGE2BMP_ERROR_FILE_NOT_FOUND);
        }
        
$ext strtolower(substr(strrchr($filename"."), 1));
        switch (
$ext) {
            case 
'jpg' :
            case 
'jpeg'
            {
                
$image = @imagecreatefromjpeg($file); 
                break;
            }
            case 
'png' :
            {
                
$image = @imagecreatefrompng($file);
                break;
            }
            case 
'gif' :
            { 
                
$image = @imagecreatefromgif($file);
                break;
            }
            default: 
            {
                throw new 
PEAR_Exception('Unsuported image format'IMAGE2BMP_ERROR_UNSUPORTED_FORMAT);
            }
        }
        if (!
$image
        { 
            throw new 
PEAR_Exception('Error loading image ['.$file.']'IMAGE2BMP_ERROR_IMAGE_LOAD_ERROR);
        }
        
$width  imagesx($image);
        
$height imagesy($image);
        
$byteC  $width $height 3;
        
$offset = ($dib_type == IMAGE2BMP_HEADER_V1)?26:54;
        if (!(
$width && $height)) 
        { 
            throw new 
PEAR_Exception('Error fetching image dimensions'IMAGE2BMP_ERROR_UNKNOWN_DIMENSIONS);
        }
        
$bmp_file = Array();
        
/* BMP file header */
        
$bmp_file[] = Array('d' => 'BM'     's' => 2); // BMP type 
        
$bmp_file[] = Array('d' => $byteC+54's' => 4); // file size
        
$bmp_file[] = Array('d' => 0        's' => 2); // reserved
        
$bmp_file[] = Array('d' => 0        's' => 2); // reserved
        
$bmp_file[] = Array('d' => $offset  's' => 4); // bmp data offset
        /*DIB header */
        
if ($dib_type == IMAGE2BMP_HEADER_V1//DIB Header >>OS/2 V1<<
        
{
            
$bmp_file[] = Array('d' => 12      's'=>4); // header size in bytes
            
$bmp_file[] = Array('d' => $width  's'=>2); // bitmap width
            
$bmp_file[] = Array('d' => $height 's'=>2); // bitmap height
            
$bmp_file[] = Array('d' => 1       's'=>2); // color planes used (must be 1)
            
$bmp_file[] = Array('d' => 24      's'=>2); // bits per pixel (allways 24)
        

        else 
// DIB Header >>Windows V3<<
        
{
            
$bmp_file[] = Array('d' => 40     's' => 4); // header size
            
$bmp_file[] = Array('d' => $width 's' => 4); // image width in px
            
$bmp_file[] = Array('d' => $height's' => 4); // image height in px
            
$bmp_file[] = Array('d' => 1      's' => 2); // color planes used (must be 1)
            
$bmp_file[] = Array('d' => 24     's' => 2); // color depth (24bits)
            
$bmp_file[] = Array('d' => 0      's' => 4); // compression method -> BI_RGB {NONE}
            
$bmp_file[] = Array('d' => $byteC 's' => 4); // BMP data size
            
$bmp_file[] = Array('d' => 0      's' => 4); // horizontal resolution of image (px/m)  {0 or 2835 for 72dpi}
            
$bmp_file[] = Array('d' => 0      's' => 4); // vertical resolution of image (px/m)  {0 or 2835 for 72dpi}
            
$bmp_file[] = Array('d' => 0      's' => 4); // number of colors in pallete {0 for depth of 16+ bits)
            
$bmp_file[] = Array('d' => 0      's' => 4); // the number of important colors used, or 0 when every color is important; 
        
}
        
/* no pallete information cause we're using 24bit BMP */
        // First two bytes of BMP header are in big-endian order , others are in little-endian
        
$headers = Array($bmp_file[0]['d']); 
        
$c count($bmp_file);
        for (
$i=1$i $c$i++) 
        { 
            
$temp_data $this->be2le($bmp_file[$i]);
            if (
PEAR::isError($temp_data))
            {
                return 
$temp_data;
            }
            else 
            {
                
$headers[] = $temp_data;
            }
        }
        unset(
$bmp_file);
        
$this->data implode($headers);
        unset(
$headers);
    
        
/* image data */
        
$data  = Array();
        
$bonus $width 4;
        
$bonus = ($bonus == 0)?0:4-$bonus;
        
$mw    $width  1;
        
$mh    $height 1;
        
// grabbing pixel by pixel from original image and "copying it to new BMP file
        
for ($h $mh$h >= 0$h--) 
        {
            
$line '';    
            for (
$w 0$w <= $mw$w++) 
            {
                
$col imagecolorat($image,$w,$h);
                
$col imagecolorsforindex($image,$col);
                
$R $col['red']; $G $col['green']; $B $col['blue'];
                
$line .= chr($B).chr($G).chr($R);
            }
        
// length of row in BMP file must be dividable by 4 so we pad it with 0x000000
           
for ($w 0$w $bonus$w++) 
           {
               
$line .= "\0\0\0";
           }
           
$this->data .= $line;
        }
    }
    
    
/**
    * Saves BMP image to file
    * @access public
    * @param string $output
    * @return boolean
    */
    
function save($output
    {
        
$out file_put_contents($output,$this->data);
        return (
$out == strlen($this->data)?true:false);
    }
    
/**
    * Outputs BMP data to the browser
    * @access public
    * @return boolean
    */
    
function display() 
    {
        
$header 'Content-Type: '.image_type_to_mime_type(IMAGETYPE_BMP);
        
header($header);
        
$out file_put_contents('php://output',$this->data);
        return (
$out == strlen($this->data)?true:false);
    }
}
?>