Welcome to Dream.In.Code
Getting Help is Easy!

Join 132,704 Programmers for FREE! Get instant access to thousands of experts, tutorials, code snippets, and more! There are 1,376 people online right now. Registration is fast and FREE... Join Now!




PHP PNG Library

 
Reply to this topicStart new topic

PHP PNG Library, An attempt at creating a non-extension PNG library for PHP.

grimpirate
post 16 Sep, 2008 - 11:37 AM
Post #1


D.I.C Head

Group Icon
Joined: 3 Aug, 2006
Posts: 187



Thanked 5 times

Dream Kudos: 375
My Contributions


Alright here's my idea. Some servers don't provide the gd or zlib extensions for PHP. PNG is a widely accepted internet picture format. If you don't have gd for php it's impossible to make things such as captcha images and the like. Therefore, I'm working on creating a PNG library that works using only the already available PHP functions (those that don't need a special install or directive enable). Right now the code I have is very limited. The limitations are as follows:
  • Images must be truecolor with alpha
  • No error handling
  • zlib is implemented using the gzcompress function of php (must remake zlib to be completely independent)
  • PNG output images have only been viewable thus far on Opera
If anyone can offer some help/guidance as to why these images aren't viewable on other browsers or in media editors/picture viewers please let me know what I'm doing wrong. To me the PNG stream seems compliant, but I'm no guru. I can also save the image from the browser and view it using Windows Picture and Fax Viewer and Paint surprisingly. However, Internet Explorer and Firefox won't show the image. Two PHP files work the code as of now. Here they are:
png.php
php
<?php
require_once('crcpng.php');
header("Content-type: image/png");
$png = new PNG(69, 5);
$png->line(0, 0, 3, 0);
$png->line(6, 0, 9, 0);
$png->line(12, 0, 16, 0);
$png->line(19, 0, 21, 0);
$png->line(24, 0, 28, 0);
$png->line(32, 0, 36, 0);
$png->pixel(38, 0);
$png->pixel(42, 0);
$png->line(46, 0, 50, 0);
$png->line(52, 0, 56, 0);
$png->line(58, 0, 61, 0);
$png->line(64, 0, 68, 0);
$png->pixel(0, 1);
$png->pixel(4, 1);
$png->pixel(6, 1);
$png->pixel(10, 1);
$png->pixel(12, 1);
$png->pixel(18, 1);
$png->pixel(22, 1);
$png->pixel(24, 1);
$png->pixel(26, 1);
$png->pixel(28, 1);
$png->pixel(34, 1);
$png->pixel(38, 1);
$png->pixel(39, 1);
$png->pixel(42, 1);
$png->pixel(46, 1);
$png->pixel(52, 1);
$png->pixel(56, 1);
$png->pixel(58, 1);
$png->pixel(62, 1);
$png->pixel(64, 1);
$png->pixel(0, 2);
$png->pixel(4, 2);
$png->line(6, 2, 10, 2);
$png->line(12, 2, 15, 2);
$png->pixel(18, 2);
$png->pixel(22, 2);
$png->pixel(24, 2);
$png->pixel(26, 2);
$png->pixel(28, 2);
$png->pixel(34, 2);
$png->pixel(38, 2);
$png->pixel(40, 2);
$png->pixel(42, 2);
$png->pixel(46, 2);
$png->pixel(52, 2);
$png->pixel(56, 2);
$png->pixel(58, 2);
$png->pixel(62, 2);
$png->line(64, 2, 67, 2);
$png->pixel(0, 3);
$png->pixel(4, 3);
$png->pixel(6, 3);
$png->pixel(8, 3);
$png->pixel(12, 3);
$png->line(18, 3, 22, 3);
$png->pixel(24, 3);
$png->pixel(26, 3);
$png->pixel(28, 3);
$png->pixel(34, 3);
$png->pixel(38, 3);
$png->pixel(41, 3);
$png->pixel(42, 3);
$png->pixel(46, 3);
$png->pixel(52, 3);
$png->pixel(56, 3);
$png->pixel(58, 3);
$png->pixel(62, 3);
$png->pixel(64, 3);
$png->line(0, 4, 3, 4);
$png->pixel(6, 4);
$png->pixel(9, 4);
$png->pixel(10, 4);
$png->line(12, 4, 16, 4);
$png->pixel(18, 4);
$png->pixel(22, 4);
$png->pixel(24, 4);
$png->pixel(28, 4);
$png->pixel(30, 4);
$png->line(32, 4, 36, 4);
$png->pixel(38, 4);
$png->pixel(42, 4);
$png->pixel(44, 4);
$png->line(46, 4, 50, 4);
$png->line(52, 4, 56, 4);
$png->line(58, 4, 61, 4);
$png->line(64, 4, 68, 4);
echo $png->resultantImage();

