Skip to content
WJunction - Webmaster Forum

Random idea -> Data to Images

Status
Not open for further replies.

95 comments

Every descent download manager has that DeL XD. But tbh I don't think this is something you should worry about elio. If this would become a real problem for IC I'd be happy to show you how you can detect them and remove them via a cron job or something.
 
Lol ok then.

Here's what I have so far for the encoder part:
Code:
// DaToPic - Data To Picture Transcoder
// =============================================

#light

open System
open System.Drawing
open System.Drawing.Imaging
open System.IO
open System.Text
open System.Threading
open System.Threading.Tasks
open System.Windows.Forms

// =============================================
// =============================================

let MaxFileNameSize = 0xff // 255
let FileVersion = 0x01 // 1
let DefaultExt = ".png"
let mutable DefaultImgRes = new Point(1600, 1600)
let HandShake = Color.FromArgb(0xff, 0x04, 0x09, 0x56)

type AlphaFlags =
    static member ALL = 0xff // Read all
    static member END = 0x00 // Read nothing
    static member R   = 0x11 // Read red
    static member RG  = 0x22 // Read red, green

// =============================================

(* Calculate the amount of bytes the can be stored in an image of the specified resolution *)
let CalcImgBytes x y mult =
    (x * y) * mult


(* Calculate the amount of images that will be needed to store the data *)
let CalcNumOfImgsRequired (dataBytes:int) (imgBytes:int) =
    let headerSize = 16 + MaxFileNameSize + 1
    let amount = (float dataBytes) / (float imgBytes) |> ceil |> int

    if ((float (dataBytes + (amount * headerSize))) / (float imgBytes) |> ceil |> int > amount) then amount + 1
    else amount


