Loading Sprite from URL

Hello,

I’ve been searching and can’t find a ready code to compare mine to. Here is what I’ve done:

    CURL *curl;
    CURLcode res;
    std::string readBuffer; 
    curl = curl_easy_init();
    if(curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, "http://blahblah.png");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }

    CCImage* img = new CCImage;
    img->initWithImageData(&readBuffer, 2000);

    cocos2d::CCTexture2D* texture = new cocos2d::CCTexture2D();
    texture->initWithImage(img);

    CCSprite* sprite = CCSprite::spriteWithTexture(texture);    
    this->addChild(sprite, 10);
    sprite->setPosition(ccp(140, 140));

What am I doing wrong?

What your callback method do?

WriteCallback

I copied it from somewhere but it looks to be copying the content of the url to the variable.

static size_t WriteCallback(void contents, size_t size, size_t nmemb, voiduserp)
{
((std::string**)userp)->appendcontents, size** nmemb);
return size * nmemb;
}

If I could somehow change it to instead write picture data.

cricket chirp

Hello Husam,

look like I do what you do same time before me :wink:

I have the same problem with initWithImageData, the sprite have the good size and everything look like it’s good.
But when I show the sprite, it’s always black.

I try lof of option on CCImage, CCTexture or CCSprite but always the same.
I use, like Husam, libcurl and I get the exact size, and a char* that look like really good (don’t check all but some character and it’s the good one).

After reading the forum, I find just one solution, that to save the image and load it, but my picture are small and can change often, so I will prefer to just load it in the memory.
I also find that : [NSData dataWithContentsOfURL:imgUrl] but it’s Objective-C and don’t exist on CCData. And I don’t know if the loading is asynchronous so know as I have the char* of my file loaded, I will continue to try with it.

I someone success to do it or have the idea, it will be really nice.

Thank you,
Damien

I find the solution.

As written by Leopold here (thanks to you Leopold), OpenGL doesn’t work with multithread.
You have to use the char* data in the main thread.

As I don’t really know how to do it with pthread, I just use the scheduler function, and then I do the job with CCImage.
Work like a charm now, so big thanks for all to help me to add libcurl, use it, and make it work easily.

How did you do it, Damien? I mean putting the image data in the buffer?

I think the length ,you wrote “2000”,
there is my code,

img->initWithImageData((void*)readBuffer.c_str(), readBuffer.length(),CCImage::kFmtPng));

and this is passed on win32.

Hello Husam.

I’m not at the computer with this project, but what do you want exactly ?

Just my remember, I use this sample http://curl.haxx.se/libcurl/c/getinmemory.html

You will have the file in chunk.memory, and now you can use it as a data with initWithImageData.

I can confirm you that later, but look like what I done.

Thank you, it worked!

I edited some things slightly, here is the full code:

- SETUP -

#include “curl.h”
>
struct MemoryStruct {
char memory;
size_t size;
};
>
static size_t
WriteMemoryCallback
{
size_t realsize = size
nmemb;
struct MemoryStruct **mem = userp;
mem~~>memory = realloc;
if {
/* out of memory! **/
printf");
exit;
}
memcpy, contents, realsize);
mem~~>size += realsize;
mem~~>memory[mem~~>size] = 0;
return realsize;
}
>
~~- IMAGE DOWNLOAD –

CURLcurl_handle;
struct MemoryStruct chunk;
chunk.memory = malloc; /* will be grown as needed by the realloc above /
chunk.size = 0; /
no data at this point /
curl_global_init;
/
init the curl session /
curl_handle = curl_easy_init;
/
specify URL to get /
curl_easy_setopt;
/
send all data to this function /
curl_easy_setopt;
/
we pass our ‘chunk’ struct to the callback function /
curl_easy_setopt&chunk);
/
some servers don’t like requests that are made without a user-agent
field, so we provide one /
curl_easy_setopt;
/
get it! /
curl_easy_perform;
/
cleanup curl stuff /
curl_easy_cleanup;
- CREATE CCSPRITE -
CCImage
img = new CCImage;
img~~>initWithImageDatachunk.memory, chunk.size, CCImage::kFmtPng);
cocos2d::CCTexture2D
texture = new cocos2d::CCTexture2D();
texture->initWithImage(img);
CCSprite* sprite = CCSprite::spriteWithTexture(texture);

if(chunk.memory)
free(chunk.memory);

/* we’re done with libcurl, so clean it up */
curl_global_cleanup();

