1.新增gif解析操作
Signed-off-by: zhoulisheng1 <zhoulisheng1@huawei.com>
This commit is contained in:
parent
82ad36f241
commit
80f912ba60
|
@ -15,14 +15,15 @@
|
|||
import {ImageKnifeComponent} from '@ohos/imageknife'
|
||||
import {ImageKnifeOption} from '@ohos/imageknife'
|
||||
import {RotateImageTransformation} from '@ohos/imageknife'
|
||||
|
||||
import ArkWorker from '@ohos.worker'
|
||||
@Entry
|
||||
@Component
|
||||
struct TestGifDontAnimatePage {
|
||||
private globalGifWorker:any = undefined
|
||||
@State imageKnifeOption1: ImageKnifeOption =
|
||||
{
|
||||
loadSrc: $r('app.media.jpgSample'),
|
||||
size: { width: 300, height: 300 },
|
||||
size: { width: '300', height: '300' },
|
||||
placeholderSrc: $r('app.media.icon_loading'),
|
||||
errorholderSrc: $r('app.media.icon_failed'),
|
||||
margin:{left:15,top:15,right:15,bottom:15}
|
||||
|
@ -36,8 +37,8 @@ struct TestGifDontAnimatePage {
|
|||
Button('本地资源gif')
|
||||
.onClick(() => {
|
||||
this.imageKnifeOption1 = {
|
||||
loadSrc: $r('app.media.gifSample'),
|
||||
size: { width: 300, height: 300 },
|
||||
loadSrc: $r('app.media.testGif2'),
|
||||
size: { width: '300', height: '300' },
|
||||
placeholderSrc: $r('app.media.icon_loading'),
|
||||
errorholderSrc: $r('app.media.icon_failed'),
|
||||
margin: { left: 15, top: 15, right: 15, bottom: 15 }
|
||||
|
@ -48,7 +49,7 @@ struct TestGifDontAnimatePage {
|
|||
.onClick(()=>{
|
||||
this.imageKnifeOption1 = {
|
||||
loadSrc: $r('app.media.gifSample'),
|
||||
size: { width: 300, height: 300 },
|
||||
size: { width: '300', height: '300' },
|
||||
placeholderSrc: $r('app.media.icon_loading'),
|
||||
errorholderSrc: $r('app.media.icon_failed'),
|
||||
margin:{left:15,top:15,right:15,bottom:15},
|
||||
|
@ -63,7 +64,7 @@ struct TestGifDontAnimatePage {
|
|||
.onClick(()=>{
|
||||
this.imageKnifeOption1 = {
|
||||
loadSrc: 'https://pic.ibaotu.com/gif/18/17/16/51u888piCtqj.gif!fwpaa70/fw/700',
|
||||
size: { width: 300, height: 300 },
|
||||
size: { width: '300', height: '300' },
|
||||
placeholderSrc: $r('app.media.icon_loading'),
|
||||
errorholderSrc: $r('app.media.icon_failed'),
|
||||
margin:{left:15,top:15,right:15,bottom:15}
|
||||
|
@ -73,7 +74,7 @@ struct TestGifDontAnimatePage {
|
|||
.onClick(()=>{
|
||||
this.imageKnifeOption1 = {
|
||||
loadSrc: 'https://pic.ibaotu.com/gif/18/17/16/51u888piCtqj.gif!fwpaa70/fw/700',
|
||||
size: { width: 300, height: 300 },
|
||||
size: { width: '300', height: '300' },
|
||||
placeholderSrc: $r('app.media.icon_loading'),
|
||||
errorholderSrc: $r('app.media.icon_failed'),
|
||||
margin:{left:15,top:15,right:15,bottom:15},
|
||||
|
@ -91,8 +92,16 @@ struct TestGifDontAnimatePage {
|
|||
}
|
||||
|
||||
aboutToAppear() {
|
||||
console.log('aboutToAppear()')
|
||||
this.globalGifWorker = new ArkWorker.Worker('entry/ets/pages/workers/gifParseWorker.ts', {
|
||||
type: 'classic',
|
||||
name: 'ImageKnifeParseGIF'
|
||||
})
|
||||
globalThis.ImageKnife.setGifWorker(this.globalGifWorker)
|
||||
}
|
||||
aboutToDisappear(){
|
||||
if(this.globalGifWorker){
|
||||
this.globalGifWorker.terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ImageKnife = globalThis.ImageKnife
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import arkWorker from '@ohos.worker';
|
||||
import { gifHandler } from '@ohos/imageknife/src/main/ets/components/imageknife/utils/gif/worker/GifWorker'
|
||||
|
||||
arkWorker.parentPort.onmessage = gifHandler;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export class GIFFrame {
|
||||
|
||||
// 显示帧 width 宽 height 高 top上边距 left左边距
|
||||
dims: {
|
||||
width: number;
|
||||
height: number;
|
||||
top: number;
|
||||
left: number
|
||||
}
|
||||
|
||||
// 当前帧的像素数据指向的颜色数组 只为了生成patch,非必要
|
||||
colorTable?: [number, number, number][]
|
||||
|
||||
// 当前帧到下一帧的间隔时长
|
||||
delay: number
|
||||
|
||||
// 当前帧绘制要求 0保留 1在上一帧绘制此帧 2恢复画布背景 3.将画布恢复到绘制当前图像之前的先前状态
|
||||
disposalType: number
|
||||
|
||||
// Uint8CampedArray颜色转换后的补片信息用于绘制 必要
|
||||
patch?: Uint8ClampedArray
|
||||
|
||||
// drawPixelMap 如果像素转换为PixelMap后使用PixelMap展示, patch和drawPixelMap 2选1
|
||||
drawPixelMap?
|
||||
|
||||
// 当前帧每个像素的颜色表查找索引 只为了生成patch,非必要
|
||||
pixels?: number[]
|
||||
|
||||
// 表示透明度的可选颜色索引
|
||||
transparentIndex: number
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { IParseGif } from './IParseGif'
|
||||
import { GIFFrame } from './GIFFrame'
|
||||
import { LoadType } from './worker/GifWorker'
|
||||
import { parseBufferToFrame } from './parse/GIFParse'
|
||||
import image from '@ohos.multimedia.image'
|
||||
|
||||
export class GIFParseImpl implements IParseGif {
|
||||
parseGifs(imageinfo: ArrayBuffer, callback: (data?, err?) => void, worker?,runMainThread?:boolean) {
|
||||
let resolveWorker = worker;
|
||||
console.log('parseGifs resolveWorker1 is null =' + (resolveWorker == null))
|
||||
if (!resolveWorker) {
|
||||
resolveWorker = globalThis.ImageKnife.getGifWorker();
|
||||
}
|
||||
console.log('parseGifs resolveWorker2 is null =' + (resolveWorker == null))
|
||||
|
||||
if (!!resolveWorker && !runMainThread) {
|
||||
console.log('parseGifs in worker thread!')
|
||||
this.useWorkerParse(resolveWorker, imageinfo, (data, err) => {
|
||||
if (err) {
|
||||
callback(undefined, err)
|
||||
} else {
|
||||
this.createPixelMapAll(data).then((pixelmaps) => {
|
||||
if (pixelmaps.length == data.length) {
|
||||
for (let i = 0;i < data.length; i++) {
|
||||
let frame = data[i];
|
||||
frame['drawPixelMap'] = pixelmaps[i];
|
||||
frame['patch'] = null;
|
||||
}
|
||||
callback(data, undefined)
|
||||
}
|
||||
}).catch(err => {
|
||||
callback(undefined, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('parseGifs in main thread!')
|
||||
let frames = parseBufferToFrame(imageinfo)
|
||||
console.log('frames length =' + frames.length)
|
||||
this.createPixelMapAll(frames).then((pixelmaps) => {
|
||||
if (pixelmaps.length == frames.length) {
|
||||
for (let i = 0;i < frames.length; i++) {
|
||||
let frame = frames[i];
|
||||
frame['drawPixelMap'] = pixelmaps[i];
|
||||
frame['patch'] = null;
|
||||
}
|
||||
console.log('parseGifs in main thread! callback is done!')
|
||||
callback(frames, undefined)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log('parseGifs in main thread! err =' + err)
|
||||
callback(undefined, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private createPixelMapAll(frames): Promise<PixelMap[]> {
|
||||
let promises = []
|
||||
let filterCriteria = (item) => {
|
||||
if (!item['drawPixelMap']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
frames.filter(filterCriteria, frames).flatMap((frame) => {
|
||||
promises.push(image.createPixelMap(frame.patch.buffer, {
|
||||
'size': {
|
||||
'height': frame.dims.height as number,
|
||||
'width': frame.dims.width as number
|
||||
}
|
||||
}))
|
||||
})
|
||||
return Promise.all(promises)
|
||||
}
|
||||
|
||||
private useWorkerParse(worker: any, buffer: ArrayBuffer, callback: (data?, err?) => void) {
|
||||
|
||||
worker.onerror = function (data) {
|
||||
callback(undefined, data)
|
||||
}
|
||||
|
||||
worker.onmessageerror = function (event) {
|
||||
callback(undefined, event)
|
||||
}
|
||||
|
||||
worker.onexit = function () {
|
||||
console.log('gifWork worker.onexit!')
|
||||
}
|
||||
|
||||
worker.onmessage = (e) => {
|
||||
var data = e.data;
|
||||
switch (data.type) {
|
||||
case LoadType.loadBufferByWorker:
|
||||
let pages = data.data;
|
||||
if (this.gifDecodeCorrect(pages)) {
|
||||
let images = this.recDecodedData(pages);
|
||||
callback(images, undefined)
|
||||
} else {
|
||||
callback(undefined, 'GIF Worker Decoder Data Is Error!')
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var obj = { type: LoadType.loadBufferByWorker, data: buffer }
|
||||
worker.postMessage(obj, [buffer])
|
||||
|
||||
}
|
||||
|
||||
private gifDecodeCorrect(frames) {
|
||||
if (
|
||||
(frames.patch.length == frames.dims.length) &&
|
||||
(frames.patch.length == frames.delay.length) &&
|
||||
(frames.patch.length == frames.disposalType.length) &&
|
||||
(frames.patch.length == frames.patch.length) &&
|
||||
(frames.patch.length == frames.transparentIndex.length)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 子线程数据回传处理
|
||||
private recDecodedData(pages): GIFFrame[] {
|
||||
let images = []
|
||||
for (let i = 0; i < pages.patch.length; i++) {
|
||||
let frame = {}
|
||||
frame['dims'] = pages.dims[i]
|
||||
pages.dims[i] = null
|
||||
frame['delay'] = pages.delay[i]
|
||||
pages.delay[i] = null
|
||||
frame['disposalType'] = pages.disposalType[i]
|
||||
pages.disposalType[i] = null
|
||||
let uint8ClampedArray = new Uint8ClampedArray(pages.patch[i])
|
||||
frame['patch'] = uint8ClampedArray
|
||||
pages.patch[i] = null
|
||||
frame['transparentIndex'] = pages.transparentIndex[i]
|
||||
pages.transparentIndex[i] = null
|
||||
images.push(frame)
|
||||
}
|
||||
pages = null
|
||||
return images;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Huawei Device Co., Ltd.
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
|
@ -12,7 +12,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export class PixelMapPack {
|
||||
pixelMap:PixelMap
|
||||
export interface IParseGif{
|
||||
// gif解析
|
||||
parseGifs(imageinfo:ArrayBuffer,callback:(data?,err?)=>void,worker?)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { decompressFrames, parseGIF } from './index'
|
||||
import { fixLoseGCE} from '../utils/ParseHelperUtils'
|
||||
import { GIFFrame } from '../GIFFrame'
|
||||
|
||||
export function parseBufferToFrame(arraybuffer: ArrayBuffer): GIFFrame[] {
|
||||
let gif = parseGIF(arraybuffer)
|
||||
fixLoseGCE(gif)
|
||||
let origins = decompressFrames(gif, true)
|
||||
return origins as GIFFrame[];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deinterlace function from https://github.com/shachaf/jsgif
|
||||
*/
|
||||
export function deinterlace(pixels, width) {
|
||||
var newPixels = new Array(pixels.length);
|
||||
var rows = pixels.length / width;
|
||||
|
||||
var cpRow = function cpRow(toRow, fromRow) {
|
||||
var fromPixels = pixels.slice(fromRow * width, (fromRow + 1) * width);
|
||||
newPixels.splice.apply(newPixels, [toRow * width, width].concat(fromPixels));
|
||||
}; // See appendix E.
|
||||
|
||||
|
||||
var offsets = [0, 4, 2, 1];
|
||||
var steps = [8, 8, 4, 2];
|
||||
var fromRow = 0;
|
||||
|
||||
for (var pass = 0; pass < 4; pass++) {
|
||||
for (var toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) {
|
||||
cpRow(toRow, fromRow);
|
||||
fromRow++;
|
||||
}
|
||||
}
|
||||
return newPixels;
|
||||
};
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var _gif = _interopRequireDefault(require("js-binary-schema-parser/lib/schemas/gif"));
|
||||
|
||||
var _jsBinarySchemaParser = require("js-binary-schema-parser");
|
||||
|
||||
var _uint = require("js-binary-schema-parser/lib/parsers/uint8");
|
||||
|
||||
import {deinterlace} from './deinterlace'
|
||||
import {lzw} from './lzw'
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
export function parseGIF(arrayBuffer) {
|
||||
var byteData = new Uint8Array(arrayBuffer);
|
||||
return (0, _jsBinarySchemaParser.parse)((0, _uint.buildStream)(byteData), _gif["default"]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
export function generatePatch(image) {
|
||||
var totalPixels = image.pixels.length;
|
||||
var patchData = new Uint8ClampedArray(totalPixels * 4);
|
||||
|
||||
for (var i = 0; i < totalPixels; i++) {
|
||||
var pos = i * 4;
|
||||
var colorIndex = image.pixels[i];
|
||||
var color = image.colorTable[colorIndex] || [0, 0, 0];
|
||||
patchData[pos] = color[2];
|
||||
patchData[pos + 1] = color[1];
|
||||
patchData[pos + 2] = color[0];
|
||||
patchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0;
|
||||
}
|
||||
|
||||
return patchData;
|
||||
};
|
||||
|
||||
export function decompressFrame(frame, gct, buildImagePatch) {
|
||||
if (!frame.image) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var image = frame.image; // get the number of pixels
|
||||
|
||||
var totalPixels = image.descriptor.width * image.descriptor.height; // do lzw decompression
|
||||
|
||||
var pixels = lzw(image.data.minCodeSize, image.data.blocks, totalPixels); // deal with interlacing if necessary
|
||||
|
||||
if (image.descriptor.lct.interlaced) {
|
||||
pixels = deinterlace(pixels, image.descriptor.width);
|
||||
}
|
||||
|
||||
var resultImage = {
|
||||
pixels: pixels,
|
||||
dims: {
|
||||
top: frame.image.descriptor.top,
|
||||
left: frame.image.descriptor.left,
|
||||
width: frame.image.descriptor.width,
|
||||
height: frame.image.descriptor.height
|
||||
}
|
||||
}; // color table
|
||||
|
||||
if (image.descriptor.lct && image.descriptor.lct.exists) {
|
||||
resultImage['colorTable'] = image.lct;
|
||||
} else {
|
||||
resultImage['colorTable'] = gct;
|
||||
} // add per frame relevant gce information
|
||||
|
||||
|
||||
if (frame.gce) {
|
||||
resultImage['delay'] = (frame.gce.delay || 10) * 10; // convert to ms
|
||||
|
||||
resultImage['disposalType'] = frame.gce.extras.disposal; // transparency
|
||||
|
||||
if (frame.gce.extras.transparentColorGiven) {
|
||||
resultImage['transparentIndex'] = frame.gce.transparentColorIndex;
|
||||
}
|
||||
} // create canvas usable imagedata if desired
|
||||
|
||||
|
||||
if (buildImagePatch) {
|
||||
resultImage['patch'] = generatePatch(resultImage);
|
||||
resultImage['colorTable'] = null
|
||||
resultImage['transparentIndex'] = null
|
||||
resultImage['pixels'] = null
|
||||
}
|
||||
|
||||
return resultImage;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export function decompressFrames(parsedGif, buildImagePatches) {
|
||||
return parsedGif.frames.filter(function (f) {
|
||||
return f.image;
|
||||
}).map(function (f) {
|
||||
return decompressFrame(f, parsedGif.gct, buildImagePatches);
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* javascript port of java LZW decompression
|
||||
* Original java author url: https://gist.github.com/devunwired/4479231
|
||||
*/
|
||||
export function lzw(minCodeSize, data, pixelCount) {
|
||||
var MAX_STACK_SIZE = 4096;
|
||||
var nullCode = -1;
|
||||
var npix = pixelCount;
|
||||
var available;
|
||||
var clear;
|
||||
var codeMask;
|
||||
var codeSize;
|
||||
var endOfInformation;
|
||||
var inCode;
|
||||
var oldCode;
|
||||
var bits;
|
||||
var code;
|
||||
var i;
|
||||
var datum;
|
||||
var dataSize;
|
||||
var first;
|
||||
var top;
|
||||
var bi;
|
||||
var pi;
|
||||
var dstPixels = new Array(pixelCount);
|
||||
var prefix = new Array(MAX_STACK_SIZE);
|
||||
var suffix = new Array(MAX_STACK_SIZE);
|
||||
var pixelStack = new Array(MAX_STACK_SIZE + 1); // Initialize GIF data stream decoder.
|
||||
|
||||
dataSize = minCodeSize;
|
||||
clear = 1 << dataSize;
|
||||
endOfInformation = clear + 1;
|
||||
available = clear + 2;
|
||||
oldCode = nullCode;
|
||||
codeSize = dataSize + 1;
|
||||
codeMask = (1 << codeSize) - 1;
|
||||
|
||||
for (code = 0; code < clear; code++) {
|
||||
prefix[code] = 0;
|
||||
suffix[code] = code;
|
||||
} // Decode GIF pixel stream.
|
||||
|
||||
|
||||
var datum, bits, count, first, top, pi, bi;
|
||||
datum = bits = count = first = top = pi = bi = 0;
|
||||
|
||||
for (i = 0; i < npix;) {
|
||||
if (top === 0) {
|
||||
if (bits < codeSize) {
|
||||
// get the next byte
|
||||
datum += data[bi] << bits;
|
||||
bits += 8;
|
||||
bi++;
|
||||
continue;
|
||||
} // Get the next code.
|
||||
|
||||
|
||||
code = datum & codeMask;
|
||||
datum >>= codeSize;
|
||||
bits -= codeSize; // Interpret the code
|
||||
|
||||
if (code > available || code == endOfInformation) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (code == clear) {
|
||||
// Reset decoder.
|
||||
codeSize = dataSize + 1;
|
||||
codeMask = (1 << codeSize) - 1;
|
||||
available = clear + 2;
|
||||
oldCode = nullCode;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oldCode == nullCode) {
|
||||
pixelStack[top++] = suffix[code];
|
||||
oldCode = code;
|
||||
first = code;
|
||||
continue;
|
||||
}
|
||||
|
||||
inCode = code;
|
||||
|
||||
if (code == available) {
|
||||
pixelStack[top++] = first;
|
||||
code = oldCode;
|
||||
}
|
||||
|
||||
while (code > clear) {
|
||||
pixelStack[top++] = suffix[code];
|
||||
code = prefix[code];
|
||||
}
|
||||
|
||||
first = suffix[code] & 0xff;
|
||||
pixelStack[top++] = first; // add a new string to the table, but only if space is available
|
||||
// if not, just continue with current table until a clear code is found
|
||||
// (deferred clear code implementation as per GIF spec)
|
||||
|
||||
if (available < MAX_STACK_SIZE) {
|
||||
prefix[available] = oldCode;
|
||||
suffix[available] = first;
|
||||
available++;
|
||||
|
||||
if ((available & codeMask) === 0 && available < MAX_STACK_SIZE) {
|
||||
codeSize++;
|
||||
codeMask += available;
|
||||
}
|
||||
}
|
||||
|
||||
oldCode = inCode;
|
||||
} // Pop a pixel off the pixel stack.
|
||||
|
||||
|
||||
top--;
|
||||
dstPixels[pi++] = pixelStack[top];
|
||||
i++;
|
||||
}
|
||||
|
||||
for (i = pi; i < npix; i++) {
|
||||
dstPixels[i] = 0; // clear missing pixels
|
||||
}
|
||||
|
||||
return dstPixels;
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export function fixLoseGCE(gif) {
|
||||
let currentGce = null;
|
||||
for (const frame of gif.frames) {
|
||||
currentGce = frame.gce ? frame.gce : currentGce;
|
||||
// fix loosing graphic control extension for same frames
|
||||
if ("image" in frame && !("gce" in frame)) {
|
||||
frame.gce = currentGce;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import arkWorker from '@ohos.worker';
|
||||
import { parseBufferToFrame } from '../parse/GIFParse'
|
||||
|
||||
export enum LoadType {
|
||||
loadBufferByWorker = "loadBufferByWorker"
|
||||
}
|
||||
|
||||
// Send or Receive Format Data Such as: {type: yourResolveType, data: yourDataJson, error?: yourErrorInfo }
|
||||
export function gifHandler(e) {
|
||||
let data = e.data;
|
||||
switch (data.type) {
|
||||
case LoadType.loadBufferByWorker:
|
||||
loadBufferByWorker(data.data, data.type);
|
||||
break;
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
function loadBufferByWorker(buffer: ArrayBuffer, recType: string) {
|
||||
let images = parseBufferToFrame(buffer);
|
||||
let dimss = [];
|
||||
let delays = [];
|
||||
let disposalTypes = [];
|
||||
let patchs = [];
|
||||
let transparentIndexs = [];
|
||||
for (let i = 0; i < images.length; i++) {
|
||||
dimss.push(images[i].dims)
|
||||
delays.push(images[i].delay)
|
||||
disposalTypes.push(images[i].disposalType)
|
||||
patchs.push(images[i].patch.buffer)
|
||||
transparentIndexs.push(images[i].transparentIndex)
|
||||
}
|
||||
let frame = {
|
||||
dims: dimss,
|
||||
// 当前帧到下一帧的间隔时长
|
||||
delay: delays,
|
||||
// 当前帧绘制要求 0保留 1在上一帧绘制此帧 2恢复画布背景 3.将画布恢复到绘制当前图像之前的先前状态
|
||||
disposalType: disposalTypes,
|
||||
// Uint8CampedArray颜色转换后的补片信息用于绘制
|
||||
patch: patchs,
|
||||
// 表示透明度的可选颜色索引
|
||||
transparentIndex: transparentIndexs
|
||||
}
|
||||
let dataObj = { type: recType, data: frame }
|
||||
arkWorker.parentPort.postMessage(dataObj, patchs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue