0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

OpenHarmony輕松玩轉(zhuǎn)GIF數(shù)據(jù)渲染

OpenAtom OpenHarmony ? 來源:未知 ? 2022-10-18 22:15 ? 次閱讀

點擊藍(lán)字 ╳ 關(guān)注我們

開源項目 OpenHarmony是每個人的 OpenHarmony 9da11e1c-4eee-11ed-a3b6-dac502259ad0.png

周黎生

OpenHarmony知識體系工作組

以下內(nèi)容來自嘉賓分享,不代表開放原子開源基金會觀點 OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)提供了Image組件支持GIF動圖的播放,但是缺乏擴(kuò)展能力,不支持播放控制等。今天介紹一款三方庫——ohos-gif-drawable三方組件,帶大家一起玩轉(zhuǎn)GIF的數(shù)據(jù)渲染,搞定GIF動圖的各種需求。

效果演示

9dc7e150-4eee-11ed-a3b6-dac502259ad0.gif ?9ec28592-4eee-11ed-a3b6-dac502259ad0.png ? 本文將從5個小節(jié)來帶領(lǐng)大家使用ohos-gif-drawable這一款三方庫,其中1、2、3這3個小節(jié),主要介紹了ohos-gif-drawable的核心能力、GIF軟解碼和GIF繪制。4和5小節(jié)主要是擴(kuò)展討論,如何添加濾鏡效果和軟解碼遇到的耗時問題。 ?9ee3461a-4eee-11ed-a3b6-dac502259ad0.png ?

1.GIF的文件格式理論基礎(chǔ)

工欲善其事必先利其器。首先我們需要為自己打下理論基礎(chǔ)。了解GIF的數(shù)據(jù)格式,為后續(xù)解碼GIF提供理論支持。 9efd9376-4eee-11ed-a3b6-dac502259ad0.png ? 通過學(xué)習(xí)GIF的文件格式,我們對于GIF的組成格式有了一定的了解,并且有助于理解后面GIF的解碼。 在開始介紹之前,我想讓大家了解一下整體的結(jié)構(gòu)思路如下圖: ?9f396cca-4eee-11ed-a3b6-dac502259ad0.jpg ? 其中g(shù)ifuct-js三方庫主要完成了解碼的工作。 ohos-gif-drawable三方庫則是在gifuct-js的三方庫之上,進(jìn)行了封裝。并結(jié)合了OpenHarmony的Canvas繪制能力,達(dá)到了播放和控制GIF的能力。 ?

2.GIF軟解碼:gifuct-js三方庫介紹