class PNG {
var $signature; // 8 bytes
var $width; // 4 bytes
var $height; // 4 bytes
var $bit_depth; // 1 byte
var $colour_type; // 1 byte
var $compression_method; // 1 byte
var $filter_method; // 1 byte
var $interlace_method; // 1 byte
var $pixelData; // n bytes

function PNG($width, $height, $bit_depth = 8, $colour_type = 6, $compression_method = 0, $filter_method = 0, $interlace_method = 0){
$this->signature = chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10);
$this->width = $width;
$this->height = $height;
$this->bit_depth = $bit_depth;
$this->colour_type = $colour_type;
$this->compression_method = $compression_method;
$this->filter_method = $filter_method;
$this->interlace_method = $interlace_method;
for($i = 0; $i < $height; $i++){
$this->pixelData[] = array();
for($j = 0; $j < $width; $j++){
$this->pixelData[$i][] = chr(255) . chr(255) . chr(255) . chr(0);
}
}
}

function chunk($type){
$chunk = array(
'length' => null, // 4 bytes
'type' => null, // 4 bytes
'data' => null, // n bytes
'crc' => null // 4 bytes
);
if(!strcmp('IHDR', $type)){
$chunk['type'] = 'IHDR';
$chunk['data'] = writableInt($this->width) . writableInt($this->height) . chr($this->bit_depth) . chr($this->colour_type) . chr($this->compression_method) . chr($this->filter_method) . chr($this->interlace_method);
}elseif(!strcmp('IDAT', $type)){
$chunk['type'] = 'IDAT';
$scanlines = array_values($this->pixelData);
$scanlines = array_map('scanser', $scanlines);
$scanlines = gzcompress(implode('', $scanlines));
$chunk['data'] = $scanlines;
}elseif(!strcmp('IEND', $type)){
$chunk['type'] = 'IEND';
}
$chunk['length'] = writableInt(strlen($chunk['data']));
$chunk['crc'] = writableInt(crc($chunk['type'] . $chunk['data']));
return implode('', $chunk);
}

function resultantImage(){
return $this->signature . $this->chunk('IHDR') . $this->chunk('IDAT') . $this->chunk('IEND');
}

function pixel($x, $y, $r = 0, $g = 0, $b = 0, $a = 255){
$this->pixelData[$y][$x] = chr($r) . chr($g) . chr($b) . chr($a);
}

/*
Makes use of the Bresenham line algorithm
http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
*/
function line($x0, $y0, $x1, $y1, $r = 0, $g = 0, $b = 0, $a = 255){
$steep = abs($y1 - $y0) > abs($x1 - $x0);
if($steep){
$temp = $x0;
$x0 = $y0;
$y0 = $temp;
$temp = $x1;
$x1 = $y1;
$y1 = $temp;
}
if($x0 > $x1){
$temp = $x0;
$x0 = $x1;
$x1 = $temp;
$temp = $y0;
$y0 = $y1;
$y1 = $temp;
}
$deltax = $x1 - $x0;
$deltay = abs($y1 - $y0);
$error = $deltax / 2;
$y = $y0;
if($y0 < $y1)
$ystep = 1;
else
$ystep = -1;
for($x = $x0; $x <= $x1; $x++){
if($steep)
$this->pixelData[$x][$y] = chr($r) . chr($g) . chr($b) . chr($a);
else
$this->pixelData[$y][$x] = chr($r) . chr($g) . chr($b) . chr($a);
$error = $error - $deltay;
if($error < 0){
$y += $ystep;
$error += $deltax;
}
}
}
}

function scanser($value){
return chr(0) . implode('', $value);
}

function writableInt($number){
$output = '';
for($i = 0; $i > -32; $i -= 8){
$output .= chr(bitRotate32($number, $i) & 0x000000ff);
}
return strrev($output);
}
// Negatives rotate right, positives rotate left
// Made by Nina Cording from the php.net site
function bitRotate32($value, $amount){
if ($amount > 0){
$amount %= 32;
$value = ($value << $amount) | ($value >> (32 - $amount));
} elseif($amount < 0){
$amount = (-1 * $amount) % 32;
$value = ($value >> $amount) | ($value << (32 - $amount));
}
return $value;
}
?>
crcpng.php
php
<?php
$crc_table = array();

$crc_table_computed = false;

function make_crc_table(){
global $crc_table, $crc_table_computed;
for($n = 0; $n < 256; $n++){
$c = $n;
for($k = 0; $k < 8; $k++){
if($c & 1 === 1)
$c = 0xedb88320 ^ (($c >> 1) & 0xefffffff);
else
$c = ($c >> 1) & 0xefffffff;
}
$crc_table[] = $c;
}
$crc_table_computed = true;
}

function update_crc($crc, $buf){
global $crc_table, $crc_table_computed;
$c = $crc;

if(!$crc_table_computed)
make_crc_table();
$len = strlen($buf);
for($n = 0; $n < $len; $n++){
$c = $crc_table[($c ^ ord(substr($buf, $n, 1))) & 0x000000ff] ^ (($c >> 8) & 0x00ffffff);
}
return $c;
}

function crc($buf){
return update_crc(0xffffffff, $buf) ^ 0xffffffff;
}
?>
The output it produces on Opera is basically a small image that says DREAM.IN.CODE on it. I'm including a screencap. Thank you for any input you can provide.


Attached image(s)
Attached Image
User is offlineProfile CardPM

Go to the top of the page

mocker
post 16 Sep, 2008 - 03:25 PM
Post #2


D.I.C Regular

Group Icon
Joined: 14 Oct, 2007
Posts: 256



Thanked 15 times
My Contributions


I tried messing around with the php headers thinking maybe the browsers weren't interpreting it right, however IE, Firefox and Chrome all read it as an invalid PNG, so they are trying to parse it as a png and just failing. If I save the file to my computer, I can then open it up.

I would check the PNG format you are writing. I am guessing something in the header information in the png data does not match the standard the browsers are expecting. Windows and Opera are probably more lax about their interpretation of the file.
User is offlineProfile CardPM

Go to the top of the page

grimpirate
post 16 Sep, 2008 - 10:03 PM
Post #3


D.I.C Head

Group Icon
Joined: 3 Aug, 2006
Posts: 187



Thanked 5 times

Dream Kudos: 375
My Contributions


I figured out the problem. It was line 143 of the png.php file. The checksum that they perform can in fact be determined with the php function crc32. I though the implementation I had coded did the exact same thing as that listed in the specifications, but apparently it is erroneous. So basically the line SHOULD become the following:
CODE
$chunk['crc'] = writableInt(crc32($chunk['type'] . $chunk['data']));
This change eliminates the need for the crcpng.php file. Now your png images should open up anywhere Mocker. Only thing left now is to figure out how to code zlib. T_T
User is offlineProfile CardPM

Go to the top of the page

grimpirate
post 17 Sep, 2008 - 12:16 AM
Post #4


D.I.C Head

Group Icon
Joined: 3 Aug, 2006
Posts: 187



Thanked 5 times

Dream Kudos: 375
My Contributions


