• 【动机】

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

  • 【联想】

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

  • 【相关知识】

  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. 模糊测试(Fuzz testing)

    模糊测试(fuzz testing)是一种安全测试方法,他介于完全的手工测试和完全的自动化测试之间.为什么是介于那两者之间?首先完全的手工测试即是渗透测试,测试人员可以模拟黑客恶意进入系统.查找漏洞, ...

  2. TCP协议学习记录 (三) Ping程序 RR选项 记录路由hop

    一开始想直接在上个程序改,自己构造IP包头,但后来发现不行,微软不让干了,所以后来选用libcap库来收发包 代码写的很乱.. #pragma pack(4) #define ECHO_REQUEST ...

  3. unity3d的Texture2D与opencv的Mat之间的相互转换

    这里的opencv使用的是opencvsharp,它是c#语言对c++的opencv的一层封装,使得c#开发者可以更方便的使用opencv,而不必纠结于跨语言调用的问题. 因此,这里使用的opencv ...

  4. 内核对TCP REUSEPORT的优化

    Q&A 当有人问起我关于reuseport的一些事的时候,我们的对话基本如下:Q1:什么是reuseport?A1:reuseport是一种套接字复用机制,它允许你将多个套接字bind在同一个 ...

  5. ShellExecuteA

    //第三个参数是指令,可以是一个可执行程序(后面不能加参数).有默认打开方式的文件.路径.网址.各种协议地址如迅雷ftp邮箱ed2k等 MessageBoxA

  6. IOS 代码提示有问题

    Window(menu) -> Organizer(menu) -> Projects(tab) 删除 Derived Data ,立刻关闭xcode 然后重启xcode然后重新打开项目.

  7. 【javascript】:Highcharts实战

    PS: Highcharts是一款前端图标设计框架,非常绚. 前端JS: var probabilityStatisticsData; var yearTool; var CoordinateX = ...

  8. 判断AngularJS渲染页面完成

    $scope.$on('$viewContentLoaded', function(){ }); // 或者 $scope.$watch('$viewContentLoaded', function( ...

  9. <%%>标签 什么意思

    <%%>是说这里面的文本不是普通直接输出到客户端的文本,而是需要服务器来解释的.不光是可以写JAVA脚本,这要看具体服务器端使用的是什么技术ASP/JSP/PHP/ASP.NET都使用相同 ...

  10. 时间管理的若干Tips

    时间管理的若干Tips 记下来 再好的记性也不如一支笔与一个本子. 买一支好点的笔于一个好点的本子,让自己有书写的欲望,将todo事项记下来. 小目标 太大太远的目标会使人气馁.通过将大目标分解再分解 ...