• 【动机】

之前看到一款卡牌游戏,当你要看全屏高清卡牌的时候,游戏会单独从网络上下载,本地只存了非高清的,这样可以省点包大小,所以我萌生了实现一个读取网络图片的类。

  • 【联想】

之前浏览网页的时候经常看到一张图片渐进(由模糊变清晰)的显示,如果在游戏中,诸如像显示高清卡牌的时候,使用有这种方式去显示一张图片,这样的体验应该会稍微好些

  • 【相关知识】

  png interlaced:png图片在导出的时候是可以选择 interlaced (Adam7)的,这样的存储的png在网页上显示会渐进显示,

这种interlaced方式是由adam 开发的,分为7段扫描,具体方式如下面的gif图

jpg progressive:在web浏览器上很多都是使用这种模式的图片

  • 【png解码】

cocos2d-x没有对interlaced模式进行支持,libpng本身肯定是支持的,对interlaced图片png必须使用png_progressive_combine_row来逐行读取,非interlaced的png图片也是一样支持的,libpng解析,首先我们要初始化png_structp,所有解析的信息都在这个结构体里

bool PNGCodec::PrepareDecode() {
png_reader_.png_struct_ptr_= png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_reader_.png_struct_ptr_)
return false; png_reader_.png_info_ptr_ = png_create_info_struct(png_reader_.png_struct_ptr_);
if (!png_reader_.png_info_ptr_) {
png_destroy_read_struct(&png_reader_.png_struct_ptr_, NULL, NULL);
return false;
} if (setjmp(png_jmpbuf(png_reader_.png_struct_ptr_))) {
png_destroy_read_struct(&png_reader_.png_struct_ptr_, &png_reader_.png_info_ptr_, (png_infopp)NULL);
return false;
} png_set_error_fn(png_reader_.png_struct_ptr_, NULL, LogLibPNGDecodeError, LogLibPNGDecodeWarning); png_set_progressive_read_fn(png_reader_.png_struct_ptr_, &png_reader_, &DecodeInfoCallback,
&DecodeRowCallback, &DecodeEndCallback);
png_reader_.decode_state_ = PNGCodec::DecodeState::DECODE_READY;
return true;
}

这里主要是png_set_progressive_read_fn 函数,通过设置回调方式,第3个参数是读完png_info(png头)的回调,第4个参数row读入的回调,第5个参数是解析结束的的回调

有这些回调函数,我们设置回调函数,通过回调函数来更新sprite的texture

/*
@parm1:png_structp
@parm2:自定义指针
@parm3:void *png_progressive_info_ptr(png_struct* png_ptr, png_info* info_ptr)
@parm4:void *png_progressive_row_ptr(png_struct* png_ptr, png_byte* new_row, png_uint_32 row_num, int pass)
@parm5:void png_progressive_end_ptr(png_struct* png_ptr, png_info* info)
*/
void, png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr, png_progressive_info_ptr info_fn,
png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn))
  • 【思路】

加载网络图片首先从网下下载png数据,通过curl把数据送给png解析,通过png的回调来更新sprite的textrue,把下载和解析放在一个线程里做,这样就不会阻塞了

我实现了四个类

PNGCoder:主要完成对png图片的解析

HttpConnection:对curl的封装

CCInterlacedImage:用于缓存png解析后的数据

WebSprite: 主要提供initWithFileUrl接口,

用户通过创建一个websprite:initWithFileUrl,并websprite加到scene中,由websprite来创建线程和创建httpconneciton,

  • 【碰到的问题】

1.如何线程通信:之前使用boost库的时候,boost 实现io_sevice,可以通过boost::asio io_service, io_sevice实际上是一个function队列,他是线程安全的,

c++11我没找到,所以我在websprite也创建了这样的一个队列,但是要自己去处理这个队列的线程安全,这个可以通过锁来实现