GIF解碼我們使用了gifuct-js這個庫,它是一個純JavaScript的GIF解碼庫。首先我們需要了解基礎(chǔ)用法。 2.1 參考樣例將一個文件ArrayBuffer轉(zhuǎn)換為GIF解碼后的幀數(shù)據(jù)數(shù)組。
//javascript
var gif = parseGIF(arraybuffer)
varframes=decompressFrames(gif,true)
2.2 由于OpenHarmony的Image生成PixelMap需要的數(shù)據(jù)是BGRA數(shù)據(jù),而2.1生成的frames所有數(shù)組中的patch字段則是RGBA數(shù)據(jù),所以我們需要使用
//javascript
var gif = parseGIF(arraybuffer)
varframes=decompressFrames(gif,false)
然后將frame目前還未生成的patch字段數(shù)據(jù),通過generatePatch 函數(shù),將RGBA的數(shù)據(jù)更換為BGRA即可,如下代碼所示:
//javascript
const generatePatch = image => {
  const totalPixels = image.pixels.length
  const patchData = new Uint8ClampedArray(totalPixels * 4)
  for (var i = 0; i < totalPixels; i++) {
    const pos = i * 4
    const colorIndex = image.pixels[i]
    const color = image.colorTable[colorIndex] || [0, 0, 0]
    patchData[pos] = color[2] // B
    patchData[pos + 1] = color[1]// G
    patchData[pos + 2] = color[0] // R
    patchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0//A
  }
  return patchData
}
generatePatch函數(shù),在這里會根據(jù)顏色表colorTable和基于顏色表的圖像數(shù)據(jù)pixels以及透明度transparentIndex生成BGRA格式的patchData,這個數(shù)據(jù)和Canvas中g(shù)etImageData獲取的ImageData數(shù)據(jù)是一致的,都是Uint8ClampedArray類型,可以直接使用putImageData讓canvas繪制 最后,生成的patchData賦值給Frame的patch字段。 這里我們并沒有直接使用Canvas的putImageData直接繪制。為了提升擴(kuò)展性,我們使用了Image的能力來生成PixelMap,這樣處理為后續(xù)濾鏡效果提供了可能,也方便后續(xù)繪制流程。 好了,到這里我們就基本上把gifuct-js庫的基礎(chǔ)使用簡單介紹完了。 如何使用GIF:ohos-gif-drawable三方庫的介紹 我們先來看看整個ohos-gif-drawable組件的模型圖,通過模型圖,我們可以看到,用戶只要關(guān)注GIFComponent組件,和GIFComponent.ControllerOptions配置參數(shù)以及控制參數(shù)autoPlay和resetGif即可,非常簡單! 9f50ce6a-4eee-11ed-a3b6-dac502259ad0.jpg ?1. 支持的功能列表如下 ● 支持播放GIF圖片。 ● 支持控制GIF播放/暫停。 ● 支持重置GIF播放動畫。 ● 支持調(diào)節(jié)GIF播放速率。 ● 支持監(jiān)聽GIF所有幀顯示完成后的回調(diào)。 ● 支持設(shè)置顯示大小。 ● 支持7種不同的展示類型。 ● 支持設(shè)置顯示區(qū)域背景顏色。 2. 如何使用ohos-gif-drawable 首先需要使用npm下載ohos-gif-drawable三方庫
npminstall@ohos/ohos-gif-drawable--save
接下來我們需要配置一個worker給gifuct-js解碼使用 配置worker,在應(yīng)用工程的entry/src/main/ets/pages目錄下新建workers文件夾,并且創(chuàng)建文件 gifParseWorker.ts ,文件內(nèi)容如下:
import arkWorker from '@ohos.worker';
import { handler } from '@ohos/ohos-gif-drawable/src/main/ets/components/gif/worker/GifWorker'
//handler封裝了子線程邏輯,但worker目前只能在entry中進(jìn)行創(chuàng)建arkWorker.parentPort.onmessage=handler;
然后在entry目錄的build-profile.json5文件中,添加如下內(nèi)容:
"buildOption": {  
"sourceOption": {    
"workers": [     
       "./src/main/ets/pages/workers/gifParseWorker.ts"
]  
}
},
到這里我們worker就配置好了。 下面就到了正式使用環(huán)節(jié),我們只要在UI界面需要的地方寫上自定義控件GIFComponent, 然后傳入GIFComponent.ControllerOptions,gifAutoPlay,gifReset這三個參數(shù)就能控制gif動畫。
import { GIFComponent, ResourceLoader } from '@ohos/ohos-gif-drawable'
// gif繪制組件用戶屬性設(shè)置
@State model:GIFComponent.ControllerOptions = new GIFComponent.ControllerOptions();
// 是否自動播放
@State gifAutoPlay:boolean = true;
// 重置GIF播放,每次取反都能生效
@State gifReset:boolean = true;
// 在ARKUI的其他容器組件中添加該組件
GIFComponent({model:$model,autoPlay:$gifAutoPlay,resetGif:this.gifReset})
舉個簡單的例子說明一下
// 創(chuàng)建worker 
let worker = new ArkWorker.Worker('entry/ets/pages/workers/gifParseWorker.ts', {type: 'classic',name: 'loadUrlByWorker'})
// 關(guān)閉動畫      
this.gifAutoPlay = false;
// 銷毀上一次資源
this.model.destroy();
// 新創(chuàng)建一個modelx,用于配置用戶參數(shù)
let modelx = new GIFComponent.ControllerOptions()
modelx  
// 配置回調(diào)動畫結(jié)束監(jiān)聽,和耗時監(jiān)聽    
.setLoopFinish((loopTime) => {   
this.gifLoopCount++;   
this.loopHint = '當(dāng)前gif循環(huán)了' + this.gifLoopCount + '次,耗時=' + loopTime + 'ms'   
})  
// 設(shè)置組件大小    
.setSize({ width: this.compWidth, height: this.compHeight })  
// 設(shè)置圖像和組件的適配類型  
.setScaleType(this.scaleType)  
// 設(shè)置播放速率  
.setSpeedFactor(this.speedFactor)  
// 設(shè)置背景  
.setBackgroundColor(Color.Grey)
// 加載網(wǎng)絡(luò)圖片,getContext(this)中的this指向page頁面或者組件都可以ResourceLoader.downloadDataWithContext(getContext(this), {   url: 'https://pic.ibaotu.com/gif/18/17/16/51u888piCtqj.gif!fwpaa70/fw/700'   }, (sucBuffer) => {    
// 網(wǎng)絡(luò)資源sucBuffer返回后處理   
modelx.loadBuffer(sucBuffer, () => {      console.log('網(wǎng)絡(luò)加載解析成功回調(diào)繪制!')    
// 開啟自動播放      
this.gifAutoPlay = true;    
// 給組件數(shù)據(jù)賦新的用戶配置參數(shù),達(dá)到后續(xù)gif動畫效果      
this.model = modelx;   }, worker)}, (err) => {   
// 用戶根據(jù)返回的錯誤信息,進(jìn)行業(yè)務(wù)處理(展示一張失敗占位圖、再次加載一次、加載其他圖片等)
})
這里ResourceLoader內(nèi)置了加載網(wǎng)絡(luò)資源GIF,本地工程資源GIF和本地路徑資源GIF文件數(shù)據(jù)的能力。 如果你已經(jīng)有了GIF文件的arraybuffer數(shù)據(jù),也可以直接調(diào)用modelx.loadBuffer(buffer: ArrayBuffer, readyRender: (err?) => void, worker: any)進(jìn)行GIF播放。 甚至你已經(jīng)生成了GIF解析數(shù)據(jù),比如調(diào)用了2.2中的解碼代碼,那么你也可以直接調(diào)用modelx.setFrames(images?: GIFFrame[])來進(jìn)行g(shù)if播放 1.控制GIF的播放與暫停:
this.gifAutoPlay = true 開啟動畫
this.gifAutoPlay=false暫停動畫
組件內(nèi)部會監(jiān)聽該參數(shù)的變化,用戶只要改變值即可達(dá)到控制效果 2.重置GIF的播放
this.gifReset=!this.gifReset 每次變化都會重置gif播放。
由于重置不需要狀態(tài)管理,所以組件內(nèi)監(jiān)聽到數(shù)據(jù)變化就會重置gif播放 3.設(shè)置GIF動畫播放速度
let modelx = new GIFComponent.ControllerOptions()
modelx.setSpeedFactor(2)//將速率提升到2倍
調(diào)用setSpeedFactor(speed: number)即可調(diào)整播放速度speed 為對比原始速率的乘積因子,比如設(shè)置0.5即為原始速率的0.5倍,設(shè)置為2即為原始速率的2倍 4.監(jiān)聽GIF動畫播放回調(diào)(比如第一次動畫結(jié)束)和獲取動畫實際播放總時長
let modelx = new GIFComponent.ControllerOptions()
modelx.setLoopFinish((loopTime?) => {
// loopTime為GIF動畫一周期耗時,回調(diào)時間為GIF動畫一周期結(jié)束時間節(jié)點
})
調(diào)用setLoopFinish(fn: (loopTime?) => void)可以通過回調(diào)得到GIF動畫運行一周期耗時和一周期結(jié)束時間節(jié)點 5.顯示GIF任意一幀
let modelx = new GIFComponent.ControllerOptions()
modelx.setSeekTo(5)//直接展示該gif第5幀圖像
調(diào)用setSeekTo(gifPosition: number)可以直接展示該gif的某一幀圖像 到這里ohos-gif-drawable三方庫的主要能力都介紹完了,是不是很簡單呢! 6.適配組件的大小
letmodelx=newGIFComponent.ControllerOptions()
modelx.setScaleType(ScaleType.FIT_CENTER) // 將圖像縮放適配組件大小 調(diào)用setScaleType(scaletype: ScaleType)可以將圖像和組件大小進(jìn)行適配。 目前支持的類型如下圖所示:GIFComponent.ScaleType 9f7d7884-4eee-11ed-a3b6-dac502259ad0.png ?

為什么要配置worker

在具體實踐過程中我們會發(fā)現(xiàn),當(dāng)我們按下解碼按鈕的時候,主界面會有一點卡頓的情況。特別是大的GIF文件進(jìn)行解碼的時候效果更明顯。這是因為我們在主線程中進(jìn)行了CPU的密集型計算,這是一個耗時且占用CPU的操作。主線程中是不能執(zhí)行耗時操作的。但是JavaScript只有一個線程?。磕敲唇獯a這一塊操作該如何處理會比較好呢?帶著疑惑,我去查閱了資料發(fā)現(xiàn)JavaScript雖然屬于單線程環(huán)境。但是通過引入Worker的能力,引入子線程worker,可以實現(xiàn)JavaScript的“多線程”技術(shù)。

OpenHarmony如何在子線程中處理耗時任務(wù)

為了爭取良好的用戶體驗,我們需要將耗時操作封裝至子線程中。 這里簡單描述一下worker的能力: 能夠讓主頁面運行的JavaScript線程中加載運行另外單獨的一個或者多個JavaScript線程,但是它的多線程編程能力區(qū)別于傳統(tǒng)意義上的多線程編程。主線程和Worker線程之間,不會共享任何作用域和資源,他們的通信方式是基于事件監(jiān)聽機(jī)制的 message。 接下來我們參考OpenHarmony文檔下的worker能力 1.OpenHarmony環(huán)境下Worker的API接口列表 2.Worker的使用簡單案例 經(jīng)過了解之后,我們可以把解碼的耗時封裝到worker中處理,避免主線程耗時操作占用CPU導(dǎo)致卡頓問題。提升用戶體驗。 這也是使用ohos-gif-drawable三方庫需要配置worker的原因。

擴(kuò)展部分

GIF的濾鏡效果 1. 灰白濾鏡
//javascript
// 重點代碼更改  
  let avg = (color[0] + color[1] + color[2]) / 3
  patchData[pos] = avg;
  patchData[pos + 1] = avg;
  patchData[pos + 2] = avg;
patchData[pos+3]=colorIndex!==image.transparentIndex?255:0;
2. 反轉(zhuǎn)濾鏡
//javascript
// 重點代碼更改
  patchData[pos] = 255 - color[0];
  patchData[pos + 1] = 255 - color[1];
  patchData[pos + 2] = 255 - color[2];
patchData[pos+3]=colorIndex!==image.transparentIndex?255:0;
3. 高級濾鏡效果 假設(shè)我們這邊已經(jīng)拿到了patch: Uint8ClampedArray像素數(shù)據(jù),這里我需要先將其變換為一張PixelMap數(shù)據(jù),參考GIFComponent中patch數(shù)據(jù)轉(zhuǎn)換為PixelMap的代碼。
//typescript
import image from "@ohos.multimedia.image"
let colorBuffer = patch.buffer
let pixelmap = await image.createPixelMap(colorBuffer, {
  'size': {
    'height': frame.dims.height as number,
    'width': frame.dims.width as number
  }
})
4. 高斯模糊 然后對PixelMap像素數(shù)據(jù)進(jìn)行高斯模糊, 調(diào)用 `blur(pixelmap,10,true, (outPixelMap)=>{ // 模糊后的pixelmap數(shù)據(jù)})`在回調(diào)中獲取模糊后的pixelmap。以下是模糊處理的算法
export async function blur(bitmap: any, radius: number, canReuseInBitmap: boolean, func: AsyncTransform) {
  if (radius < 1) {
    func("error,radius must be greater than 1 ", null);
    return;
  }


  let imageInfo = await bitmap.getImageInfo();
  let size = {
    width: imageInfo.size.width,
    height: imageInfo.size.height
  }


  if (!size) {
    func(new Error("fastBlur The image size does not exist."), null)
    return;
  }


  let w = size.width;
  let h = size.height;
  var pixEntry: Array = new Array()
  var pix: Array = new Array()




  let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());
  await bitmap.readPixelsToBuffer(bufferData);
  let dataArray = new Uint8Array(bufferData);


  for (let index = 0; index < dataArray.length; index+=4) {
    const r = dataArray[index];
    const g = dataArray[index+1];
    const b = dataArray[index+2];
    const f = dataArray[index+3];


    let entry = new PixelEntry();
    entry.a = 0;
    entry.b = b;
    entry.g = g;
    entry.r = r;
    entry.f = f;
    entry.pixel = ColorUtils.rgb(entry.r, entry.g, entry.b);
    pixEntry.push(entry);
    pix.push(ColorUtils.rgb(entry.r, entry.g, entry.b));
  }


  let wm = w - 1;
  let hm = h - 1;
  let wh = w * h;
  let div = radius + radius + 1;


  let r = CalculatePixelUtils.createIntArray(wh);
  let g = CalculatePixelUtils.createIntArray(wh);
  let b = CalculatePixelUtils.createIntArray(wh);


  let rsum, gsum, bsum, x, y, i, p, yp, yi, yw: number;
  let vmin = CalculatePixelUtils.createIntArray(Math.max(w, h));


  let divsum = (div + 1) >> 1;
  divsum *= divsum;
  let dv = CalculatePixelUtils.createIntArray(256 * divsum);
  for (i = 0; i < 256 * divsum; i++) {
    dv[i] = (i / divsum);
  }


  yw = yi = 0;
  let stack = CalculatePixelUtils.createInt2DArray(div, 3);
  let stackpointer, stackstart, rbs, routsum, goutsum, boutsum, rinsum, ginsum, binsum: number;
  let sir: Array;
  let r1 = radius + 1;
  for (y = 0; y < h; y++) {
    rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
    for (i = -radius; i <= radius; i++) {
      p = pix[yi + Math.min(wm, Math.max(i, 0))];
      sir = stack[i + radius];
      sir[0] = (p & 0xff0000) >> 16;
      sir[1] = (p & 0x00ff00) >> 8;
      sir[2] = (p & 0x0000ff);
      rbs = r1 - Math.abs(i);
      rsum += sir[0] * rbs;
      gsum += sir[1] * rbs;
      bsum += sir[2] * rbs;
      if (i > 0) {
        rinsum += sir[0];
        ginsum += sir[1];
        binsum += sir[2];
      } else {
        routsum += sir[0];
        goutsum += sir[1];
        boutsum += sir[2];
      }
    }
    stackpointer = radius;


    for (x = 0; x < w; x++) {


      r[yi] = dv[rsum];
      g[yi] = dv[gsum];
      b[yi] = dv[bsum];


      rsum -= routsum;
      gsum -= goutsum;
      bsum -= boutsum;


      stackstart = stackpointer - radius + div;
      sir = stack[stackstart % div];


      routsum -= sir[0];
      goutsum -= sir[1];
      boutsum -= sir[2];


      if (y == 0) {
        vmin[x] = Math.min(x + radius + 1, wm);
      }
      p = pix[yw + vmin[x]];


      sir[0] = (p & 0xff0000) >> 16;
      sir[1] = (p & 0x00ff00) >> 8;
      sir[2] = (p & 0x0000ff);


      rinsum += sir[0];
      ginsum += sir[1];
      binsum += sir[2];


      rsum += rinsum;
      gsum += ginsum;
      bsum += binsum;


      stackpointer = (stackpointer + 1) % div;
      sir = stack[(stackpointer) % div];


      routsum += sir[0];
      goutsum += sir[1];
      boutsum += sir[2];


      rinsum -= sir[0];
      ginsum -= sir[1];
      binsum -= sir[2];


      yi++;
    }
    yw += w;
  }
  for (x = 0; x < w; x++) {
    rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
    yp = -radius * w;
    for (i = -radius; i <= radius; i++) {
      yi = Math.max(0, yp) + x;


      sir = stack[i + radius];


      sir[0] = r[yi];
      sir[1] = g[yi];
      sir[2] = b[yi];


      rbs = r1 - Math.abs(i);


      rsum += r[yi] * rbs;
      gsum += g[yi] * rbs;
      bsum += b[yi] * rbs;


      if (i > 0) {
        rinsum += sir[0];
        ginsum += sir[1];
        binsum += sir[2];
      } else {
        routsum += sir[0];
        goutsum += sir[1];
        boutsum += sir[2];
      }


      if (i < hm) {
        yp += w;
      }
    }
    yi = x;
    stackpointer = radius;
    for (y = 0; y < h; y++) {
      // Preserve alpha channel: ( 0xff000000 & pix[yi] )
      pix[yi] = (0xff000000 & pix[Math.round(yi)]) | (dv[Math.round(rsum)] << 16) | (dv[
      Math.round(gsum)] << 8) | dv[Math.round(bsum)];


      rsum -= routsum;
      gsum -= goutsum;
      bsum -= boutsum;


      stackstart = stackpointer - radius + div;
      sir = stack[stackstart % div];


      routsum -= sir[0];
      goutsum -= sir[1];
      boutsum -= sir[2];


      if (x == 0) {
        vmin[y] = Math.min(y + r1, hm) * w;
      }
      p = x + vmin[y];


      sir[0] = r[p];
      sir[1] = g[p];
      sir[2] = b[p];


      rinsum += sir[0];
      ginsum += sir[1];
      binsum += sir[2];


      rsum += rinsum;
      gsum += ginsum;
      bsum += binsum;


      stackpointer = (stackpointer + 1) % div;
      sir = stack[stackpointer];


      routsum += sir[0];
      goutsum += sir[1];
      boutsum += sir[2];


      rinsum -= sir[0];
      ginsum -= sir[1];
      binsum -= sir[2];


      yi += w;
    }
  }


  let bufferNewData = new ArrayBuffer(bitmap.getPixelBytesNumber());
  let dataNewArray = new Uint8Array(bufferNewData);
  let index = 0;


  for (let i = 0; i < dataNewArray.length; i += 4) {
    dataNewArray[i] = ColorUtils.red(pix[index]);
    dataNewArray[i+1] = ColorUtils.green(pix[index]);
    dataNewArray[i+2] = ColorUtils.blue(pix[index]);
    dataNewArray[i+3] = pixEntry[index].f;
    index++;
  }
  await bitmap.writeBufferToPixels(bufferNewData);
  if (func) {
    func("success", bitmap);
  }
}
如果需要高級濾鏡效果可以參考ImageKnife組件的transform部分,這里僅僅展示模糊效果。 由于濾鏡效果目前ohos-gif-drawable三方庫并沒有開發(fā)接口提供出來,所以開發(fā)者可以根據(jù)實際需求重寫自定義組件GIFComponent.,只需要在生成PixelMap的代碼片段中加入濾鏡代碼,即可利用濾鏡效果開發(fā)更多精彩的應(yīng)用。