ths is an async call?

Husam Aldahiyat wrote:

Thank you, it worked!
>
I edited some things slightly, here is the full code:
>
- SETUP -
>
> #include “curl.h”
>
> struct MemoryStruct {
> char memory;
> size_t size;
> };
>
> static size_t
> WriteMemoryCallback
> {
> size_t realsize = size
nmemb;
> struct MemoryStruct mem = userp;
> mem~~>memory = realloc;
> if {
> /* out of memory! /
> printf
");
> exit;
> }
> memcpy, contents, realsize);
> mem~~>size += realsize;
> mem~~>memory[mem~~>size] = 0;
> return realsize;
> }
>
>~~- IMAGE DOWNLOAD –
>
>
CURL
curl_handle;
>
struct MemoryStruct chunk;
>
chunk.memory = malloc; /* will be grown as needed by the realloc above /
chunk.size = 0; /
no data at this point /
>
curl_global_init;
>
/
init the curl session /
curl_handle = curl_easy_init;
>
/
specify URL to get /
curl_easy_setopt;
>
/
send all data to this function /
curl_easy_setopt;
>
/
we pass our ‘chunk’ struct to the callback function /
curl_easy_setopt&chunk);
>
/
some servers don’t like requests that are made without a user-agent
field, so we provide one /
curl_easy_setopt;
>
/
get it! /
curl_easy_perform;
>
/
cleanup curl stuff /
curl_easy_cleanup;
>
CCImage
img = new CCImage;
img~~>initWithImageDatachunk.memory, chunk.size, CCImage::kFmtPng);
cocos2d::CCTexture2D
texture = new cocos2d::CCTexture2D();
texture->initWithImage(img);
CCSprite* sprite = CCSprite::spriteWithTexture(texture);
>
if(chunk.memory)
free(chunk.memory);
>
/* we’re done with libcurl, so clean it up */
curl_global_cleanup();

No, not async.

Husam, I think I am missing something in your code. When I try to implement, I get a compile error on this line:

struct MemoryStruct mem = (struct MemoryStruct *)userp;

The error is:

No viable conversion from 'struct MemoryStruct *' to 'struct MemoryStruct'

Changing to:

struct MemoryStruct *mem = (struct MemoryStruct *)userp;

…leads to more compile errors. I think I know how to fix them, but I’m a newb on these boards so I thought I’d ask…
Is there any way you can share the entire code snipped or something equivalent so I can see how you wrote this in your project?

Thanks!

good work.

but need modify a little

because here(cocos2d-x.org) text editor’s problem

  • in here means in html.

struct MemoryStruct *mem = (struct MemoryStruct *)userp;
mem->memory = (char *)realloc(mem~~>memory, mem~~>size + realsize + 1);

chunk.memory = (char*)malloc(1);
chunk.size = 0;

struct MemoryStruct {
char **memory;
size_t size;
};

size_t HelloWorld::WriteMemoryCallback
{
size_t realsize = size** nmemb;
struct MemoryStruct mem = userp;
mem->memory = realloc;
if {
/
out of memory! **/
printf");
exit;
}
memcpy, contents, realsize);
mem~~>size += realsize;
mem~~>memory[mem->size] = 0;
return realsize;
}

void HelloWorld::DoCulr
{
CURL**curl_handle;

struct MemoryStruct chunk;

chunk.memory = (char )malloc; / will be grown as needed by the realloc above /
chunk.size = 0; /
no data at this point /
curl_global_init;
/
init the curl session /
curl_handle = curl_easy_init;
/
specify URL to get /
curl_easy_setopt;
/
send all data to this function /
curl_easy_setopt;
/
we pass our ‘chunk’ struct to the callback function /
curl_easy_setopt&chunk);
/
some servers don’t like requests that are made without a user-agent
field, so we provide one /
curl_easy_setopt;
/
get it! /
curl_easy_perform;
/
cleanup curl stuff /
curl_easy_cleanup;
/

* Now, our chunk.memory points to a memory block that is chunk.size
* bytes big and contains the remote file.
*
* Do something nice with it!
*
* You should be aware of the fact that at this point we might have an
* allocated data block, and nothing has yet deallocated that data. So when
* you’re done with it, you should free() it as a nice application.
/
printfchunk.size);
if
free;
/
we’re done with libcurl, so clean it up */
curl_global_cleanup();

}
when i run the code ,it’s all throw error:HelloCpp.exe 中的 0x00000178 处有未经处理的异常: 0xC0000005: Access violation.Why is it? The code is the same as http://curl.haxx.se/libcurl/c/getinmemory.html

