• 【动机】

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

  • 【联想】

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

  • 【相关知识】

  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,所有解析的信息都在这个结构体里

  1. bool PNGCodec::PrepareDecode() {
  2. png_reader_.png_struct_ptr_= png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  3. if (!png_reader_.png_struct_ptr_)
  4. return false;
  5.  
  6. png_reader_.png_info_ptr_ = png_create_info_struct(png_reader_.png_struct_ptr_);
  7. if (!png_reader_.png_info_ptr_) {
  8. png_destroy_read_struct(&png_reader_.png_struct_ptr_, NULL, NULL);
  9. return false;
  10. }
  11.  
  12. if (setjmp(png_jmpbuf(png_reader_.png_struct_ptr_))) {
  13. png_destroy_read_struct(&png_reader_.png_struct_ptr_, &png_reader_.png_info_ptr_, (png_infopp)NULL);
  14. return false;
  15. }
  16.  
  17. png_set_error_fn(png_reader_.png_struct_ptr_, NULL, LogLibPNGDecodeError, LogLibPNGDecodeWarning);
  18.  
  19. png_set_progressive_read_fn(png_reader_.png_struct_ptr_, &png_reader_, &DecodeInfoCallback,
  20. &DecodeRowCallback, &DecodeEndCallback);
  21. png_reader_.decode_state_ = PNGCodec::DecodeState::DECODE_READY;
  22. return true;
  23. }

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

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

  1. /*
    @parm1:png_structp
  2. @parm2:自定义指针
  3. @parm3:void *png_progressive_info_ptr(png_struct* png_ptr, png_info* info_ptr)
  4. @parm4:void *png_progressive_row_ptr(png_struct* png_ptr, png_byte* new_row, png_uint_32 row_num, int pass)
  5. @parm5:void png_progressive_end_ptr(png_struct* png_ptr, png_info* info)
    */
  6. void, png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr, png_progressive_info_ptr info_fn,
  7. 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

  • 【代码】
  1. #include "CCWebSprite.h"
  2. #include "CCInterlacedPngImage.h"
  3. #include "http_connection.h"
  4. #include "png_codec.h"
  5.  
  6. #include <future>
  7.  
  8. namespace cocos2d {
  9.  
  10. // Callback function used by libcurl for collect response data
  11. size_t WebSprite::DataBridge::WriteData(void *ptr, size_t size, size_t nmemb, void *stream) {
  12. if (stream == nullptr) {
  13. return ;
  14. }
  15. WebSprite* web_sprite = static_cast<WebSprite*>(stream);
  16. if (web_sprite == nullptr) {
  17. return ;
  18. }
  19. size_t sizes = size * nmemb;
  20. web_sprite->reciverData((unsigned char*)ptr, sizes);
  21. return sizes;
  22. }
  23.  
  24. void WebSprite::DataBridge::ReadHeaderCompleteCallBack(void* ptr) {
  25. WebSprite* web_sprite = static_cast<WebSprite*>(ptr);
  26. web_sprite->readHeaderComplete();
  27. }
  28.  
  29. void WebSprite::DataBridge::ReadRowCompleteCallBack(void* ptr, int pass) {
  30. WebSprite* web_sprite = static_cast<WebSprite*>(ptr);
  31. web_sprite->readRowComplete(pass);
  32. }
  33.  
  34. void WebSprite::DataBridge::ReadAllCompleteCallBack(void* ptr) {
  35. WebSprite* web_sprite = static_cast<WebSprite*>(ptr);
  36. web_sprite->readAllComplete();
  37. }
  38.  
  39. WebSprite::WebSprite() : http_connection_(nullptr),
  40. png_coder_(std::make_shared<util::PNGCodec>()), interlaced_png_image_buff_(new InterlacedPngImage()), code_pass_(-){
  41.  
  42. }
  43.  
  44. WebSprite::~WebSprite() {
  45. if (http_connection_ != nullptr) {
  46. http_connection_->SetWriteCallBack(nullptr, WebSprite::DataBridge::WriteData);
  47. }
  48. png_coder_->SetReadCallBack(nullptr, nullptr, nullptr, nullptr);
  49. CC_SAFE_RELEASE(interlaced_png_image_buff_);
  50. }
  51.  
  52. WebSprite* WebSprite::create() {
  53. WebSprite *sprite = new WebSprite();
  54. if (sprite && sprite->init())
  55. {
  56. sprite->autorelease();
  57. return sprite;
  58. }
  59. CC_SAFE_DELETE(sprite);
  60. return nullptr;
  61. }
  62.  
  63. WebSprite* WebSprite::createWithFileUrl(const char *file_url) {
  64. WebSprite *sprite = new WebSprite();
  65. if (sprite && sprite->initWithFileUrl(file_url))
  66. {
  67. sprite->autorelease();
  68. return sprite;
  69. }
  70. CC_SAFE_DELETE(sprite);
  71. return nullptr;
  72. }
  73.  
  74. bool WebSprite::initWithFileUrl(const char *file_url) {
  75. Sprite::init();
  76. file_url_ = file_url;
  77. if (isRemotoeFileUrl(file_url)) {
  78. return initWithRemoteFile();
  79. } else {
  80. return initWithLocalFile();
  81. }
  82. }
  83.  
  84. bool WebSprite::initWithRemoteFile() {
  85. assert(http_connection_ == nullptr);
  86. http_connection_ = std::make_shared<HttpConnection>();
  87. http_connection_->Init(file_url_.c_str());
  88. png_coder_->PrepareDecode();
  89. png_coder_->SetReadCallBack(this, WebSprite::DataBridge::ReadHeaderCompleteCallBack, WebSprite::DataBridge::ReadRowCompleteCallBack, WebSprite::DataBridge::ReadAllCompleteCallBack);
  90. http_connection_->SetWriteCallBack(this, WebSprite::DataBridge::WriteData);
  91. this->scheduleUpdate();
  92. std::thread http_thread = std::thread(std::bind(&HttpConnection::PerformGet, http_connection_));
  93. http_thread.detach();
  94. return true;
  95. }
  96.  
  97. bool WebSprite::initWithLocalFile() {
  98. auto filePath = FileUtils::getInstance()->fullPathForFilename(file_url_);
  99. std::shared_ptr<Data> data = std::make_shared<Data>(FileUtils::getInstance()->getDataFromFile(filePath));
  100. png_coder_->PrepareDecode();
  101. png_coder_->SetReadCallBack(this, &WebSprite::DataBridge::ReadHeaderCompleteCallBack, WebSprite::DataBridge::ReadRowCompleteCallBack, WebSprite::DataBridge::ReadAllCompleteCallBack);
  102. std::thread http_thread = std::thread(std::bind([=](){
  103. png_coder_->Decoding(data->getBytes(), data->getSize());
  104. }
  105. ));
  106. http_thread.detach();
  107. this->scheduleUpdate();
  108. return true;
  109. }
  110.  
  111. bool WebSprite::isRemotoeFileUrl(const char *file_url) {
  112. if (strlen(file_url) > && (strncmp(file_url, "http://", ) == )) {
  113. return true;
  114. }
  115. return false;
  116. }
  117.  
  118. void WebSprite::reciverData(unsigned char* data, size_t data_size) {
  119. png_coder_->Decoding(data, data_size);
  120. }
  121.  
  122. void WebSprite::updateTexture() {
  123. cocos2d::Texture2D* texture = cocos2d::Director::getInstance()->getTextureCache()->addImage(interlaced_png_image_buff_, file_url_);
  124. texture->updateWithData(interlaced_png_image_buff_->getData(), , , interlaced_png_image_buff_->getWidth(),
  125. interlaced_png_image_buff_->getHeight());
  126. SpriteFrame* sprite_frame = cocos2d::SpriteFrame::createWithTexture(texture,
  127. CCRectMake(,,texture->getContentSize().width, texture->getContentSize().height));
  128. Sprite::setSpriteFrame(sprite_frame);
  129. }
  130.  
  131. void WebSprite::readHeaderComplete() {
  132. interlaced_png_image_buff_->setImageHeader(png_coder_->png_width(), png_coder_->png_height(), png_coder_->png_color_type(), png_coder_->png_output_channels());
  133. }
  134.  
  135. void WebSprite::readRowComplete(int pass) {
  136. if (code_pass_ < pass) {
  137. perform_mutex_.lock();
  138. interlaced_png_image_buff_->setImageBodyData((char*)png_coder_->png_data_buffer(), png_coder_->png_data_size());
  139. perform_main_thread_functions_.push_back(std::bind(&WebSprite::updateTexture, this));
  140. perform_mutex_.unlock();
  141. code_pass_ = pass;
  142. }
  143. }
  144.  
  145. // run on sub thread
  146. void WebSprite::readAllComplete() {
  147. perform_mutex_.lock();
  148. interlaced_png_image_buff_->setImageBodyData((char*)png_coder_->png_data_buffer(), png_coder_->png_data_size());
  149. perform_main_thread_functions_.push_back(std::bind(&WebSprite::updateTexture, this));
  150. perform_mutex_.unlock();
  151. }
  152.  
  153. void WebSprite::update(float fDelta) {
  154. Sprite::update(fDelta);
  155. perform_mutex_.lock();
  156. for (std::vector<std::function<void ()> >::iterator it = perform_main_thread_functions_.begin();
  157. it != perform_main_thread_functions_.end(); ++it) {
  158. (*it)();
  159. }
  160. perform_main_thread_functions_.clear();
  161. perform_mutex_.unlock();
  162. }

这是在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. 【随笔】js加载

    有时候,当发现js操作一个dom的时候,发现dom没有找到,这是由于html没有加载完就开始操作该dom的缘故,所以需要在html文档加载完后再加载js,于是我们可以这么做: js方法:window. ...

  2. Spring bean的作用域和生命周期

    bean的作用域 1.singleton,prototype, web环境下:request,session,gloab session 2.通过scope="" 来进行配置 3. ...

  3. Java线程池入门

    序 为什么要用线程池?什么情况下才会用到线程池? 并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 因此 ...

  4. 转载 - Vultr VPS注册开通且一键快速安装PPTP VPN和电脑连接使用

    本文转载来自:https://www.vultrclub.com/139.html 从2014年Vultr VPS进入市场之后,作为有背景.实力的搅局者,是的最近两年VPS.服务器的用户成本降低.配置 ...

  5. 把文件打成zip或然rar下载 (详询请加qq:2085920154)

    //文件打包下载 public static HttpServletResponse downLoadFiles(List<File> files, HttpServletRequest ...

  6. javascript学习面向对象(二)

    主要内容: prototype扩展应用示例: 对比如下: 数组中forEach用法示例: 从上面示例可以看出,forEach只适合遍历一维数组: 应用prototype扩展实现全部元素遍历如下: 简单 ...

  7. Android自定义View之CircleView

    Android自定义View之CircleView 版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请表明出处:http://www.cnblogs.com/cavalier-/p/5999 ...

  8. asp.net下简单的Epplus导出excel

    引用的命名空间 using System.IO; using OfficeOpenXml; /// <summary> /// 导出excel /// </summary> / ...

  9. uglifyjs2压缩混淆js文件

    uglifyjs可以用来压缩混淆js文件,发布release版本应用利器.在StackOverflow浏览了一下,相比Google Closure和YUI compressor,uglifyjs被推荐 ...

  10. 两种状态显示处理. enum , Linq AsEnumerable

    1.ENUM protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e) { GridViewRow ro ...