New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load a image from the web #219

Closed
RafaelOliveira opened this Issue Jan 23, 2016 · 17 comments

Comments

Projects
None yet
4 participants
@RafaelOliveira
Contributor

RafaelOliveira commented Jan 23, 2016

I'm trying to load a png file from the web and create a Image with the data. I made some code with the help of Robert, but I don't know much about read bytes from a image. I'm putting my code here so other people can help and the result can be merged in Kha.
The Format library is needed for this.

This is the last result with a black ball. It's almost there, but it's not the image.

image

the code:

package;

import haxe.io.Bytes;
import haxe.io.BytesInput;
import kha.Color;
import kha.Framebuffer;
import kha.Image;
import kha.Scaler;
import kha.System;
import format.png.Reader;
import format.png.Data;
import format.png.Tools;

class BasicKha 
{
    var backbuffer:Image;   
    var urlImage:Image; 

    public function new() 
    {
        backbuffer = Image.createRenderTarget(800, 600);        

        // note that in html5 we need to deal with the 'Access-Control-Allow-Origin' problem
        // so this function is more usable in cpp targets

        loadImageFromUrl('http://sudoestegames.com/play/ball.png', function(image:Image) {
            urlImage = image;           
        });
    }   

    public function update():Void
    {


    }

    public function render(framebuffer:Framebuffer):Void 
    {       
        backbuffer.g2.begin(true, kha.Color.White); 

        if (urlImage != null)
            backbuffer.g2.drawImage(urlImage, 10, 10);      

        backbuffer.g2.end();

        framebuffer.g2.begin();
        Scaler.scale(backbuffer, framebuffer, System.screenRotation);
        framebuffer.g2.end();
    }

    function loadImageFromUrl(url:String, callback:Image->Void):Void
    {       
        var http = new haxe.Http(url);
        http.onData = function(strData:String)
        {           
            var bytes = Bytes.ofString(strData);            
            var reader = new Reader(new BytesInput(bytes));
            var data = reader.read();
            var header = Tools.getHeader(data);

            var tempImg = Image.create(header.width, header.height);
            var pngbytes = Tools.extract32(data);
            var imagebytes = tempImg.lock();

            for (y in 0...tempImg.height) for (x in 0...tempImg.width) 
            {
                imagebytes.set(y * Std.int(tempImg.width) * 4 + x * 4 + 0, pngbytes.get(y * Std.int(tempImg.width) * 4 + x * 4 + 2));
                imagebytes.set(y * Std.int(tempImg.width) * 4 + x * 4 + 1, pngbytes.get(y * Std.int(tempImg.width) * 4 + x * 4 + 1));
                imagebytes.set(y * Std.int(tempImg.width) * 4 + x * 4 + 2, pngbytes.get(y * Std.int(tempImg.width) * 4 + x * 4 + 0));
                imagebytes.set(y * Std.int(tempImg.width) * 4 + x * 4 + 3, pngbytes.get(y * Std.int(tempImg.width) * 4 + x * 4 + 3));
            }
            tempImg.unlock();

            callback(tempImg);
        }

        http.request();
    }   
}
@sh-dave

This comment has been minimized.

Contributor

sh-dave commented Jan 25, 2016

Do you have to do it by hand and can't just use Assets.loadImageFromPath()?

package;

class LoadRemoteImageSample {
    var image : kha.Image;

    public function new() {
        kha.System.init('LoadRemoteImageSample', 512, 512, system_initializedHandler);
    }

    function system_initializedHandler() {
        kha.Assets.loadImageFromPath('http://sudoestegames.com/play/ball.png', true, function( loadedImage : kha.Image ) {
            image = loadedImage;
            kha.System.notifyOnRender(render);
        });
    }

    function render( fb : kha.Framebuffer ) {
        var g = fb.g2;

        g.begin(true, kha.Color.White);
            g.drawImage(image, 0, 0);
        g.end();
    }
}

class Main {
    public static function main() {
        new LoadRemoteImageSample();
    }
}
@RafaelOliveira

This comment has been minimized.

Contributor

RafaelOliveira commented Jan 25, 2016

This function only works with local files on the system, it wasn't designed to load a remote file. This was the error trying to load the remote file (in the windows target):

Could not open file http:\\sudoestegames.com\play\ball.png.
Could not open file http://sudoestegames.com/play/ball.png.

For a local file it worked. In html5 it worked with a file in localhost, and maybe can work with a domain that don't have problems with cross-origin, but it is just because the way html5 works (it uses a ImageElement).

@sh-dave

This comment has been minimized.

Contributor

sh-dave commented Jan 25, 2016

Oh ok, only tried on flash and it was using flash.net.URLLoader that supports remote loading.

Anyways, your platform is probably not supporting non-pot-textures, try to replace urlImage.width with urlImage.realWidth. Internally the texture gets resized to 128x128 instead of the png's 120x120

imagebytes.set(y * Std.int(urlImage.realWidth) * 4 + x * 4 + 0, pngbytes.get(y * Std.int(header.width) * 4 + x * 4 + 2));
imagebytes.set(y * Std.int(urlImage.realWidth) * 4 + x * 4 + 1, pngbytes.get(y * Std.int(header.width) * 4 + x * 4 + 1));
imagebytes.set(y * Std.int(urlImage.realWidth) * 4 + x * 4 + 2, pngbytes.get(y * Std.int(header.width) * 4 + x * 4 + 0));
imagebytes.set(y * Std.int(urlImage.realWidth) * 4 + x * 4 + 3, pngbytes.get(y * Std.int(header.width) * 4 + x * 4 + 3));

Edit: you can check with trace('non pot texture support: ${kha.Image.nonPow2Supported}');

@RafaelOliveira

This comment has been minimized.

Contributor

RafaelOliveira commented Jan 26, 2016

It's showing that non-pot-textures is supported.
"non pot texture support: true"
i tried the realWidth but the results are the same.

@RafaelOliveira

This comment has been minimized.

Contributor

RafaelOliveira commented Jan 26, 2016

I tested with some images with pot size, and they are showing correctly. But the change with realWidth didn't make effect.
The final numbers on the right I put equal to the left to correct the colors (from 2, 1, 0, 3 to 0, 1, 2, 3)

The two images on the right has pot size. The images on the left are the same with a different size.
teste

@sh-dave

This comment has been minimized.

Contributor

sh-dave commented Jan 27, 2016

Did you maybe change both calls imageBytes.set() and pngbytes.get() to realWidth? If so it won't work. The one in pngbytes.get() must be width.

But if your platform supports non-pot textures it shouldn't actually make a diffrence at all.
Can you post the traces?

trace('real width/height ${tempImage.realWidth}x${tempImage.realHeight}');
trace('width/height ${tempImage.width}x${tempImage.height}');
@RafaelOliveira

This comment has been minimized.

Contributor

RafaelOliveira commented Jan 27, 2016

this are traces of the four images. the size in width and realwidth are the same. And I made the code like you said, the ones in pngbytes.get using width.

BasicKha.hx:94: real width/height 120x120
BasicKha.hx:95: width/height 120x120

BasicKha.hx:94: real width/height 128x128
BasicKha.hx:95: width/height 128x128

BasicKha.hx:94: real width/height 163x168
BasicKha.hx:95: width/height 163x168

BasicKha.hx:94: real width/height 64x64
BasicKha.hx:95: width/height 64x64

this is my code:

for (y in 0...tempImg.height) 
{
    for (x in 0...tempImg.width) 
    {
        imagebytes.set(y * Std.int(tempImg.realWidth) * 4 + x * 4 + 0, pngbytes.get(y * Std.int(tempImg.width) * 4 + x * 4 + 0));
        imagebytes.set(y * Std.int(tempImg.realWidth) * 4 + x * 4 + 1, pngbytes.get(y * Std.int(tempImg.width) * 4 + x * 4 + 1));
        imagebytes.set(y * Std.int(tempImg.realWidth) * 4 + x * 4 + 2, pngbytes.get(y * Std.int(tempImg.width) * 4 + x * 4 + 2));
        imagebytes.set(y * Std.int(tempImg.realWidth) * 4 + x * 4 + 3, pngbytes.get(y * Std.int(tempImg.width) * 4 + x * 4 + 3));
    }
}

I'm thinking if maybe this could be a bug?

@dmitryhryppa

This comment has been minimized.

Contributor

dmitryhryppa commented Feb 23, 2016

Did you find a solution? Have the same issue too.
Btw, you can use a little bit simpler loop:

            for (i in 0...imagebytes.length) {
                imagebytes.set(i, pngbytes.get(i));
            }
@sh-dave

This comment has been minimized.

Contributor

sh-dave commented Feb 24, 2016

Ok, i think i kinda figured it out. Please try this in Kha/Kore/Backends/OpenGL2/Sources/Kore/TextureImpl.cpp

void Texture::unlock() {
...
    // (DK) use the size of the data buffer to upload (width/height), not the gl texture size (texWidth/Height), as it may be larger than data 120x120 -> 128x128
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, convertInternal(format), GL_UNSIGNED_BYTE, data);
...
}

and you shouldn't need a loop at all, just blit the bytes:

imagebytes.blit(0, pngbytes, 0, pngbytes.length);

If you're targeting iOS you have to to it in Texture::upload(u8* data) as well i think, haven't tested this yet though.

@RobDangerous

This comment has been minimized.

Member

RobDangerous commented Feb 24, 2016

iOS should also work fine using unlock, update is just there for more efficient video support.

@RafaelOliveira

This comment has been minimized.

Contributor

RafaelOliveira commented Feb 25, 2016

The change is working, but is specific to opengl. But we need this in directx too (the default in windows). But its a start.

@RafaelOliveira

This comment has been minimized.

Contributor

RafaelOliveira commented Feb 26, 2016

Format is a dependence that I think people shouldn't have to install, and I saw that it doesn't support reading jpg. I think that in Kore this could be handled in the same way as images are loaded (if possible, we need to pass the bytes to cpp), html5 and flash can use Assets.loadImageFromPath, the others target we need to think.

@RobDangerous

This comment has been minimized.

Member

RobDangerous commented Feb 26, 2016

True, already thought about a decodeImageBytes method, implemented by the backends.

@RafaelOliveira

This comment has been minimized.

Contributor

RafaelOliveira commented Jun 30, 2016

I just tested a alternative method, load the image with haxe.Http, save in the disk and load with Kha. And I needed to add hxssl to the project to compile in Visual Studio. This is only for cpp targets:

import sys.io.File;
import haxe.io.Bytes;
import haxe.Http;

function downloadImage(name:String, completePath:String):Void
{       
    var http = new Http(completePath);
    http.onData = function(data:String) {
        var bytes = Bytes.ofString(data);
        File.saveBytes(name, bytes);
        Assets.loadImageFromPath(name, false, imageDownloaded); 
    }

    http.onError = function(msg:String) {
        trace(msg);
    }

    http.request();
}

function imageDownloaded(image:Image):Void
{
    // use the image downloaded
}
@RobDangerous

This comment has been minimized.

Member

RobDangerous commented Jul 1, 2016

The hxssl thing should be fixed, did you try with the very very latest Kha?

@RafaelOliveira

This comment has been minimized.

Contributor

RafaelOliveira commented Jul 1, 2016

You are right, Kha seems to be updated but deleting the build folder and compiling without hxssl worked now.

@RafaelOliveira

This comment has been minimized.

Contributor

RafaelOliveira commented May 5, 2017

I made a library that works (html5 and linux) based on #440 (comment). It will be put on the forum when finished. Thanks @romamik.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment