Files
server/usr/share/psa-pear/pear/php/Horde/Image/Png.php
2026-01-07 20:52:11 +01:00

364 lines
9.1 KiB
PHP

<?php
/**
* Copyright 2003-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Mike Cochrane <mike@graftonhall.co.nz>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* This class implements the Horde_Image API for PNG images.
*
* It mainly provides some utility functions, such as the ability to make
* pixels or solid images for now.
*
* @author Mike Cochrane <mike@graftonhall.co.nz>
* @category Horde
* @copyright 2003-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
class Horde_Image_Png extends Horde_Image_Base
{
/**
* The array of pixel data.
*
* @var array
*/
protected $_img = array();
/**
* Color depth (only 8 and 16 implemented).
*
* @var integer
*/
protected $_colorDepth = 8;
/**
* Color type (only 2 (true color) implemented).
*
* @var integer
*/
protected $_colorType = 2;
/**
* Compression method (0 is the only current valid value).
*
* @var integer
*/
protected $_compressionMethod = 0;
/**
* Filter method (0 is the only current valid value).
*
* @var integer
*/
protected $_filterMethod = 0;
/**
* Interlace method (only 0 (no interlace) implemented).
*
* @var integer
*/
protected $_interlaceMethod = 0;
/**
* PNG image constructor.
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
if (!empty($params['width'])) {
$this->rectangle(
0, 0, $params['width'], $params['height'],
$this->_background, $this->_background
);
}
}
/**
* Returns the MIME type for this image.
*
* @return string The MIME type for this image.
*/
public function getContentType()
{
return 'image/png';
}
/**
* Returns the raw data for this image.
*
* @return string The raw image data.
*/
public function raw()
{
return $this->_header()
. $this->_IHDR()
/* Say what created the image file. */
. $this->_tEXt('Software', 'Horde_Image_Png')
/* Set the last modified date/time. */
. $this->_tIME()
. $this->_IDAT()
. $this->_IEND();
}
/**
* Resets the image data to defaults.
*/
public function reset()
{
parent::reset();
$this->_img = array();
}
/**
* Draws a rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rectangle.
*/
public function rectangle(
$x, $y, $width, $height, $color = 'black', $fill = 'none'
)
{
list($r, $g, $b) = Horde_Image::getRGB($color);
if ($fill != 'none') {
list($fR, $fG, $fB) = Horde_Image::getRGB($fill);
}
$x2 = $x + $width;
$y2 = $y + $height;
for ($h = $y; $h <= $y2; $h++) {
for ($w = $x; $w <= $x2; $w++) {
// See if we're on an edge.
if ($w == $x || $h == $y || $w == $x2 || $h == $y2) {
$this->_img[$h][$w] = array('r' => $r, 'g' => $g, 'b' => $b);
} elseif ($fill != 'none') {
$this->_img[$h][$w] = array('r' => $fR, 'g' => $fG, 'b' => $fB);
}
}
}
}
/**
* Creates the PNG file header.
*/
protected function _header()
{
return pack('CCCCCCCC', 137, 80, 78, 71, 13, 10, 26, 10);
}
/**
* Creates the IHDR block.
*/
protected function _IHDR()
{
$data = pack(
'a4NNCCCCC',
'IHDR',
$this->_width,
$this->_height,
$this->_colorDepth,
$this->_colorType,
$this->_compressionMethod,
$this->_filterMethod,
$this->_interlaceMethod
);
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Creates the IEND block.
*/
protected function _IEND()
{
$data = 'IEND';
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Creates the IDAT block.
*/
protected function _IDAT()
{
$data = '';
$prevscanline = null;
$filter = 0;
for ($i = 0; $i < $this->_height; $i++) {
$scanline = array();
$data .= chr($filter);
for ($j = 0; $j < $this->_width; $j++) {
if ($this->_colorDepth == 8) {
$scanline[$j] = pack(
'CCC',
$this->_img[$i][$j]['r'],
$this->_img[$i][$j]['g'],
$this->_img[$i][$j]['b']
);
} elseif ($this->_colorDepth == 16) {
$scanline[$j] = pack(
'nnn',
$this->_img[$i][$j]['r'] << 8,
$this->_img[$i][$j]['g'] << 8,
$this->_img[$i][$j]['b'] << 8
);
}
if ($filter == 0) {
/* No Filter. */
$data .= $scanline[$j];
} elseif ($filter == 2) {
/* Up Filter. */
$pixel = $scanline[$j] - $prevscanline[$j];
if ($this->_colorDepth == 8) {
$data .= pack(
'CCC',
$pixel >> 16,
($pixel >> 8) & 0xFF,
$pixel & 0xFF
);
} elseif ($this->_colorDepth == 16) {
$data .= pack(
'nnn',
($pixel >> 32),
($pixel >> 16) & 0xFFFF,
$pixel & 0xFFFF
);
}
}
}
$prevscanline = $scanline;
}
$compressed = gzdeflate($data, 9);
$data = 'IDAT'
. pack(
'CCa' . strlen($compressed) . 'a4',
0x78,
0x01,
$compressed,
$this->_Adler32($data)
);
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Creates the tEXt block.
*/
protected function _tEXt($keyword, $text)
{
$data = 'tEXt' . $keyword . "\0" . $text;
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Creates the tIME block.
*
* @param integer $date A timestamp.
*/
protected function _tIME($date = null)
{
if (is_null($date)) {
$date = time();
}
$data = 'tIME'
. pack(
'nCCCCC',
intval(date('Y', $date)),
intval(date('m', $date)),
intval(date('j', $date)),
intval(date('G', $date)),
intval(date('i', $date)),
intval(date('s', $date))
);
return pack(
'Na' . strlen($data) . 'N',
strlen($data) - 4,
$data,
crc32($data)
);
}
/**
* Calculates an Adler32 checksum for a string.
*/
protected function _Adler32($input)
{
$s1 = 1;
$s2 = 0;
$iMax = strlen($input);
for ($i = 0; $i < $iMax; $i++) {
$s1 = ($s1 + ord($input[$i])) % 0xFFF1;
$s2 = ($s2 + $s1) % 0xFFF1;
}
return pack('N', (($s2 << 16) | $s1));
}
/**
* Requests a specific image from the collection of images.
*
* @param integer $index The index to return
*
* @return Horde_Image_Png
* @throws Horde_Image_Exception
*/
public function getImageAtIndex($index)
{
if ($index > 0) {
throw new Horde_Image_Exception('Image index out of bounds.');
}
return clone($this);
}
/**
* Returns the number of image pages available in the image object.
*
* @return integer The number of images.
*/
public function getImagePageCount()
{
return 1;
}
}