Related
I have set up a PHP WebSocket server that is able to read string data from clients. My question is on how to handle binary data types. Below is the code on the client side that records microphone input as a Float32Array object and sends the data over a WebSocket connection in binary.
websocket = new WebSocket("ws://...");
websocket.binaryType = "arraybuffer";
recorder.onaudioprocess = function(stream) {
var inputData = stream.inputBuffer.getChannelData(0);
websocket.send(inputData);
}
As for the server side, I am using the following functions which I've found online for encoding/unmasking.
function mask($text)
{
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if( $length <= 125)
$header = pack('CC', $b1, $length);
elseif ($length > 125 && $length < 65536)
$header = pack('CCS', $b1, 126, $length);
elseif ($length >= 65536)
$header = pack('CCN', $b1, 127, $length);
return $header.$text;
}
function unmask($payload)
{
$length = ord($payload[1]) & 127;
if($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
$len = (ord($payload[2]) << 8) + ord($payload[3]);
}
elseif($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
$len = (ord($payload[2]) << 56) + (ord($payload[3]) << 48) +
(ord($payload[4]) << 40) + (ord($payload[5]) << 32) +
(ord($payload[6]) << 24) +(ord($payload[7]) << 16) +
(ord($payload[8]) << 8) + ord($payload[9]);
}
else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
$len = $length;
}
$text = '';
for ($i = 0; $i < $len; ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
return $text;
}
The code works great except they only work on string data and not for binary type. My question is how do I handle binary type and push them to clients?
I don't really get what you are trying to do. WebRTC can be used to create Peer2Peer connections. Thus you would use your PHP Server as a signaling server. The actual data is not passed to your server unless it is needed for forwarding because a p2p connection could not be established. Is that what you want to do?
Otherwise create a RtcPeerConnection and send data from peer to peer as it is intended by webrtc.
I am working from this code and taking into account the comments to "fix" it for 32-bit, but it seems to still not work. I am pretty sure it has something to do with the TGA descriptor. This will have bits 0-3 as the alpha channel depth which will always be 8 for 32 bit, and the code doesn't account for that.
I tried to understand how to piece it together using this C code as a guide, but no luck.
It seems the once you take into account the pixel being of length of 4 (as per the patch in comments) his dwordize only accounts for 3 of the 4 bytes, the 4th byte being the alpha bits I think.
I tried changing the function from
function dwordize($str)
{
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
return $c*256*256 + $b*256 + $a;
}
to
function dwordize($str)
{
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
$d = ord($str[3]);
return $d*256*256*256 + $c*256*256 + $b*256 + $a;
}
which didn't work, and then tried
function dwordize($str)
{
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
$d = ord($str[3]);
return $c*256*256 + $b*256 + $a + $d*256*256*256;
}
All of which I am trying to go off the C code which goes from like RBGA to BGRA and then the indices are all weird. I really don't understand the C code enough to apply it to the PHP code.
I also found this site which may help, I am reading it over now and if I come up with anything I will update.
I have found your solution. There were missing adjustments that needed to be made in the RLE decode function to support the 32 bits pixel format: i.e: AARRGGBB
So first of all we need to bypass the color depth check of your library as suggested in the comments, so we change the check to this on line 99
if ($header['pixel_size'] != 24 && $header['pixel_size'] != 32) {
die('Unsupported TGA color depth');
}
Then we need to change some variables that are color depth dependent. Most of them will be used for data formatting so we need them to have the correct value regardless of the color depth.
So that would be:
line 104 : $bytes = $header['pixel_size'] / 8;
line 117 : $size = $header['width'] * $header['height'] * $bytes;
line 154 : $pixels = str_split($data, $bytes);
and remove the line 153: $num_bytes = $header['pixel_size']/8; We do not need it anymore.
Then we will need the rle_decode() function to know the pixel format size, so int the for loop when we call it we need to pass this parameter, so we need to change as well the following line:
line 141: $data = rle_decode($data, $size, $bytes);
Therefore the function prototypes changes into:
line 9: function rle_decode($data, $datalen, $pixel_size)
Now you can see on this function there are multiple 3 magic numbers (i.e: for ($j = 0; $j<3*$value; $j++) on line 29).
All of those must be replaced by the pixel size parameter. (3 for 24 bits and 4 for 32 bits)
Moreover sometimes it will write only 3 bytes instead of 4 when creating the pixels so we must adjust that as well.
So finally this gives us the following algorithm:
function rle_decode($data, $datalen, $pixel_size)
{
$len = strlen($data);
$out = '';
$i = 0;
$k = 0;
while ($i<$len)
{
dec_bits(ord($data[$i]), $type, $value);
if ($k >= $datalen)
{
break;
}
$i++;
if ($type == 0) //raw
{
for ($j=0; $j<$pixel_size*$value; $j++)
{
$out .= $data[$j+$i];
$k++;
}
$i += $value*$pixel_size;
}
else //rle
{
for ($j=0; $j<$value; $j++)
{
$out .= $data[$i] . $data[$i+1] . $data[$i+2];
if ($pixel_size == 4) $out .= $data[$i+3];
$k++;
}
$i += $pixel_size;
}
}
return $out;
}
That's it, you will now get your TGA images perfectly converted. I'm giving you the full tga.php code so you can directly paste it and test it by yourself:
<?php
// Author: de77
// Licence: MIT
// First-version: 9.02.2010
// Version: 24.08.2010
// http://de77.com
function rle_decode($data, $datalen, $pixel_size)
{
$len = strlen($data);
$out = '';
$i = 0;
$k = 0;
while ($i<$len)
{
dec_bits(ord($data[$i]), $type, $value);
if ($k >= $datalen)
{
break;
}
$i++;
if ($type == 0) //raw
{
for ($j=0; $j<$pixel_size*$value; $j++)
{
$out .= $data[$j+$i];
$k++;
}
$i += $value*$pixel_size;
}
else //rle
{
for ($j=0; $j<$value; $j++)
{
$out .= $data[$i] . $data[$i+1] . $data[$i+2];
if ($pixel_size == 4) $out .= $data[$i+3];
$k++;
}
$i += $pixel_size;
}
}
return $out;
}
function dec_bits($byte, &$type, &$value)
{
$type = ($byte & 0x80) >> 7;
$value = 1 + ($byte & 0x7F);
}
function getimagesizetga($filename)
{
$f = fopen($filename, 'rb');
$header = fread($f, 18);
$header = #unpack( "cimage_id_len/ccolor_map_type/cimage_type/vcolor_map_origin/vcolor_map_len/" .
"ccolor_map_entry_size/vx_origin/vy_origin/vwidth/vheight/" .
"cpixel_size/cdescriptor", $header);
fclose($f);
$types = array(0,1,2,3,9,10,11,32,33);
if (!in_array($header['image_type'], $types))
{
return array(0, 0, 0, 0, 0);
}
if ($header['pixel_size'] > 32)
{
return array(0, 0, 0, 0, 0);
}
return array($header['width'], $header['height'], 'tga', $header['pixel_size'], $header['image_type']);
}
function imagecreatefromtga($filename)
{
$f = fopen($filename, 'rb');
if (!$f)
{
return false;
}
$header = fread($f, 18);
$header = unpack( "cimage_id_len/ccolor_map_type/cimage_type/vcolor_map_origin/vcolor_map_len/" .
"ccolor_map_entry_size/vx_origin/vy_origin/vwidth/vheight/" .
"cpixel_size/cdescriptor", $header);
switch ($header['image_type'])
{
case 2: echo "Image is not compressed\n";//no palette, uncompressed
case 10: //no palette, rle
break;
default: die('Unsupported TGA format');
}
if ($header['pixel_size'] != 24 && $header['pixel_size'] != 32)
{
die('Unsupported TGA color depth');
}
$bytes = $header['pixel_size'] / 8;
if ($header['image_id_len'] > 0)
{
$header['image_id'] = fread($f, $header['image_id_len']);
}
else
{
$header['image_id'] = '';
}
$im = imagecreatetruecolor($header['width'], $header['height']);
$size = $header['width'] * $header['height'] * $bytes;
//-- check whether this is NEW TGA or not
$pos = ftell($f);
fseek($f, -26, SEEK_END);
$newtga = fread($f, 26);
if (substr($newtga, 8, 16) != 'TRUEVISION-XFILE')
{
$newtga = false;
}
fseek($f, 0, SEEK_END);
$datasize = ftell($f) - $pos;
if ($newtga)
{
$datasize -= 26;
}
fseek($f, $pos, SEEK_SET);
//-- end of check
$data = fread($f, $datasize);
if ($header['image_type'] == 10)
{
$data = rle_decode($data, $size, $bytes);
}
if (bit5($header['descriptor']) == 1)
{
$reverse = true;
}
else
{
$reverse = false;
}
$i = 0;
$pixels = str_split($data, $bytes);
//read pixels
if ($reverse)
{
for ($y=0; $y<$header['height']; $y++)
{
for ($x=0; $x<$header['width']; $x++)
{
imagesetpixel($im, $x, $y, dwordize($pixels[$i]));
$i++;
}
}
}
else
{
for ($y=$header['height']-1; $y>=0; $y--)
{
for ($x=0; $x<$header['width']; $x++)
{
imagesetpixel($im, $x, $y, dwordize($pixels[$i]));
$i++;
}
}
}
fclose($f);
return $im;
}
function dwordize($str)
{
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
return $c*256*256 + $b*256 + $a;
}
function bit5($x)
{
return ($x & 32) >> 5;
}
And here is the output PNG image directly created from the script:
And if you would prefer a direct download link rather than pasting the fixed code I give you here the full php file: https://www.sendspace.com/file/92uir9
At the end of the day, just change the tga.php file and everything will automagically work.
Based on the de77.com code, but with some additions:
<?php
// References:
// https://stackoverflow.com/questions/24709142/convert-32-bit-tga-to-png
// https://github.com/nothings/stb/blob/master/stb_image.h
// http://tfc.duke.free.fr/coding/tga_specs.pdf
// http://www.paulbourke.net/dataformats/tga/
//
// This class development started with the following code:
// http://de77.com/tga-imagetga-imagecreatefromtga
// Author: de77
// Licence: MIT
// First-version: 9.02.2010
// Version: 24.08.2010
// http://de77.com
//
// C.. oct 2022
// I put the code in a class,
// I added the color-mapped formats,
// I added the 15b & 16b pixel-size formats.
//
// The following is supported:
//
// Pixel-depths: 8b, 15b, 16b, 24b, 32b
//
// Image Type Description
// 0 No Image Data Included
// 1 Uncompressed, Color-mapped Image
// 2 Uncompressed, True-color Image
// 3 Uncompressed, Grayscale Image
// 9 Run-length encoded, Color-mapped Image
// 10 Run-length encoded, True-color Image
// 11 Run-length encoded, Grayscale Image
//
// NOTE: i commented out 16b-alpha code using: //!
// It does not work, and images come out all transparent.
class uje_tga_class {
private function rle_decode(&$data, $bytes) {
$out = '';
$i = 0;
$len = strlen($data);
while ($i<$len) {
$b = ord($data[$i]);
$type = ($b & 0x80);
$count = 1 + ($b & 0x7F);
$i++;
// raw or RLE
if ($type == 0) { //raw
$size = $bytes*$count;
$out .= substr($data, $i, $size);
$i += $size;
} else { //rle
$s = substr($data, $i, $bytes);
$out .= str_repeat($s, $count);
$i += $bytes;
}
}
return $out;
}
private function unmapcolors(&$data, $npixels, $colormapentrybytes, &$palette, $pixelbytes) {
$out = '';
for ($i=0, $i2=0; $i<$npixels; $i++, $i2+=$colormapentrybytes) {
$idx = ord($data[$i2]); // $colormapentrybytes == 1 or 2
if ($colormapentrybytes == 2) $idx += (ord($data[$i2+1]) << 8);
$idx *= $pixelbytes;
$out .= substr($palette, $idx, $pixelbytes);
}
return $out;
}
public function getimagesizetga($filename) {
$f = fopen($filename, 'rb');
$header = fread($f, 18);
$header = #unpack("cimage_id_len/ccolor_map_type/cimage_type/vcolor_map_origin/vcolor_map_len/" .
"ccolor_map_entry_size/vx_origin/vy_origin/vwidth/vheight/" .
"cpixel_size/cdescriptor", $header);
fclose($f);
$types = array(0,1,2,3,9,10,11,32,33);
if (!in_array($header['image_type'], $types)) return array(0, 0, 0, 0, 0);
if ($header['pixel_size'] > 32) return array(0, 0, 0, 0, 0);
return array($header['width'], $header['height'], 'tga', $header['pixel_size'], $header['image_type']);
}
public function imagecreatefromtga($filename) {
// open the TGA file for binary reading
$f = fopen($filename, 'rb');
if (!$f) return false;
// read the file
try {
// read the TGA header
$header = fread($f, 18);
$header = unpack("cimage_id_len/ccolor_map_type/cimage_type/" .
"vcolor_map_origin/vcolor_map_len/ccolor_map_entry_size/" .
"vx_origin/vy_origin/vwidth/vheight/" .
"cpixel_size/cdescriptor", $header);
// check for supported tga formats
switch ($header['image_type']) {
case 1: // color-mapped uncompressed
case 2: // truecolor uncompressed
case 3: // grayscale uncompressed
case 9: // color-mapped run-length encoded
case 10: // truecolor run-length encoded
case 11: // grayscale run-length encoded
break;
default:
throw new RuntimeException('Unsupported format: '. $header['image_type']);
}
$iscolormapped = ($header['image_type'] == 9 || $header['image_type'] == 1);
$isrle = ($header['image_type'] == 9 || $header['image_type'] == 10 || $header['image_type'] == 11);
// check for supported pixel sizes. ($header['pixel_size'])
// But if this is a colormapped image, that "pixel_size" is in fact the width of each entry in the colormap.
// For a colormapped image, the pixelsize is stored in $header['color_map_entry_size'].
if ($iscolormapped) {
if ($header['color_map_type'] == 0) {
throw new RuntimeException('Missing colormap');
}
$pixelbits = $header['color_map_entry_size'];
// the entries in the colormap can be 8 or 16 bit wide
$colormapentrybytes = $header['pixel_size']; // this is really the number of bits..
if (($colormapentrybytes != 8) && ($colormapentrybytes != 16)) {
throw new RuntimeException('Unsupported colormap entry bits: '. $colormapentrybytes);
}
$colormapentrybytes /= 8; // ..now it's bytes
} else {
$pixelbits = $header['pixel_size'];
}
switch ($pixelbits) {
case 8: // grayscale
$pixelbytes = 1;
break;
case 15: // truecolor 5b blue + 5b green + 5b red + 1b dummy
case 16: // truecolor 5b blue + 5b green + 5b red + 1b alpha
$pixelbytes = 2;
break;
case 24: // truecolor
$pixelbytes = 3;
break;
case 32: // truecolor
$pixelbytes = 4;
break;
default:
throw new RuntimeException('Unsupported pixel bits: '. $pixelbits);
}
// skip the image_id
$header['image_id'] = ($header['image_id_len'] > 0)? fread($f, $header['image_id_len']) : '';
// check whether this is a TGA v2.0
$tgav2 = true;
$pos = ftell($f); // store the current filepointer
fseek($f, -26, SEEK_END); // (possibly) read the file footer
$footer = fread($f, 26);
if (substr($footer, 8, 16) != 'TRUEVISION-XFILE') $tgav2 = false;
fseek($f, 0, SEEK_END);
$datasize = ftell($f) - $pos;
if ($tgav2) $datasize -= 26; // pixeldata does not include any footer
fseek($f, $pos, SEEK_SET); // restore filepointer
// if color-mapped then read the palette.
// The palette starts at the file-location where the image-data starts for the other formats.
// So first read the palette, and then correct the final datasize.
if ($iscolormapped) {
$palettesize = $header['color_map_len'] * $pixelbytes;
$pos = ftell($f) + $header['color_map_origin'];
fseek($f, $pos, SEEK_SET); // set filepointer to palette
$palette = fread($f, $palettesize);
$datasize -= $palettesize;
}
// Read the image data.
// If this is a colormapped image then this is not the pixeldata, but the indexes into the colormap.
$data = fread($f, $datasize);
} catch (Exception $e) {
//echo 'Exception: ', $e->getMessage(), "\n";
return false;
} finally {
fclose($f);
}
// get the pixeldata
if ($iscolormapped) {
$npixels = $header['width'] * $header['height'];
// colormapped images have the color-indexes encoded (pixeldata must be looked-up after RL decoding)
if ($isrle) $data = $this->rle_decode($data, $colormapentrybytes);
$pixeldata = $this->unmapcolors($data, $npixels, $colormapentrybytes, $palette, $pixelbytes);
} else
if ($isrle) { // possibly Run Length decode
$pixeldata = $this->rle_decode($data, $pixelbytes);
} else // uncompressed
$pixeldata = $data;
// create the image
$im = imagecreatetruecolor($header['width'], $header['height']);
// if the image has alpha data, then prepare for it
imagealphablending($im, false); // no blending. Just copy the pixel
//! if (!$iscolormapped && ($header['pixel_size'] == 32 || $header['pixel_size'] == 16)) {
if (!$iscolormapped && ($header['pixel_size'] == 32)) {
imagesavealpha($im, true); // be sure to save the alpha data
} else {
imagesavealpha($im, false);
}
// read pixel-ordering
$toptobottom = (($header['descriptor'] & 32) != 0);
$righttoleft = (($header['descriptor'] & 16) != 0);
// process the pixels
$i = 0;
for ($y=0; $y<$header['height']; $y++)
for ($x=0; $x<$header['width']; $x++) {
switch($pixelbytes) {
case 1:
$r = $g = $b = ord($pixeldata[$i]);
$col = imagecolorallocate($im, $r,$g,$b);
break;
case 2:
$r = (ord($pixeldata[$i+1]) & 0x7C) >> 2;
$g = ((ord($pixeldata[$i]) & 0xE0) >> 5) + ((ord($pixeldata[$i+1]) & 0x03) << 3);
$b = ord($pixeldata[$i]) & 0x1F;
$r <<= 3;
$g <<= 3;
$b <<= 3;
//! if ($header['pixel_size'] == 16) {
//! $a = ((ord($pixeldata[$i+1]) & 0x80) == 0)? 127 : 0; // 1b alpha means? transparent : opaque
//! $col = imagecolorallocatealpha($im, $r,$g,$b,$a);
//! } else {
$col = imagecolorallocate($im, $r,$g,$b);
//! }
break;
case 3:
$r = ord($pixeldata[$i+2]);
$g = ord($pixeldata[$i+1]);
$b = ord($pixeldata[$i]);
$col = imagecolorallocate($im, $r,$g,$b);
break;
case 4:
$r = ord($pixeldata[$i+2]);
$g = ord($pixeldata[$i+1]);
$b = ord($pixeldata[$i]);
$a = 127 - (ord($pixeldata[$i+3]) >> 1); // 128 alpha values with png transulency (where 127 = transparent, 0 = opaque)
$col = imagecolorallocatealpha($im, $r,$g,$b,$a);
break;
}
// set the pixel in the image
$xp = ($righttoleft)? $header['width'] - 1 - $x : $x;
$yp = ($toptobottom)? $y : $header['height'] - 1 - $y;
imagesetpixel($im, $xp, $yp, $col);
// next pixel in the pixeldata
$i += $pixelbytes;
}
return $im;
}
public function imagetga($im, $filename) {
list($width, $height) = array(imagesx($im), imagesy($im));
$tga = "\0\0\2\0\0\0\0\0\0\0\0\0" . pack('vv', $width, $height) . ' ';
for ($y=0; $y<$height; $y++)
for ($x=0; $x<$width; $x++) {
$rgb = imagecolorat($im, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$a = ($rgb >> 24) & 0x7F;
$tga .= chr($b).chr($g).chr($r).chr((127-$a)*2);
}
file_put_contents($filename, $tga);
}
public function tga2png($tga_filename, $png_filename) {
$im = $this->imagecreatefromtga($tga_filename);
if (!$im) return false;
//header('Content-Disposition: Attachment;filename=image.png');
//header("Content-type: image/png");
imagepng($im, $png_filename);
imagedestroy($im);
return true;
}
}
?>
Im using the the online code (https://github.com/hm2k/hm2k-php/blob/master/functions/imagecreatefrombmp.php) for converting "BMP" to "JPG"; I put in a separate file (in the includes library) as following:
class bmp2jpg {
/**
* #convert BMP to GD
* #param string $src
* #param string|bool $dest
* #return bool
*/
public static function bmp2gd($src, $dest = false) {
// open source file for reading
if (!($srch = fopen($src, 'rb'))) {
user_error('Unable to open source for reading.');
return false;
}
// open the destination file for writing
if (!($desth = fopen($dest, 'wb'))) {
user_error('Unable to open destination for writing.');
return false;
}
// get the headers
$header = unpack('vtype/Vsize/v2reserved/Voffset', fread($srch, 14));
// get the rest of the image
$info = unpack('Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vncolor/Vimportant', fread($srch, 40));
// extract the header and info into varibles
extract($info);
extract($header);
// check for BMP signature
if ($type != 0x4D42) {
user_error('Source image is not a BMP.');
return false;
}
// set the pallete
$palette_size = $offset - 54;
$ncolor = $palette_size / 4;
// true-color vs. palette
$gd_header = '';
$gd_header .= ($palette_size == 0) ? "\xFF\xFE" : "\xFF\xFF";
$gd_header .= pack('n2', $width, $height);
$gd_header .= ($palette_size == 0) ? "\x01" : "\x00";
if ($palette_size) {
$gd_header .= pack('n', $ncolor);
}
// do not allow transparency
$gd_header .= "\xFF\xFF\xFF\xFF";
// write the destination headers
fwrite($desth, $gd_header);
unset($gd_header);
// if we have a palette
if ($palette_size) {
// read the palette
$palette = fread($srch, $palette_size);
// begin the gd palette
$gd_palette = '';
$j = 0;
// loop of the palette
while ( $j < $palette_size ) {
$b = $palette{$j++};
$g = $palette{$j++};
$r = $palette{$j++};
$a = $palette{$j++};
// assemble the gd palette
$gd_palette .= $r . $g . $b . $a;
}
// finish the palette
$gd_palette .= str_repeat("\x00\x00\x00\x00", 256 - $ncolor);
// write the gd palette
fwrite($desth, $gd_palette);
unset($gd_palette);
}
// scan line size and alignment
$scan_line_size = (($bits * $width) + 7) >> 3;
$scan_line_align = ($scan_line_size & 0x03) ? 4 - ($scan_line_size & 0x03) : 0;
// main loop
for($i = 0, $l = $height - 1; $i < $height; $i++, $l--) {
// create scan lines starting from bottom
fseek($srch, $offset + (($scan_line_size + $scan_line_align) * $l));
$scan_line = fread($srch, $scan_line_size);
$gd_scan_line = '';
if ($bits == 24) {
$j = 0;
while ( $j < $scan_line_size ) {
$b = $scan_line{$j++};
$g = $scan_line{$j++};
$r = $scan_line{$j++};
$gd_scan_line .= "\x00" . $r . $g . $b;
}
}
else if ($bits == 8) {
$gd_scan_line = $scan_line;
}
else if ($bits == 4) {
$j = 0;
while ( $j < $scan_line_size ) {
$byte = ord($scan_line{$j++});
$p1 = chr($byte >> 4);
$p2 = chr($byte & 0x0F);
$gd_scan_line .= $p1 . $p2;
}
$gd_scan_line = substr($gd_scan_line, 0, $width);
}
else if ($bits == 1) {
$j = 0;
while ( $j < $scan_line_size ) {
$byte = ord($scan_line{$j++});
$p1 = chr((int)(($byte & 0x80) != 0));
$p2 = chr((int)(($byte & 0x40) != 0));
$p3 = chr((int)(($byte & 0x20) != 0));
$p4 = chr((int)(($byte & 0x10) != 0));
$p5 = chr((int)(($byte & 0x08) != 0));
$p6 = chr((int)(($byte & 0x04) != 0));
$p7 = chr((int)(($byte & 0x02) != 0));
$p8 = chr((int)(($byte & 0x01) != 0));
$gd_scan_line .= $p1 . $p2 . $p3 . $p4 . $p5 . $p6 . $p7 . $p8;
}
// put the gd scan lines together
$gd_scan_line = substr($gd_scan_line, 0, $width);
}
// write the gd scan lines
fwrite($desth, $gd_scan_line);
unset($gd_scan_line);
}
// close the source file
fclose($srch);
// close the destination file
fclose($desth);
// return destination file
return $dest;
}
/**
* #ceate a BMP image
* #param string $filename
* #return bin string on success
* #return bool false on failure
*/
public static function imagecreatefrombmp($filename) {
// create a temp file
$tmpfile = tempnam(sys_get_temp_dir(), 'gd');
// convert to gd
if (bmp2gd($filename, $tmpfile)) {
// create image resource
$img = imagecreatefromgd($tmpfile);
}
// remove temp file
#unlink($tmpfile);
return isset($img) ? $img : false;
}
}
and Im calling the function this way:
require_once ("..\includes\imagecreatefrombmp.php");
$GD_Employee = bmp2jpg::bmp2gd("..\img\Rep.bmp");
$dest=bmp2jpg::imagecreatefrombmp($GD_Employee);
but it gives me the following errors:
Warning: require_once(..\includes\imagecreatefrombmp.php): failed to open stream
Fatal error: require_once(): Failed opening required '..\includes\imagecreatefrombmp.php'
Could you please let me know why? and am I supposed to pass the image name like "..\img\Rep.bmp"
Thanks
Because it can't find imagecreatefrombmp.php ... check if it is in right directory, it counts from the first run php file.
I got this php code.. that creates a pie Chart
when you go to the site: http://localhost/social/Test.php?data=100*200*100
How do i get this image from the php file to my Xcode project?
When you enter the site the image will download
But when you enter the site on the iPhone the image will only show
<?php
$show_label = true; // true = show label, false = don't show label.
$show_percent = true; // true = show percentage, false = don't show percentage.
$show_text = true; // true = show text, false = don't show text.
$show_parts = false; // true = show parts, false = don't show parts.
$label_form = 'square'; // 'square' or 'round' label.
$width = 199;
$background_color = 'FFFFFF'; // background-color of the chart...
$text_color = '000000'; // text-color.
$colors = array('003366', 'CCD6E0', '7F99B2','F7EFC6', 'C6BE8C', 'CC6600','990000','520000','BFBFC1','808080'); // colors of the slices.
$shadow_height = 16; // Height on shadown.
$shadow_dark = true; // true = darker shadow, false = lighter shadow...
// DON'T CHANGE ANYTHING BELOW THIS LINE...
$data = $_GET["data"];
$label = $_GET["label"];
$height = $width/2;
$data = explode('*',$data);
if ($label != '') $label = explode('*',$label);
for ($i = 0; $i < count($label); $i++)
{
if ($data[$i]/array_sum($data) < 0.1) $number[$i] = ' '.number_format(($data[$i]/array_sum($data))*100,1,',','.').'%';
else $number[$i] = number_format(($data[$i]/array_sum($data))*100,1,',','.').'%';
if (strlen($label[$i]) > $text_length) $text_length = strlen($label[$i]);
}
if (is_array($label))
{
$antal_label = count($label);
$xtra = (5+15*$antal_label)-($height+ceil($shadow_height));
if ($xtra > 0) $xtra_height = (5+15*$antal_label)-($height+ceil($shadow_height));
$xtra_width = 5;
if ($show_label) $xtra_width += 20;
if ($show_percent) $xtra_width += 45;
if ($show_text) $xtra_width += $text_length*8;
if ($show_parts) $xtra_width += 35;
}
$img = ImageCreateTrueColor($width+$xtra_width, $height+ceil($shadow_height)+$xtra_height);
ImageFill($img, 0, 0, colorHex($img, $background_color));
foreach ($colors as $colorkode)
{
$fill_color[] = colorHex($img, $colorkode);
$shadow_color[] = colorHexshadow($img, $colorkode, $shadow_dark);
}
$label_place = 5;
if (is_array($label))
{
for ($i = 0; $i < count($label); $i++)
{
if ($label_form == 'round' && $show_label && $data[$i] > 0)
{
imagefilledellipse($img,$width+11,$label_place+5,10,10,colorHex($img, $colors[$i % count($colors)]));
imageellipse($img,$width+11,$label_place+5,10,10,colorHex($img, $text_color));
}
else if ($label_form == 'square' && $show_label && $data[$i] > 0)
{
imagefilledrectangle($img,$width+6,$label_place,$width+16,$label_place+10,colorHex($img, $colors[$i % count($colors)]));
imagerectangle($img,$width+6,$label_place,$width+16,$label_place+10,colorHex($img, $text_color));
}
if ($data[$i] > 0)
{
if ($show_percent) $label_output = $number[$i].' ';
if ($show_text) $label_output = $label_output.$label[$i].' ';
if ($show_parts) $label_output = $label_output.$data[$i];
imagestring($img,'2',$width+20,$label_place,$label_output,colorHex($img, $text_color));
$label_output = '';
$label_place = $label_place + 15;
}
}
}
$centerX = round($width/2);
$centerY = round($height/2);
$diameterX = $width-4;
$diameterY = $height-4;
$data_sum = array_sum($data);
$start = 270;
for ($i = 0; $i < count($data); $i++)
{
$value += $data[$i];
$end = ceil(($value/$data_sum)*360) + 270;
$slice[] = array($start, $end, $shadow_color[$value_counter % count($shadow_color)], $fill_color[$value_counter % count($fill_color)]);
$start = $end;
$value_counter++;
}
for ($i=$centerY+$shadow_height; $i>$centerY; $i--)
{
for ($j = 0; $j < count($slice); $j++)
{
if ($slice[$j][0] != $slice[$j][1]) ImageFilledArc($img, $centerX, $i, $diameterX, $diameterY, $slice[$j][0], $slice[$j][1], $slice[$j][2], IMG_ARC_PIE);
}
}
for ($j = 0; $j < count($slice); $j++)
{
if ($slice[$j][0] != $slice[$j][1]) ImageFilledArc($img, $centerX, $centerY, $diameterX, $diameterY, $slice[$j][0], $slice[$j][1], $slice[$j][3], IMG_ARC_PIE);
}
OutputImage($img);
ImageDestroy($img);
function colorHex($img, $HexColorString)
{
$R = hexdec(substr($HexColorString, 0, 2));
$G = hexdec(substr($HexColorString, 2, 2));
$B = hexdec(substr($HexColorString, 4, 2));
return ImageColorAllocate($img, $R, $G, $B);
}
function colorHexshadow($img, $HexColorString, $mork)
{
$R = hexdec(substr($HexColorString, 0, 2));
$G = hexdec(substr($HexColorString, 2, 2));
$B = hexdec(substr($HexColorString, 4, 2));
if ($mork)
{
($R > 99) ? $R -= 100 : $R = 0;
($G > 99) ? $G -= 100 : $G = 0;
($B > 99) ? $B -= 100 : $B = 0;
}
else
{
($R < 220) ? $R += 35 : $R = 255;
($G < 220) ? $G += 35 : $G = 255;
($B < 220) ? $B += 35 : $B = 255;
}
return ImageColorAllocate($img, $R, $G, $B);
}
function OutputImage($img)
{
header('Content-type: image/jpg');
ImageJPEG($img,NULL,100);
}
?>
please do a post request form php server to your app, convert the image to base64 encoding and pass this as a string, then save this file as .jpg or .png etc as required.
Thanks
djrecker
Retrieve the image data via dataWithContentsOfUrl and then either
Use imageWithData to get your UIImage; or
Write the NSData to a file with writeToFile and then you can later retrieve the image via imageWithContentsOfFile
Note, you'll have to put that PHP code on a web server accessible by the iPhone (as obviously localhost won't work). So, it might look like (I've replaced your localhost reference with host.com ... you'll have to replace that with the URL of the web site you put your PHP code on):
NSString *urlString = #"http://host.com/social/Test.php?data=100*200*100";
NSURL *url = [NSURL URLWithString:urlString];
NSError *err;
NSData *imageData = [NSData dataWithContentsOfURL:url options:0 error:&err];
NSAssert(imageData, #"Unable to retrieve image");
Now you can use that image:
self.imageView.image = [UIImage imageWithData:imageData];
Or, alternatively, now you can write it to a file on your iPhone, too, if you want:
NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *documentFullPath = [documentsFolder stringByAppendingPathComponent:#"pie.jpg"]; // I noticed from the php source that this is a jpg
BOOL success = [imageData writeToFile:documentFullPath options:0 error:&err];
NSAssert(success, #"Unable to write file");
Which you can retrieve later:
self.imageView.image = [UIImage imageWithContentsOfFile:documentFullPath];
Note, this is an inefficient way to draw a pie chart in iOS (to have php server generate jpg that you then download, requiring Internet connection and download at 3G speeds if you're not on wifi, etc.). I'd probably do something with Core Graphics or grab one of the various charting/graphing APIs out there. If you google "ios 3d pie chart" you'll get a bunch of hits. This one looks pretty cool, though I've never tried it.
I'm writing some app based on websockets (RFC 6455). Unfortunetly it looks like the client (testing on Chrome 18) doesn't receive data, but the server says it is sending...
Chrome doesn't say anything
Here are main server methods:
private function decode($payload) {
$length = ord($payload[1]) & 127;
if ($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
} elseif ($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
} else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
}
$text = '';
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
$text = base64_decode($text);
return $text;
}
private function encode($text) {
$text = base64_encode($text);
// 0x1 text frame (FIN + opcode)
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if ($length <= 125)
$header = pack('CC', $b1, $length);
elseif ($length > 125 && $length < 65536)
$header = pack('CCS', $b1, 126, $length);
else
$header = pack('CCN', $b1, 127, $length);
return $header . $text;
}
protected function process($user, $msg) {
echo '<< '.$msg.N;
if (empty($msg)) {
$this->send($user->socket, $msg);
return;
}
}
protected function send($client, $msg) {
$msg = $this->encode($msg);
echo '>> '.$msg.N;
socket_write($client, $msg, strlen($msg));
}
If you're sending a test message >125 bytes but <65536, your problem might be caused by a faulty format string to pack. I think this one should be 'CCn' (your current code writes the 2 bytes of the length in the wrong order).
If that doesn't help, you could try some client-side logging:
Does the onopen callback run to prove that the initial handshake completed successfully?
Do the onerror or onclose callbacks run, either after connection or after your server sends its message?