size_t HelloWorld::WriteMemoryCallback(void contents, size_t size, size_t nmemb, voiduserp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;

mem~~>memory = realloc;
if {
/* out of memory! */
printf");
exit;
}
memcpy, contents, realsize);
mem~~>size += realsize;
mem~~>memory[mem~~>size] = 0;

return realsize;
}

Hi,

when I tried to execute this code, XCode throws an error saying “malloc: * error for object 0x1: pointer being realloc’d was not allocated” in the line “mem->memory = (char *)realloc(mem~~>memory, mem~~>size + realsize + 1);”. Anyone knows why could it be happen?

Has anyone run this code to read a CCSprite from a URL?

Thank you!

Ok,
I got this working from this site.
http://techbirds.in/how-to-make-a-sprite-from-url-in-cocos2d-x/

@numandinas
I have written a blog You can refer to this complete working blog
http://techbirds.in/how-to-make-a-sprite-from-url-in-cocos2d-x/

Hope it helps

Hi, this is my 2 cents, for download images with an url, I’m using this option, for now it works very well using iOS and Mac OS, I’ll try curl, looks less code to maintain:

#include <assets-manager/Downloader.h>

void COTDMain::onError(const cocos2d::extension::Downloader::Error &error)
{
}

void COTDMain::onProgress(double total, double downloaded, const std::string &url, const std::string &customId)
{
}

void COTDMain::onSuccess(const std::string &srcUrl, const std::string &storagePath, const std::string &customId)
{

// here in storagePath I see the image created:
// in MacOS X: ~/Documents/image.name.jpg
// in iOS in the bundle app
}

void COTDMain::googleSearchCallback(bool succeeded,
                                    const std::string& link)
{
#define DEFAULT_CONNECTION_TIMEOUT 8
    
    std::size_t found = link.find_last_of("/");
    FileUtils* fu = FileUtils::getInstance();
    std::string storagePath = fu->getWritablePath();
    
    storagePath += link.substr(found+1);
    
    if (fu->isFileExist(storagePath))
    {
        return;
    }
    
    // File does not exist, download.
    this->downloader = std::make_shared<cocos2d::extension::Downloader>();
    this->downloader->setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);
    
    this->downloader->setErrorCallback(std::bind(&COTDMain::onError,
                                                 this,
                                                 std::placeholders::_1));
    
    this->downloader->setProgressCallback(std::bind(&COTDMain::onProgress,
                                                    this,
                                                    std::placeholders::_1,
                                                    std::placeholders::_2,
                                                    std::placeholders::_3,
                                                    std::placeholders::_4));
    
    this->downloader->setSuccessCallback(std::bind(&COTDMain::onSuccess,
                                                   this,
                                                   std::placeholders::_1,
                                                   std::placeholders::_2,
                                                   std::placeholders::_3));
    
    this->downloader->downloadAsync(link, storagePath);
}