參考資料

1.《GIF文件格式解析》

https://segmentfault.com/a/1190000022866045

2.GIF解碼庫gifuct-js

https://github.com/matt-way/gifuct-js

3.GIF解碼庫底層邏輯jsBinarySchemaParser

https://github.com/matt-way/jsBinarySchemaParser

4.高級濾鏡算法借鑒

https://gitee.com/openharmony-tpc/ImageKnife/tree/master/imageknife/src/main/ets/components/imageknife/transform

5.OpenHarmony環(huán)境下Worker的API接口列表

https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-worker.md

6.Worker的使用簡單案例

https://gitee.com/wang_zhaoyong/js_worker_module/wikis/Worker%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8

7.Web Worker API參考

https://developer.mozilla.org/zh-CN/docs/Web/API/Worker

8.OpenHarmony的Canvas文檔

https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/arkui-ts/ts-components-canvas-canvas.md

9.OpenHarmony的CanvasRenderingContext2D對象文檔

https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/arkui-ts/ts-canvasrenderingcontext2d.md


原文標(biāo)題:OpenHarmony輕松玩轉(zhuǎn)GIF數(shù)據(jù)渲染

文章出處:【微信公眾號:OpenAtom OpenHarmony】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • OpenHarmony
    +關(guān)注

    關(guān)注

    25

    文章

    3607

    瀏覽量

    15956