To all those that would defy me, BEHOLD MY GLORY!
php
<?php
define('DEF_BLK_SIZE', 65531);
define('ADLER_BASE', 65521);
header("Content-type: image/png");
$png = new PNG(69, 5);
$png->line(0, 0, 3, 0);
$png->line(6, 0, 9, 0);
$png->line(12, 0, 16, 0);
$png->line(19, 0, 21, 0);
$png->line(24, 0, 28, 0);
$png->line(32, 0, 36, 0);
$png->pixel(38, 0);
$png->pixel(42, 0);
$png->line(46, 0, 50, 0);
$png->line(52, 0, 56, 0);
$png->line(58, 0, 61, 0);
$png->line(64, 0, 68, 0);
$png->pixel(0, 1);
$png->pixel(4, 1);
$png->pixel(6, 1);
$png->pixel(10, 1);
$png->pixel(12, 1);
$png->pixel(18, 1);
$png->pixel(22, 1);
$png->pixel(24, 1);
$png->pixel(26, 1);
$png->pixel(28, 1);
$png->pixel(34, 1);
$png->pixel(38, 1);
$png->pixel(39, 1);
$png->pixel(42, 1);
$png->pixel(46, 1);
$png->pixel(52, 1);
$png->pixel(56, 1);
$png->pixel(58, 1);
$png->pixel(62, 1);
$png->pixel(64, 1);
$png->pixel(0, 2);
$png->pixel(4, 2);
$png->line(6, 2, 10, 2);
$png->line(12, 2, 15, 2);
$png->pixel(18, 2);
$png->pixel(22, 2);
$png->pixel(24, 2);
$png->pixel(26, 2);
$png->pixel(28, 2);
$png->pixel(34, 2);
$png->pixel(38, 2);
$png->pixel(40, 2);
$png->pixel(42, 2);
$png->pixel(46, 2);
$png->pixel(52, 2);
$png->pixel(56, 2);
$png->pixel(58, 2);
$png->pixel(62, 2);
$png->line(64, 2, 67, 2);
$png->pixel(0, 3);
$png->pixel(4, 3);
$png->pixel(6, 3);
$png->pixel(8, 3);
$png->pixel(12, 3);
$png->line(18, 3, 22, 3);
$png->pixel(24, 3);
$png->pixel(26, 3);
$png->pixel(28, 3);
$png->pixel(34, 3);
$png->pixel(38, 3);
$png->pixel(41, 3);
$png->pixel(42, 3);
$png->pixel(46, 3);
$png->pixel(52, 3);
$png->pixel(56, 3);
$png->pixel(58, 3);
$png->pixel(62, 3);
$png->pixel(64, 3);
$png->line(0, 4, 3, 4);
$png->pixel(6, 4);
$png->pixel(9, 4);
$png->pixel(10, 4);
$png->line(12, 4, 16, 4);
$png->pixel(18, 4);
$png->pixel(22, 4);
$png->pixel(24, 4);
$png->pixel(28, 4);
$png->pixel(30, 4);
$png->line(32, 4, 36, 4);
$png->pixel(38, 4);
$png->pixel(42, 4);
$png->pixel(44, 4);
$png->line(46, 4, 50, 4);
$png->line(52, 4, 56, 4);
$png->line(58, 4, 61, 4);
$png->line(64, 4, 68, 4);
echo $png->resultantImage();

class PNG {
var $signature; // 8 bytes
var $width; // 4 bytes
var $height; // 4 bytes
var $bit_depth; // 1 byte
var $colour_type; // 1 byte
var $compression_method; // 1 byte
var $filter_method; // 1 byte
var $interlace_method; // 1 byte
var $pixelData; // n bytes

function PNG($width, $height, $bit_depth = 8, $colour_type = 6, $compression_method = 0, $filter_method = 0, $interlace_method = 0){
$this->signature = chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10);
$this->width = $width;
$this->height = $height;
$this->bit_depth = $bit_depth;
$this->colour_type = $colour_type;
$this->compression_method = $compression_method;
$this->filter_method = $filter_method;
$this->interlace_method = $interlace_method;
for($i = 0; $i < $height; $i++){
$this->pixelData[] = array();
for($j = 0; $j < $width; $j++){
$this->pixelData[$i][] = chr(255) . chr(255) . chr(255) . chr(0);
}
}
}

function chunk($type){
$chunk = array(
'length' => null, // 4 bytes
'type' => null, // 4 bytes
'data' => null, // n bytes
'crc' => null // 4 bytes
);
if(!strcmp('IHDR', $type)){
$chunk['type'] = 'IHDR';
$chunk['data'] = writableInt($this->width) . writableInt($this->height) . chr($this->bit_depth) . chr($this->colour_type) . chr($this->compression_method) . chr($this->filter_method) . chr($this->interlace_method);
}elseif(!strcmp('IDAT', $type)){
$chunk['type'] = 'IDAT';
$scanlines = array_values($this->pixelData);
$scanlines = array_map('scanser', $scanlines);
//$scanlines = gzcompress(implode('', $scanlines));
$scanlines = defstr(implode('', $scanlines));
$chunk['data'] = $scanlines;
}elseif(!strcmp('IEND', $type)){
$chunk['type'] = 'IEND';
}
$chunk['length'] = writableInt(strlen($chunk['data']));
$chunk['crc'] = writableInt(crc32($chunk['type'] . $chunk['data']));
return implode('', $chunk);
}

function resultantImage(){
return $this->signature . $this->chunk('IHDR') . $this->chunk('IDAT') . $this->chunk('IEND');
}

function pixel($x, $y, $r = 0, $g = 0, $b = 0, $a = 255){
$this->pixelData[$y][$x] = chr($r) . chr($g) . chr($b) . chr($a);
}