(* Break an sequence into a sequence of array's *)
let Break n (s:seq<_>) = seq {
    use e = s.GetEnumerator()
    while e.MoveNext() do
        let i = ref 0
        yield [|
            yield e.Current
            i := !i + 1
            while !i < n && e.MoveNext() do            
                yield e.Current
                i := !i + 1 |]
    }


(* Generates the header data that contains the needed info *)
let CreateHeader part totalParts fileVersion (fileName:string) =
    let GetColor (b:byte[]) =
        match b.Length with
        | 0 -> Color.FromArgb(0x00, 0x00, 0x00, 0x00)
        | 1 -> Color.FromArgb(int b.[0], 0x00, 0x00, 0x00)
        | 2 -> Color.FromArgb(int b.[0], int b.[1], 0x00, 0x00)
        | 3 -> Color.FromArgb(int b.[0], int b.[1], int b.[2], 0x00)
        | _ -> Color.FromArgb(int b.[0], int b.[1], int b.[2], int b.[3])

    if (fileName.Length > MaxFileNameSize) then
        let msg = "File name length must be " + string MaxFileNameSize + " or smaller and bigger than 0"
        let fn = "fileName"
        raise(new ArgumentOutOfRangeException(fn, fileName.Length, msg))
    else
        [| HandShake; HandShake; HandShake |]
        |> fun colors -> Array.append colors [|Color.FromArgb(MaxFileNameSize, part, totalParts, fileVersion)|]
        |> fun colors -> (colors, Break 4 (Encoding.UTF8.GetBytes(fileName) |> Seq.ofArray))
        |> fun (colors, bytes) -> (colors, [for item in bytes -> GetColor item])
        |> fun (colors, colors') -> Array.append colors (Array.ofList colors')


(* Generates a 'usable' filename *)
let DoCorrectFileName (fileInfo:FileInfo) =
    if (fileInfo.Name.Length > MaxFileNameSize) then
        if (fileInfo.Name.Contains(".")) then
            fileInfo.Extension.ToCharArray()
            |> fun chars -> Array.append chars (fileInfo.Name.ToCharArray(0, MaxFileNameSize - (fileInfo.Extension.Length - 1)))
            |> fun chars -> new String(chars)
        else
            fileInfo.Name.ToCharArray(0, MaxFileNameSize)
            |> fun chars -> new String(chars)
    else
        fileInfo.Name.ToCharArray()
        |> fun chars -> Array.append chars [|for i in 0 .. MaxFileNameSize - chars.Length - 1 -> '\x00'|]
        |> fun chars -> new String(chars)


(* Creates ready to use data out of which we can generate our images *)
let CreateImages file targetDirectory =
    let fileInfo = new FileInfo(file)
    let fileName = DoCorrectFileName fileInfo
    let numOfImgs = CalcNumOfImgsRequired (int fileInfo.Length)  (CalcImgBytes DefaultImgRes.X DefaultImgRes.Y 3)

    [for i in [ 0 .. numOfImgs - 1 ] -> async {
        let header = CreateHeader (i+1) numOfImgs FileVersion fileName
        use bmp = new Bitmap(DefaultImgRes.X, DefaultImgRes.Y, PixelFormat.Format32bppArgb)
        use fs = fileInfo.OpenRead()

        let GetX y = if (y = 0) then header.Length - 1 else 0

        // Set file stream offset
        fs.Position <- int64 ((((bmp.Width * bmp.Height) - header.Length) * 3) * i)

        // Write header
        for x in [ 0 .. header.Length - 1 ] do bmp.SetPixel(x, 0, header.[x])

        // Bytes to pixels
        for y in [ 0 .. bmp.Height - 1 ] do
            for x in [ GetX y .. bmp.Width - 1 ] do
                match ((fs.Length - 1L) - fs.Position) with
                | 0L -> bmp.SetPixel(x, y, Color.FromArgb(AlphaFlags.END, 0x00, 0x00, 0x00))
                | 1L -> bmp.SetPixel(x, y, Color.FromArgb(AlphaFlags.R, fs.ReadByte(), 0x00, 0x00))
                | 2L -> bmp.SetPixel(x, y, Color.FromArgb(AlphaFlags.RG, fs.ReadByte(), fs.ReadByte(), 0x00))
                | _ -> bmp.SetPixel(x, y, Color.FromArgb(AlphaFlags.ALL, fs.ReadByte(), fs.ReadByte(), fs.ReadByte()))

        let savePath = (*targetDirectory + "\\" +*) DateTime.Now.Ticks.ToString("x") + DefaultExt
        bmp.Save(savePath)

        return savePath}]
    |> Async.Parallel
    |> Async.RunSynchronously


(* Creates ready to use data out of which we can generate our images *)
let CreateImages2 file targetDirectory =
    let fileInfo = new FileInfo(file)
    let fileName = DoCorrectFileName fileInfo
    let numOfImgs = CalcNumOfImgsRequired (int fileInfo.Length)  (CalcImgBytes DefaultImgRes.X DefaultImgRes.Y 3)
    let headerLength = (CreateHeader 1 numOfImgs FileVersion fileName).Length
    use fs = fileInfo.OpenRead()
    let buffers = File.ReadAllBytes(file)
                  |> Seq.ofArray
                  |> Break (((DefaultImgRes.X * DefaultImgRes.Y) - headerLength) * 3)
                  |> Array.ofSeq
                  |> fun b -> [for i in 0 .. numOfImgs - 1 -> async{return (b.[i], CreateHeader (i + 1) numOfImgs FileVersion fileName)}]
                  |> Async.Parallel
                  |> Async.RunSynchronously
    
    [for b, h in buffers -> async {
        use bmp = new Bitmap(DefaultImgRes.X, DefaultImgRes.Y, PixelFormat.Format32bppArgb)

        // implementation....

        let savePath = (*targetDirectory + "\\" +*) DateTime.Now.Ticks.ToString("x") + DefaultExt
        //bmp.Save(savePath)

        //[ savePath ]
        //|> (fun s -> List.append s savedFiles)
        //|> loop (i + 1)
        return savePath}]
    |> Async.Parallel
    |> Async.RunSynchronously


(* Creates ready to use data out of which we can generate our images *)
let CreateImages3 file targetDirectory = 
    let fileInfo = new FileInfo(file)
    let fileName = DoCorrectFileName fileInfo
    let numOfImgs = CalcNumOfImgsRequired (int fileInfo.Length)  (CalcImgBytes DefaultImgRes.X DefaultImgRes.Y 3)
    
    Parallel.For(0, numOfImgs - 1, (fun i ->
        let header = CreateHeader (i+1) numOfImgs FileVersion fileName
        use bmp = new Bitmap(DefaultImgRes.X, DefaultImgRes.Y, PixelFormat.Format32bppArgb)
        use g = Graphics.FromImage(bmp)
        use fs = fileInfo.OpenRead()

        let GetX y = if (y = 0) then header.Length - 1 else 0

        fs.Position <- int64 ((((bmp.Width * bmp.Height) - header.Length) * 3) * i)

        // Write header
        for x in [ 0 .. header.Length - 1 ] do bmp.SetPixel(x, 0, header.[x])

        //printfn "Processing Image %i of %i" (i + 1) numOfImgs

        for y in [ 0 .. DefaultImgRes.Y - 1 ] do
            for x in [ 0 .. DefaultImgRes.X - 1 ] do
                match ((fs.Length - 1L) - fs.Position) with
                | 0L -> g.DrawRectangle(new Pen(Color.FromArgb(AlphaFlags.END, 0x00, 0x00, 0x00)), x, y, 1, 1)
                | 1L -> g.DrawRectangle(new Pen(Color.FromArgb(AlphaFlags.R, fs.ReadByte(), 0x00, 0x00)), x, y, 1, 1)
                | 2L -> g.DrawRectangle(new Pen(Color.FromArgb(AlphaFlags.RG, fs.ReadByte(), fs.ReadByte(), 0x00)), x, y, 1, 1)
                | _ -> g.DrawRectangle(new Pen(Color.FromArgb(AlphaFlags.ALL, fs.ReadByte(), fs.ReadByte(), fs.ReadByte())), x, y, 1, 1)

        let savePath = (*targetDirectory + @"\" +*) DateTime.Now.Ticks.ToString("x") + DefaultExt
        
        bmp.Save(savePath)
    ))
CreateImages3() is the synchronous version, CreateImages2() is the multi core version with a RAM buffer, CreateImages() the multi core version without a RAM buffer.

All of them are slow, GDI(+) being the major bottleneck here. If someone knows of a good image manipulation library I'd like to hear it because GDI will be to slow for anyone.

I could use DirectDraw, Direct2D, DirectCompute 10 or OpenCL but all of those require to present on the client system so an alternative would be nice.

See now this is why a coding section would be handy on WJ
 
Finally cracked it, the fasted encoding speed the above methods gave me was 650 Kbyte/sec. I switched to FastPixel for the drawing and added functions to FastPixel to be able to draw pixels via bytes instead of passing Color objects. Also changed to staged caching. New encoding speed: 2-3 Mbyte/sec. That is faster than the speed I get in the winrar bechmark so should be a fast enough starting point for peoples.

The magic, for those interested:
Code:
let CreateImages file =
    let fileInfo = new FileInfo(file)
    let fileName = DoCorrectFileName fileInfo
    let numOfImgs = CalcNumOfImgsRequired (int fileInfo.Length)  (CalcImgBytes DefaultImgRes.X DefaultImgRes.Y 3)

    [for i in [ 0 .. numOfImgs - 1 ] -> async {
        let header = CreateHeader (i+1) numOfImgs FileVersion fileName
        use fs = fileInfo.OpenRead()
        use bmp = new Bitmap(DefaultImgRes.X, DefaultImgRes.Y, PixelFormat.Format32bppArgb)
        let fpx = new FastPixel(bmp)

        fs.Position <- int64 ((((DefaultImgRes.X * DefaultImgRes.Y) - header.Length) * 3) * i)

        let length = ((DefaultImgRes.X * DefaultImgRes.Y) - header.Length) * 3

        let count = ref 0
        let buffer' = ref (Array.create (length + 1) 0x00uy)
        let GetX y = if (y = 0) then header.Length - 1 else 0
        let realLength = fs.Read(!buffer', 0, (!buffer').Length)
        let buffer = (!buffer').[0 .. realLength - 1]
                     |> Seq.ofArray
                     |> Break 3
                     |> Array.ofSeq

        // Set file stream offset and free temp buffer
        buffer' := Array.empty<byte>
        fpx.Lock()

        // Write header
        for x in [ 0 .. header.Length - 1 ] do fpx.SetPixel(x, 0, header.[x])
        
        // Bytes to pixels
        for y in [ 0 .. DefaultImgRes.Y - 1 ] do
            for x in [ GetX y .. DefaultImgRes.X - 1 ] do
                if (!count < buffer.Length) then
                    match (buffer.[!count].Length) with
                        | 3 -> fpx.SetBytes(x, y, byte AlphaFlags.ALL, buffer.[!count].[0], buffer.[!count].[1], buffer.[!count].[2])
                        | 2 -> fpx.SetBytes(x, y, byte AlphaFlags.RG, buffer.[!count].[0], buffer.[!count].[1], 0x00uy)
                        | 1 -> fpx.SetBytes(x, y, byte AlphaFlags.R, buffer.[!count].[0], 0x00uy, 0x00uy)
                        | _ -> fpx.SetBytes(x, y, byte AlphaFlags.END, 0x00uy, 0x00uy, 0x00uy)

                    count := !count + 1
                else
                    fpx.SetBytes(x, y, byte AlphaFlags.END, 0x00uy, 0x00uy, 0x00uy)

        fpx.Unlock(true)

        return bmp}]
    |> Async.Parallel
    |> Async.RunSynchronously
Anyway, I wanted to ask if some would be willing to run a test program that will bechmark encoding speed? It would require you to download .NET 4 Beta 2 though.
 
That is pretty slow for just passing bytes. We'll talk again about speeds when you have a working transcoder ^^ (working as being able to split data across multiple images, one that knows when it's decoding a data image instead of any image, and one that doesn't corrupt data in the whole process). You'll be lucky to get 100 kbyte/sec :p.
 
Already posted go back a couple pages.

Ive tested on PHP6 :P

PHP:
<?
    $im = new ImageContainer('file.txt','image');
    $im->CreateSplit(1024*50);//50kb split.
    /*
    Note: this is filesized splits not the output image size, the output size will depend on compression, if its text you can expect a output size of roughly 20% of the file
    else if its actually binary you can expect around 20% overhead.
    */

    $im2 = new ImageContainerExtract('image0.png');
    $im2->Extract_File('php://output');
    error_reporting(E_ALL);

    class ImageContainer {
        private $file = '';
        private $number_total = 0;
        private $out = '';

        function ImageContainer($file,$out){
            $this->file = $file;
            if(!file_exists($file)){
                throw new Exception('File does not exist');
            }
            $this->out = $out;
        }

        function CreateSplit($split){
            $fs = filesize($this->file);
            if($split>$fs){
                $split = $fs;
            }
            $this->number_total = ceil($fs/$split);//Total number of splits
            $fp = fopen($this->file,'rb');
            $i = 0;
            while($fs>0){
                $h = $w = floor(sqrt($split));
                $h += ceil((sqrt($split)-$w)/$w);
                $fs -= $h*$w;
                $this->file2image($i,$fp,$this->out.$i.'.png',$w,$h,$split);
                $i++;
            }
            fclose($fp);
        }

        function file2image($split_no,$fp,$image,$w,$h,$split){
            $header = '[I]'.dechex($split_no).'.'.dechex($this->number_total).'.'.base64_encode(basename($this->file)).'|';
            if(strlen($header)>$w*$h-$split){
                $h++;
            }

            $gd = imagecreate($w,$h);

            $color = array();
            for($i=0;$i<=255;$i++){
                $color[$i] = imagecolorallocate($gd,$i,$i,$i);
            }
            $wc = $hc =0;        

            for($hc=0;$hc<$h;$hc++){
                for($wc=0;$wc<$w;$wc++){
                    $r = '';
                    if($header) {
                        $r=$header{0};
                        $header = substr($header,1);
                    }else $r = fread($fp,1);
                    imagesetpixel($gd,$wc,$hc,$color[ord($r)]);
                }
            }
            imagepng($gd,$image,9,PNG_NO_FILTER);

            imagedestroy($gd);
        }
    }

    class ImageContainerExtract {
        private $file = '';
        private $efile = '';
        private $data = array();
        private $parts = 1;

        function ImageContainerExtract($file){
            $this->file = $file;
            $this->data[0] = $this->decode_image_raw($file);
            $part_number = $this->ReadHeader(0,true);
            if($part_number!==0){
                throw new Exception('Not first part.');
            }
            for($i=1;$i<$this->parts;$i++){
                $filen = str_replace('0.png',$i.'.png',$file);
                $this->data[$i] = $this->decode_image_raw($filen);
            }
        }

        function ReadHeader($index,$strip=true){
            $mark = substr($this->data[$index],0,3);
            switch($mark){
                case '[I]':
                    $endof = strpos($this->data[$index],'|');
                    $header = substr($this->data[$index],0,$endof);
                    if($strip){
                        $this->data[$index] = substr($this->data[$index],$endof+1);
                    }
                    list($part_number,$this->parts,$this->efile) = explode('.',$header);
                    $part_number = hexdec($part_number);
                    $this->parts = hexdec($this->parts);
                    $this->efile = base64_decode($this->efile);
                    return $part_number;
                    break;
            }
        }

        function decode_image_raw($image){
            $ret = '';
            $gd = imagecreatefrompng($image);
            list($w,$h) = getimagesize($image);
            for($ih=0;$ih<$h;$ih++){
                for($iw=0;$iw<$w;$iw++){
                    $rgb = imagecolorat($gd,$iw,$ih);
                    $ret .= chr($rgb);
                }
            }
            imagedestroy($gd);
            return $ret;
        }

        function Extract_File(){
            $fp = fopen($this->efile,'wb');
            foreach($this->data as $d){
                fwrite($fp,$d);
            }
            fclose($fp);
        }
    }
?>
 
Time to bench it. I forgot how to measure time difference in php though.

Something a la:
Code:
let l = (float (new FileInfo(f)).Length) / float 1024
let mb = (float (new FileInfo(f)).Length) / float 1024 / float 1024
let s = DateTime.Now

CreateImages f t
|> ignore

let e = DateTime.Now

printfn "Operation took %s seconds @ %s KB/s (%s MB)!" (e.Subtract(s).TotalSeconds.ToString("n")) ((l / e.Subtract(s).TotalSeconds).ToString("n")) (mb.ToString("n"))

Help ^^
 
Windows clock is how I did it.

Also btw disable compression (change imagepng(...,...,9) to imagepng(...,...,0)) as I did for my test.

Howeaver microtime() or time() will do it.

Just a warning, it will depend greatly on the amount of ram and alot of other independant variables.

Also larger files are faster per mb, due to the memory allocation overheads in php.
 
Mkay. I forgot to change the file size because I read your reply after first trying to test it. But the difference in results is big enough to say that upping the file size would have made no real difference. If you want I can send you the exe to test yourself if you want.

Spec: Phenom II x4 955BE @ stock (3.2ghz) / 4GB RAM DDR2 800
PHP: 5.2.9
.NET: 4 Beta 2 using F# Beta

Results:
[slide]http://www.cubeupload.com/files/677c00bench.png[/slide]

May that case be settled :wub:
 
lol well I used a file generated from str_repeat = exactly 50mb

Using time() and no compression.
5 seconds

Using time() and compression
8 seconds

script (compression):
PHP:
<?
ini_set('memory_limit','2G');
set_time_limit(0);
file_put_contents('file.txt',str_repeat('-',1024*1024*50));
$start = time();
    $im = new ImageContainer('file.txt','image');
    $im->CreateSplit(1024*1024*1024);//1gb split.
    /*
    Note: this is filesized splits not the output image size, the output size will depend on compression, if its text you can expect a output size of roughly 20% of the file
    else if its actually binary you can expect around 20% overhead.
    */
die('Time (sec): '.(time()-$start));
    $im2 = new ImageContainerExtract('image0.png');
    $im2->Extract_File('php://output');
    error_reporting(E_ALL);

    class ImageContainer {
        private $file = '';
        private $number_total = 0;
        private $out = '';

        function ImageContainer($file,$out){
            $this->file = $file;
            if(!file_exists($file)){
                throw new Exception('File does not exist');
            }
            $this->out = $out;
        }

        function CreateSplit($split){
            $fs = filesize($this->file);
            if($split>$fs){
                $split = $fs;
            }
            $this->number_total = ceil($fs/$split);//Total number of splits
            $fp = fopen($this->file,'rb');
            $i = 0;
            while($fs>0){
                $h = $w = floor(sqrt($split));
                $h += ceil((sqrt($split)-$w)/$w);
                $fs -= $h*$w;
                $this->file2image($i,$fp,$this->out.$i.'.png',$w,$h,$split);
                $i++;
            }
            fclose($fp);
        }

        function file2image($split_no,$fp,$image,$w,$h,$split){
            $header = '[I]'.dechex($split_no).'.'.dechex($this->number_total).'.'.base64_encode(basename($this->file)).'|';
            if(strlen($header)>$w*$h-$split){
                $h++;
            }

            $gd = imagecreate($w,$h);

            $color = array();
            for($i=0;$i<=255;$i++){
                $color[$i] = imagecolorallocate($gd,$i,$i,$i);
            }
            $wc = $hc =0;        

            for($hc=0;$hc<$h;$hc++){
                for($wc=0;$wc<$w;$wc++){
                    $r = '';
                    if($header) {
                        $r=$header{0};
                        $header = substr($header,1);
                    }else $r = fread($fp,1);
                    imagesetpixel($gd,$wc,$hc,$color[ord($r)]);
                }
            }
            imagepng($gd,$image,9,PNG_NO_FILTER);

            imagedestroy($gd);
        }
    }

    class ImageContainerExtract {
        private $file = '';
        private $efile = '';
        private $data = array();
        private $parts = 1;

        function ImageContainerExtract($file){
            $this->file = $file;
            $this->data[0] = $this->decode_image_raw($file);
            $part_number = $this->ReadHeader(0,true);
            if($part_number!==0){
                throw new Exception('Not first part.');
            }
            for($i=1;$i<$this->parts;$i++){
                $filen = str_replace('0.png',$i.'.png',$file);
                $this->data[$i] = $this->decode_image_raw($filen);
            }
        }

        function ReadHeader($index,$strip=true){
            $mark = substr($this->data[$index],0,3);
            switch($mark){
                case '[I]':
                    $endof = strpos($this->data[$index],'|');
                    $header = substr($this->data[$index],0,$endof);
                    if($strip){
                        $this->data[$index] = substr($this->data[$index],$endof+1);
                    }
                    list($part_number,$this->parts,$this->efile) = explode('.',$header);
                    $part_number = hexdec($part_number);
                    $this->parts = hexdec($this->parts);
                    $this->efile = base64_decode($this->efile);
                    return $part_number;
                    break;
            }
        }

        function decode_image_raw($image){
            $ret = '';
            $gd = imagecreatefrompng($image);
            list($w,$h) = getimagesize($image);
            for($ih=0;$ih<$h;$ih++){
                for($iw=0;$iw<$w;$iw++){
                    $rgb = imagecolorat($gd,$iw,$ih);
                    $ret .= chr($rgb);
                }
            }
            imagedestroy($gd);
            return $ret;
        }

        function Extract_File(){
            $fp = fopen($this->efile,'wb');
            foreach($this->data as $d){
                fwrite($fp,$d);
            }
            fclose($fp);
        }
    }
?>
Split: 1gb which is greater than file size. I hope you did benchmark the 100mb file with 50kb splits (the code I demo'ed to begin with)

Specs: Dell Studio 15
Ram: 3gb
OS: Vista 32bit
Processor 2.1ghz

Owned.
 
Copy/pasted your code and hit refresh.

[slide]http://www.cubeupload.com/files/6a3a00bench.png[/slide]

I might add that this kind of synthetic test is useless, mainly because image hosts have file or resolution restrictions.
 
Not sure whats so different between our 2 pc's. Maybe its the 1066mhz ram or the intel processor IDK. Doesnt even seem to use full cpu usage on your pc either strange.

I did the test again.



And i fixed a discrepency between the posted code and the code I have on my PC on the posted version.
 
I ran the script a second time and removed the line that creates the text file, now the cpu usage doubled to 50% spread over 4 cores nicely. Thinking I was gonna see a much lower time I got 194 secs, even higher. It's odd, but realistic for being php. anything below 10 seconds is a bad result for those sizes. Drawing to images ain't cheap on the CPU and PHP was just not made nor optimized for this kind of work.

Edit: Mind downloading .net 4 and running my test? I'm wondering if that would result in strange times to.
 
lol nah I dont want to install 4 till its stable this is my work/class laptop I cant afford to break anything on it.

BTW ran it again and got 9 seconds, and the output is correct, decodes correctly. Anyway might even be different gd versions or anything.
 
I'm sure it has nothing to do with ram speed or file versions, the difference is to big. The only thing that could explain this away is some extremely effective catching but then your 10 seconds would be to big.

But when I look at my 1st mockup it did a 191MB file (zip) in 277 seconds. Your code isn't optimized so the results it gives on my PC are actually rather accurate when compared to my 1st test, yours being a bit slower (again what would be expected of php). There is nothing running on my PC that could be bottle-necking the tests either.

To give an insight in why I believe the results from your script on my pc can be trusted compared to my last test (33sec/2.1MBs/70mb file) I'll explain the method:

First thing I did was switch to FastPixel. What this does is direct I/O with the in-memory byte array that holds the color data of a Bitmap object. That method alone will vastly outperform any drawing via php. Because the overhead is next to none and it doesn't cause the kernel to start using half of cpu time. Next thing I did was optimize its get/setpixel methods so that it no longer requires a color to be passed allowing me to write directly from the data buffer to the bitmap buffer -> byte to byte in memory = FAST. That also saves 2.560.000 Color objects from being created at 1600x1600px. Some other tweaks were done to FastPixel to result in less "x = new y()" statements, again saving 2.560.000 cpu intensive operations for each. All this gets executed in parallel (new threads) for each image to put all cores to work.

I think we both know that this method should outperform any php implementation by far because if php would be faster its the same as saying that php is faster than the language it was written in, C. But I'm gonna leave an open mind for your results because if it isn't some flaw I want to know why you are getting those low times. So do let me know if you can think of something.
 
Status
Not open for further replies.

About the author

Hyperz
Active Member · Joined
2,482
Messages
618
Reactions
113
Points

Advertise on WJunction

Reach 1000's of webmasters, hosts & affiliates. Banner & sponsored-thread slots available.

Contact us
Back
Top Bottom