2.如何终止线程:当我们释放websprite,线程属于分离状态,线程无法强转终止,std:thread没有提供相关接口,curl_easy_perform是阻塞的,当你要释放websprite的时候,这个时候线程还在跑,怎么终止 curl可以通过size_twriteData(void*ptr,size_tsize,size_tnmemb,void*stream) 的返回0时,curl_easy_perform会终止返回错误,

3.如何处理内存释放的问题:因为这是跨线程的,数据的安全释放就要变得尤为小心,因为我的通常你可能需要设置某个标志位在两个线程间来通知相关指针是否已经失效,使用共享指针线程之间的内存释放问题可以很好的解决了,你不需要去关心这个问题,引用计数来解决这个问题,std:shared_ptr的引用计数是线程安全的

  • 【效果图】

这是在浏览

http://daltonclaybrook.com/future.png

  • 【代码】
#include "CCWebSprite.h"
#include "CCInterlacedPngImage.h"
#include "http_connection.h"
#include "png_codec.h" #include <future> namespace cocos2d { // Callback function used by libcurl for collect response data
size_t WebSprite::DataBridge::WriteData(void *ptr, size_t size, size_t nmemb, void *stream) {
if (stream == nullptr) {
return ;
}
WebSprite* web_sprite = static_cast<WebSprite*>(stream);
if (web_sprite == nullptr) {
return ;
}
size_t sizes = size * nmemb;
web_sprite->reciverData((unsigned char*)ptr, sizes);
return sizes;
} void WebSprite::DataBridge::ReadHeaderCompleteCallBack(void* ptr) {
WebSprite* web_sprite = static_cast<WebSprite*>(ptr);
web_sprite->readHeaderComplete();
} void WebSprite::DataBridge::ReadRowCompleteCallBack(void* ptr, int pass) {
WebSprite* web_sprite = static_cast<WebSprite*>(ptr);
web_sprite->readRowComplete(pass);
} void WebSprite::DataBridge::ReadAllCompleteCallBack(void* ptr) {
WebSprite* web_sprite = static_cast<WebSprite*>(ptr);
web_sprite->readAllComplete();
} WebSprite::WebSprite() : http_connection_(nullptr),
png_coder_(std::make_shared<util::PNGCodec>()), interlaced_png_image_buff_(new InterlacedPngImage()), code_pass_(-){ } WebSprite::~WebSprite() {
if (http_connection_ != nullptr) {
http_connection_->SetWriteCallBack(nullptr, WebSprite::DataBridge::WriteData);
}
png_coder_->SetReadCallBack(nullptr, nullptr, nullptr, nullptr);
CC_SAFE_RELEASE(interlaced_png_image_buff_);
} WebSprite* WebSprite::create() {
WebSprite *sprite = new WebSprite();
if (sprite && sprite->init())
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return nullptr;
} WebSprite* WebSprite::createWithFileUrl(const char *file_url) {
WebSprite *sprite = new WebSprite();
if (sprite && sprite->initWithFileUrl(file_url))
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return nullptr;
} bool WebSprite::initWithFileUrl(const char *file_url) {
Sprite::init();
file_url_ = file_url;
if (isRemotoeFileUrl(file_url)) {
return initWithRemoteFile();
} else {
return initWithLocalFile();
}
} bool WebSprite::initWithRemoteFile() {
assert(http_connection_ == nullptr);
http_connection_ = std::make_shared<HttpConnection>();
http_connection_->Init(file_url_.c_str());
png_coder_->PrepareDecode();
png_coder_->SetReadCallBack(this, WebSprite::DataBridge::ReadHeaderCompleteCallBack, WebSprite::DataBridge::ReadRowCompleteCallBack, WebSprite::DataBridge::ReadAllCompleteCallBack);
http_connection_->SetWriteCallBack(this, WebSprite::DataBridge::WriteData);
this->scheduleUpdate();
std::thread http_thread = std::thread(std::bind(&HttpConnection::PerformGet, http_connection_));
http_thread.detach();
return true;
} bool WebSprite::initWithLocalFile() {
auto filePath = FileUtils::getInstance()->fullPathForFilename(file_url_);
std::shared_ptr<Data> data = std::make_shared<Data>(FileUtils::getInstance()->getDataFromFile(filePath));
png_coder_->PrepareDecode();
png_coder_->SetReadCallBack(this, &WebSprite::DataBridge::ReadHeaderCompleteCallBack, WebSprite::DataBridge::ReadRowCompleteCallBack, WebSprite::DataBridge::ReadAllCompleteCallBack);
std::thread http_thread = std::thread(std::bind([=](){
png_coder_->Decoding(data->getBytes(), data->getSize());
}
));
http_thread.detach();
this->scheduleUpdate();
return true;
} bool WebSprite::isRemotoeFileUrl(const char *file_url) {
if (strlen(file_url) > && (strncmp(file_url, "http://", ) == )) {
return true;
}
return false;
} void WebSprite::reciverData(unsigned char* data, size_t data_size) {
png_coder_->Decoding(data, data_size);
} void WebSprite::updateTexture() {
cocos2d::Texture2D* texture = cocos2d::Director::getInstance()->getTextureCache()->addImage(interlaced_png_image_buff_, file_url_);
texture->updateWithData(interlaced_png_image_buff_->getData(), , , interlaced_png_image_buff_->getWidth(),
interlaced_png_image_buff_->getHeight());
SpriteFrame* sprite_frame = cocos2d::SpriteFrame::createWithTexture(texture,
CCRectMake(,,texture->getContentSize().width, texture->getContentSize().height));
Sprite::setSpriteFrame(sprite_frame);
} void WebSprite::readHeaderComplete() {
interlaced_png_image_buff_->setImageHeader(png_coder_->png_width(), png_coder_->png_height(), png_coder_->png_color_type(), png_coder_->png_output_channels());
} void WebSprite::readRowComplete(int pass) {
if (code_pass_ < pass) {
perform_mutex_.lock();
interlaced_png_image_buff_->setImageBodyData((char*)png_coder_->png_data_buffer(), png_coder_->png_data_size());
perform_main_thread_functions_.push_back(std::bind(&WebSprite::updateTexture, this));
perform_mutex_.unlock();
code_pass_ = pass;
}
} // run on sub thread
void WebSprite::readAllComplete() {
perform_mutex_.lock();
interlaced_png_image_buff_->setImageBodyData((char*)png_coder_->png_data_buffer(), png_coder_->png_data_size());
perform_main_thread_functions_.push_back(std::bind(&WebSprite::updateTexture, this));
perform_mutex_.unlock();
} void WebSprite::update(float fDelta) {
Sprite::update(fDelta);
perform_mutex_.lock();
for (std::vector<std::function<void ()> >::iterator it = perform_main_thread_functions_.begin();
it != perform_main_thread_functions_.end(); ++it) {
(*it)();
}
perform_main_thread_functions_.clear();
perform_mutex_.unlock();
}

