1. 程式人生 > >cocos2dx載入網路圖片&圖片流載入顯示

cocos2dx載入網路圖片&圖片流載入顯示

本功能是在後文基礎上擴充套件開發的,新增網路jpg格式圖片的支援,新增圖片本地快取功能,可匯出至lua中使用; 使用時請新增png標頭檔案搜尋路徑,如win32下為:$(EngineRoot)external\png\include\win32 ------------------------  原文分割線  ----------------------
  • 【動機】

   之前看到一款卡牌遊戲,當你要看全屏高清卡牌的時候,遊戲會單獨從網路上下載,本地只存了非高清的,這樣可以省點包大小,所以我萌生了實現一個讀取網路圖片的類。

  • 【聯想】

之前瀏覽網頁的時候經常看到一張圖片漸進(由模糊變清晰)的顯示,如果在遊戲中,諸如像顯示高清卡牌的時候,使用有這種方式去顯示一張圖片,這樣的體驗應該會稍微好些

  • 【相關知識】

  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,就可以了

  • 【參考】

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

相關推薦

取出資料庫中BASE64編碼後的圖片二進位制資料顯示在JSP頁面上

1.建立一個maven的web專案,加入oracle10/postgresql9.4驅動包依賴(本專案使用兩種資料來源進行測試) 依賴如下 <!-- Oracle驅動包 --> <dependency> <group

cocos2dx載入網路圖片&圖片載入顯示

本功能是在後文基礎上擴充套件開發的,新增網路jpg格式圖片的支援,新增圖片本地快取功能,可匯出至lua中使用; 使用時請新增png標頭檔案搜尋路徑,如win32下為:$(EngineRoot)external\png\include\win32 ---------

Unity載入網路圖片顯示在UGUI上,解決載入網路圖片出現問號的問題及其案例分析,例項Demo親測可用

Unity載入網路圖片並顯示在UGUI上,解決載入網路圖片出現問號的問題及其案例分析,例項Demo親測可用 最近自己在載入網路圖片的時候也遇到了載入的圖片無法顯示或者是問號的問題。下面就分析下為什麼會出現這樣的情況。   首先我們直接上程式碼(比較簡單) using U

android-Picasso請求https載入網路圖片不能顯示的解決方案

Picasso載入https的圖片載入不出來的解決方案 最近專案中有需求–要顯示https連結的圖片,但是配置好路徑後原生的Picasso死活沒法加載出圖片,最後在網上找到了如下的解決辦法 確認依賴 compile 'com.squareup.ok

img標籤src引用網路圖片,頁面不顯示,返回403錯誤,網路圖片地址在瀏覽器能載入,放html卻不能顯示

在html頁面加入<meta name="referrer" content="no-referrer">標籤,就可以解決頁面載入網路圖片的問題,原因大概是網路安全的問題,別人的頁面做了安全防護的問題。<head> <meta chars

IOS開發之非同步載入網路圖片並快取本地實現瀑布(一)

</pre><pre name="code" class="objc"></pre><pre name="code" class="objc">在前面的一篇部落格中,我寫了一個瀑布流照片牆的程式,由於之前的程式載入的圖片是本

SDWebImage 載入網路圖片不能顯示

iOS9讓所有的HTTP預設使用了HTTPS,原來的HTTP協議傳輸都改成TLS1.2協議進行傳輸。直接造成的情況就是App發請求的時候彈出網路無法連線。解決辦法就是在專案的info.plist 檔案里加上如下節點: 在info.plist檔案中加入此節點就可以加載

Android----高德地圖多個Marker載入網路圖片出現圖片顯示問題

需求說要地圖上展示的是mark,不是infowindow,加載出網路圖片,由於app的marker比較多,沒有達到需要的效果。 看了看高德地圖api中有這個方法 markerOption.icon(BitmapDescriptorFactory.fromBitmap(Bi

載入網路圖片顯示的轉圈效果及載入成功前與失敗後所顯示的圖示

MainActivity的一個呼叫方法: //網路圖片下載 private void downLoadFile(final String url, final String target, final String fileName) {

Android中通過Picasso來載入網路圖片,並通過ListView顯示出來。

在使用之前需要將Picasso的jar包匯入。 MainActivity程式碼: import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.o

vue實現網路圖片瀑布 + 下拉重新整理 + 上拉載入更多

一、思路分析和效果圖   用vue來實現一個瀑布流效果,載入網路圖片,同時有下拉重新整理和上拉載入更多功能效果。然後針對這幾個效果的實現,捋下思路: 根據載入資料的順序,依次追加標籤展示效果; 選擇哪種方式實現瀑布流,這裡選擇絕對定位方式; 關鍵問題:由於每張圖片的寬高不一樣,而瀑布流中要求所有圖

瀑布載入圖片

這個效果大家見過很多了 在百度搜索圖片的時候 下面補上我學習的製作這個的過程 當然 圖片是從別的地方抓取的 或者在資料庫中讀取的 我這裡只是放在了一個json裡面 作為表示。廢話不多說 上程式碼 css部分 <title>瀑布流效果</title> &l

canvas 載入網路圖片遇到的問題

<canvas id="canvas" width="800" height="500">抱歉,您的瀏覽器不支援canvas元素</canvas> 之前是這樣寫的 var img = new Image();//表示嵌入一個影象物件例項 img.src = thi

Android之Volley框架載入網路圖片

更多幹貨 分散式實戰(乾貨) spring cloud 實戰(乾貨) mybatis 實戰(乾貨) spring boot 實戰(乾貨) React 入門實戰(乾貨) 構建中小型網際網路企業架構(乾貨) python 學習持續更

C++ QT 載入網路圖片、本地圖片

原始碼下載:C++_QT 載入圖片 QTShowImage.cpp #include "QtShowImage.h" #include <QMovie> #include <QNetworkAccessManager> #include <QUrl> #i

Volley網路框架之快取載入圖片、Post與get的資料請求

         前言:Volley作為主流網路框架之一,必然有它的優點。Volley可是說是把AsyncHttpClient和Universal-Image-Loader的優點集於了一身, 它的常用在資料量不大,但網路通訊頻繁,而且有圖片快取

QQ 玩一玩獲取使用者影象暱稱以及CocosCreator動態載入網路圖片

文章目錄 1、CocosCreator 載入圖片的幾種方式 2、QQ 玩一玩通過openId獲取使用者影象、暱稱 QQ 玩一玩獲取使用者影象、暱稱以及CocosC

微信小程式cavans.drawImage方法無法載入網路圖片

微信小程式cavans.drawImage方法無法載入網路圖片 canvas.drawImage(圖片地址, 起始x座標, 起始y座標, 圖片高, 圖片寬)  圖片地址src不能為網路地址:http://www.域名.com/專案/123.jpg  如果

需要載入網路圖片的時候我們在adapter繫結資料裡面解析等到圖片的url

public void BindData(UserBean.DataBean date){ title.setText(date.getTitle()); data.setText(date.getPrice()+""); String R = ""; i

使用OKhttp載入網路上的圖片

public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button btnOK; private ImageView imgShow; privat