/*
Makes use of the Bresenham line algorithm
http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
*/
function line($x0, $y0, $x1, $y1, $r = 0, $g = 0, $b = 0, $a = 255){
$steep = abs($y1 - $y0) > abs($x1 - $x0);
if($steep){
$temp = $x0;
$x0 = $y0;
$y0 = $temp;
$temp = $x1;
$x1 = $y1;
$y1 = $temp;
}
if($x0 > $x1){
$temp = $x0;
$x0 = $x1;
$x1 = $temp;
$temp = $y0;
$y0 = $y1;
$y1 = $temp;
}
$deltax = $x1 - $x0;
$deltay = abs($y1 - $y0);
$error = $deltax / 2;
$y = $y0;
if($y0 < $y1)
$ystep = 1;
else
$ystep = -1;
for($x = $x0; $x <= $x1; $x++){
if($steep)
$this->pixelData[$x][$y] = chr($r) . chr($g) . chr($b) . chr($a);
else
$this->pixelData[$y][$x] = chr($r) . chr($g) . chr($b) . chr($a);
$error = $error - $deltay;
if($error < 0){
$y += $ystep;
$error += $deltax;
}
}
}
}

function defstr($string){
$adler = writableInt(adler32($string));
$output = chr(120) . chr(1);
$len = strlen($string);
$div = floor($len / DEF_BLK_SIZE);
if($div != 0){
for($i = 0; $i < $div; $i++){
$output .= chr(0);
$output .= strrev(substr(writableInt(DEF_BLK_SIZE), 2));
$output .= strrev(substr(writableInt(DEF_BLK_SIZE ^ 0xffffffff), 2));
$output .= substr($string, 0, DEF_BLK_SIZE);
$string = substr($string, DEF_BLK_SIZE);
}
}
$len = strlen($string);
$output .= chr(1);
$output .= strrev(substr(writableInt($len), 2));
$output .= strrev(substr(writableInt($len ^ 0xffffffff), 2));
$output .= $string;
$output .= $adler;
return $output;
}
function adler32($buf){
return update_adler32(1, $buf);
}

function update_adler32($adler, $buf){
$s1 = $adler & 0x0000ffff;
$s2 = ($adler >> 16) & 0x0000ffff;
$len = strlen($buf);
for($n = 0; $n < $len; $n++){
$s1 = ($s1 + ord(substr($buf, $n, 1))) % ADLER_BASE;
$s2 = ($s2 + $s1) % ADLER_BASE;
}
return ($s2 << 16) + $s1;
}
function scanser($value){
return chr(0) . implode('', $value);
}

function writableInt($number){
$output = '';
for($i = 0; $i > -32; $i -= 8){
$output .= chr(bitRotate32($number, $i) & 0x000000ff);
}
return strrev($output);
}
// Negatives rotate right, positives rotate left
// Made by Nina Cording from the php.net site
function bitRotate32($value, $amount){
if ($amount > 0){
$amount %= 32;
$value = ($value << $amount) | ($value >> (32 - $amount));
} elseif($amount < 0){
$amount = (-1 * $amount) % 32;
$value = ($value >> $amount) | ($value << (32 - $amount));
}
return $value;
}

?>
DREAM.IN.CODE generated from the ground up without any outside libraries! BWAHAHAHA! NEXT THE WORLD!
User is offlineProfile CardPM

Go to the top of the page

grimpirate
post 17 Sep, 2008 - 06:55 PM
Post #5


D.I.C Head

Group Icon
Joined: 3 Aug, 2006
Posts: 187



Thanked 5 times

Dream Kudos: 375
My Contributions


As my specialty is not compression algorithms. I will likely be keeping this as is. Uncompressed true color with alpha images as the only applicable PNG setting. Currently, I'm working on trying to implement a Xiaolin Wu line drawing algorithm. Once I get that working I'll see about working out line thicknesses and polygon drawing and other such 2d graphical constructs. If anyone has any pointers or useful info to assist me with that I'd be appreciative.
User is offlineProfile CardPM

Go to the top of the page

Fast ReplyReply to this topicStart new topic
Time is now: 11/23/08 08:15AM

Live Help!

Tutorials

Programming

Web Development

Reference Sheets

Code Snippets

Bye Bye Ads

Free DIC T-Shirt

T-Shirt Example

Related Sites

Monthly Drawing

Thumb Drive

Partners

Top Contributors

Top 10 Kudos This Month