这是在cocos2d3.0基础上开发的,把下面的文件替换掉ccp-empty-test,就可以了

https://github.com/SachinKung/WebSprite

  • 【参考】

1.https://github.com/daltonclaybrook/SFSInterlacedImageView

2.https://code.google.com/p/chromium/codesearch#chromium/src/ui/gfx/codec/png_codec.h&q=png_code&sq=package:chromium&l=1

[原创]cocos2dx加载网络图片&异步加载图片的更多相关文章

  1. Android实战简易教程-第四十九枪(两种方式实现网络图片异步加载)

    加载图片属于比较耗时的工作,我们需要异步进行加载,异步加载有两种方式:1.通过AsyncTask类进行:2.通过Handler来实现,下面我们就来看一下如何通过这两种方式实现网络图片的异步加载. 一. ...

  2. 【UE4 C++ 基础知识】<11>资源的同步加载与异步加载

    同步加载 同步加载会造成进程阻塞. FObjectFinder / FClassFinder 在构造函数加载 ConstructorHelpers::FObjectFinder Constructor ...

  3. Unity+NGUI打造网络图片异步加载和本地缓存工具(一)

    我们已经开发了在移动终端中,异步网络图片被装入多,在unity其中尽管AssetBundle存在,通常第一个好游戏的资源,然后加载到现场,但也有很多地方可以使用异步网络加载图像以及其缓存机制. 我也写 ...

  4. cocos2d-x lua中实现异步加载纹理

    原文地址:  http://www.cnblogs.com/linchaolong/p/4033118.html 前言   问题:最近项目中需要做一个loading个界面,界面中间有一个角色人物走动的 ...

  5. 动态加载(异步加载)jquery/MUI类库 页面加载完成后加载js类库

    动态加载Mui类库: // ==UserScript== // @name // @version 1.4.0 // @author zzdhidden@gmail.com // @namespace ...

  6. javascript 同步加载与异步加载

    HTML 4.01 的script属性 charset: 可选.指定src引入代码的字符集,大多数浏览器忽略该值. defer: boolean, 可选.延迟脚本执行,相当于将script标签放入页面 ...

  7. Javascript 文件的同步加载与异步加载

    HTML 4.01 的script属性 charset: 可选.指定src引入代码的字符集,大多数浏览器忽略该值.defer: boolean, 可选.延迟脚本执行,相当于将script标签放入页面b ...

  8. 关于requireJS的同步加载和异步加载

    这篇随笔主要记录require('name')和require(['name1','name2'])在同步和异步加载使用的区别 1.require('name')同步加载模块的形式 define(fu ...

  9. AJAX中的同步加载与异步加载

    AJAX是四个单词的简写,其中Asynchronous即异步的意思,异步的链接可以同时发起多个,并且不会阻止JS代码执行.与之对应的概念是同步,同步的链接在同一时刻只会有一个,并且会阻止后续JS代码的 ...

随机推荐

  1. JavaWeb 学习009-4个页面,5条sql语句(添加、查看、修改、删除)

    ===========++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++==+++++++++ 2016-12-3------ ...

  2. MySQL安装,启动

    一.安装.配置环境变量 http://www.cnblogs.com/mr-wid/archive/2013/05/09/3068229.html 配置环境变量:把你的安装目录复制下,例如:G:\Do ...

  3. 修改了chrome的官方的有道词典插件,添加了生词本的功能

    项目地址+导入教程 https://github.com/cclient/chrome-extensions-youdaowithwordnode

  4. 我总结的git命令指南。

    git命令行指南 1.windows上安装git的网址:http://msysgit.github.com/. 没有安装过git的 这个网址里下mymsgit. 因为发现有些同学对git还不是那么熟悉 ...

  5. URAL 1992 CVS 可持久化链栈

    http://www.cnblogs.com/tedzhao/archive/2008/11/12/1332112.html 看这篇的链表部分的介绍应该就能理解“可持久化”了 动态分配内存的会T,只能 ...

  6. 刨一刨内核container_of()的设计精髓

    新年第一帖,总得拿出点干货才行,虽然这篇水分还是有点大,大家可以晒干了温水冲服.这段时间一直在整理内核学习的基础知识点,期间又碰到了container_of()这个宏,当然还包括一个叫做offseto ...

  7. Faster-RCNN 解析

    http://blog.csdn.net/xzzppp/article/details/52317863 包含faster-rcnn源码和对应的训练测试相关的知识点解析

  8. Loadrunner监控Linux的17个指标

    这17个指标根据需要设置吧,指标设置的越多,对服务器真实值影响越大,所以要秉承按需而设的原则.   1.Average load:Average number of processes simulta ...

  9. amr转MP3

    using System; using System.Threading; using System.IO; using System.Diagnostics; using System.Securi ...

  10. Thinkphp 学习笔记

    前后台配置: 在根目录文件夹中创建一个Conf文件夹 Conf文件夹下建立一个config.php文件,里面存放公共配置信息,方便前后台调用. 简单定义404页面 伪静态去除.html Config中 ...