原文標(biāo)題:OpenHarmony輕松玩轉(zhuǎn)GIF數(shù)據(jù)渲染

文章出處:【微信號:gh_e4f28cfa3159,微信公眾號:OpenAtom OpenHarmony】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    OpenHarmony Sheet 表格渲染引擎

    基于 Canvas 實現(xiàn)的高性能 Excel 表格引擎組件 [OpenHarmonySheet]。 由于大部分前端項目渲染層是使用框架根據(jù)排版模型樹結(jié)構(gòu)逐層渲染的,整棵渲染樹也是與排版
    發(fā)表于 01-05 16:32

    輕松玩轉(zhuǎn)AVR C語言 資料很好

    輕松玩轉(zhuǎn)AVR資料很好
    發(fā)表于 07-26 22:44

    第13章 GIF圖片顯示

    Service開發(fā)出了GIF文件格式(圖形交換格式)。 它設(shè)計用于跨數(shù)據(jù)網(wǎng)絡(luò)傳輸圖像。GIF標(biāo)準(zhǔn)支持隔行掃描、透明、應(yīng)用定義數(shù)據(jù)、動畫以及原始文本
    發(fā)表于 10-13 08:18

    如何輕松玩轉(zhuǎn)adc?

    如何輕松玩轉(zhuǎn)adc?
    發(fā)表于 01-21 06:28

    OpenHarmony小型系統(tǒng)有什么方案可以加載gif的圖片?

    OpenHarmony 小型系統(tǒng)中提供基礎(chǔ)組件image,但是只能加載png或者jpg類型的圖片,有什么方案可以加載gif的圖片?
    發(fā)表于 04-19 10:13

    OpenHarmony輕松玩轉(zhuǎn)GIF數(shù)據(jù)渲染

    OpenHarmony輕松玩轉(zhuǎn)GIF數(shù)據(jù)渲染OpenAtom
    發(fā)表于 10-20 10:57

    OpenHarmony 3.2 Beta Audio——音頻渲染

    └── audio_server.cpp三、音頻渲染總體流程四、Native接口使用在OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)系統(tǒng)中,音頻模塊提供了功能測試代碼,本文
    發(fā)表于 03-02 14:28

    HarmonyOS/OpenHarmony應(yīng)用開發(fā)-ArkTS語言渲染控制概述

    渲染控制語句包括控制組件是否顯示的條件渲染語句,基于數(shù)組數(shù)據(jù)快速生成組件的循環(huán)渲染語句以及針對大數(shù)據(jù)量場景的
    發(fā)表于 08-09 09:54

    零死角輕松玩轉(zhuǎn)stm32下載

    《零死角輕松玩轉(zhuǎn) STM32》 系列教程由初級篇、 中級篇、 高級篇、 系統(tǒng)篇、 四個部分組成,根據(jù)野火 STM32 開發(fā)板舊版教程升級而來,且經(jīng)過重新深入編 寫,重新排版,更適合初學(xué)者
    發(fā)表于 04-28 14:23 ?0次下載

    GIF Decoder

    GIF Decoder GIF Decoder GIF Decoder GIF Decoder
    發(fā)表于 05-24 10:53 ?2次下載

    輕松玩轉(zhuǎn)STM32Cube資料包下載(上)

    輕松玩轉(zhuǎn)STM32Cube資料包下載(上)
    發(fā)表于 09-28 09:39 ?0次下載

    輕松玩轉(zhuǎn)STM32Cube資料包下載(2)

    輕松玩轉(zhuǎn)STM32Cube資料包下載(2)
    發(fā)表于 09-28 09:41 ?0次下載

    輕松玩轉(zhuǎn)STM32Cube資料包下載(下)

    輕松玩轉(zhuǎn)STM32Cube資料包下載(下)
    發(fā)表于 09-28 09:46 ?0次下載

    輕松玩轉(zhuǎn)AVR單片機(jī)C語言源代碼

    輕松玩轉(zhuǎn)AVR單片機(jī)C語言圖書的配套源代碼資料分享。
    發(fā)表于 04-20 16:17 ?17次下載

    OpenHarmony 3.2 Beta Audio——音頻渲染

    點擊藍(lán)字 ╳ 關(guān)注我們 開源項目 OpenHarmony 是每個人的 OpenHarmony 巴延興 深圳開鴻數(shù)字產(chǎn)業(yè)發(fā)展有限公司 資深OS框架開發(fā)工程師 一、簡介 Audio是多媒體子系統(tǒng)中的一個
    的頭像 發(fā)表于 03-11 16:40 ?632次閱讀