[原创]cocos2dx加载网络图片&异步加载图片

  • 【动机】

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

  • 【联想】

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

  • 【相关知识】

  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 0;
  }
  WebSprite* web_sprite = static_cast<WebSprite*>(stream);
    if (web_sprite == nullptr) {
        return 0;
    }
  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_(-1){

}

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) > 7 && (strncmp(file_url, "http://", 7) == 0)) {
        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(), 0, 0, interlaced_png_image_buff_->getWidth(),
                interlaced_png_image_buff_->getHeight());
    SpriteFrame* sprite_frame = cocos2d::SpriteFrame::createWithTexture(texture,
            CCRectMake(0,0,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加载网络图片&异步加载图片,布布扣,bubuko.com

时间: 2024-10-12 22:37:21

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

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

加载图片属于比较耗时的工作,我们需要异步进行加载,异步加载有两种方式:1.通过AsyncTask类进行:2.通过Handler来实现,下面我们就来看一下如何通过这两种方式实现网络图片的异步加载. 一.AsyncTask方式 1.main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.co

浏览器渲染阻塞与优化-详解推迟加载、异步加载。

我认为一个前端工程师是否优秀,很大程度上取决于对前端性能上优化的功力.所以性能优化对前端真的很重要!!! 本文介绍了什么是阻塞.为什么会阻塞?阻塞优化常用的5种方式以及他们的注意事项. 浏览器渲染阻塞与优化      什么是阻塞?在页面中我们通常会引用外部文件,而浏览器在解析HTML页面是从上到下依次解析.渲染,如果<head>中引用了一个a.js文件,而这个文件很大或者有问题,需要2秒加载,那么浏览器会停止渲染页面(此时是白屏显示,就是页面啥都没有),2秒后加载完成才会继续渲染,这个就是阻塞

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

我们在移动端的开发中,异步网络图片加载用的非常的多,在unity当中虽然有AssetBundle的存在,一般是先加载好游戏资源然后再进入场景,但是还有不少地方能够用到异步网络图片的加载以及其缓存机制. 我之前也写过两个版本的ios中的异步网络图片加载helper类,所以今天按照同样的思路,也想做一个好用的helper类给大家使用以及简单的说下实现原理. 首先我们加载一张网络图片,要做的事情分步来讲为: 0.开始之前设置一张固定的图片作为占位图(placeholder),表示我们的图片还没加载好,

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

原文地址:  http://www.cnblogs.com/linchaolong/p/4033118.html 前言   问题:最近项目中需要做一个loading个界面,界面中间有一个角色人物走动的动画,在显示这个loading界面的时候加载资源,项目是用cocos2d-x lua实现的,界面做出来后发现在加载资源的时候界面会卡住. 原因: 因为使用的不是异步加载,而且cocos2d-x没有绑定异步加载资源的api到lua中,其实在lua中实现不了异步. 想通过在lua中启动一个线程去加载资源

同步加载、异步加载、延迟加载

一.同步加载 平常默认用的都是同步加载.如:<script src="http://yourdomain.com/script.js"></script>  同步模式又称阻塞模式,会阻止浏览器的后续处理,停止了后续的文件的解析,执行,如图像的渲染.流览器之所以会采用同步模式,是因为加载的js文件中有对dom的操作,重定向,输出document等默认行为,所以同步才是最安全的.通常会把要加载的js放到body结束标签之前,使得js可在页面最后加载,尽量减少阻塞页面

html+js实现图片预加载(异步加载)

在前端设计时,为了让某县页面的图片即使缓存到浏览器中,一般使用预加载技术,但更为确切的应该称为异步加载,因为对线程不会造成阻塞. 核心代码 <script type="text/javascript">   function loadImage(id,src,callback)   { var img = new window.Image(); //当图片成功加载到浏览器缓存 img.onload =function(evt)   { if(typeof(img.ready

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

HTML 4.01 的script属性 charset: 可选.指定src引入代码的字符集,大多数浏览器忽略该值.defer: boolean, 可选.延迟脚本执行,相当于将script标签放入页面body标签的底部,js脚本会在document的DOMContentLoaded之前执行.除IE和较新版本的Firefox外,其他浏览器并未支持.language: 已废弃.大部分浏览器会忽略该值.src: 可选.指定引入的外部代码文件,不限制后缀名.type: 必选.指定脚本的内容类型(MIME类

介绍同步加载、异步加载、延迟加载

同步加载 同步模式又称阻塞模式,会阻止浏览器的后续处理,停止了后续的文件的解析,执行,如图像的渲染.流览器之所以会采用同步模式,是因为加载的js文件中有对dom的操作,重定向,输出document等默认行为,所以同步才是最安全的.通常会把要加载的js放到body结束标签之前,使得js可在页面最后加载,尽量减少阻塞页面的渲染.这样可以先让页面显示出来 简单的说 js在网页中加载会阻塞到未加载的css,html的加载跟渲染 •异步加载也叫非阻塞模式加载,浏览器在下载js的同时,同时还会执行后续的页面

Unity+NGUI打造网络图片异步加载与本地缓存工具类(二)

接上文,我们的工具类中的主要方法: public  void SetAsyncImage(string url,UITexture texture) 按照前文分析的图片加载步骤来 public void SetAsyncImage(string url,UITexture texture){ //开始下载图片前,将UITexture的主图片设置为占位图 texture.mainTexture = placeholder; //判断是否是第一次加载这张图片 if (!File.Exists (pa