gif是什么储存格式,gif是不是音频文件格式

首页 > 实用技巧 > 作者:YD1662024-01-25 19:15:15

3、图形控制扩展结构(Graphic Control Extension)这部分对图片的渲染比较重要

gif是什么储存格式,gif是不是音频文件格式(9)

除了前面说过的Dispose Method、Delay、Background Color之外,User Input用来定义是否接受用户输入后再播放下一帧,需要图像解码器对应api的配合,可以用来实现一些特殊的交互效果。

4、平滑文本扩展结构(Plain Text Control Extension)

gif是什么储存格式,gif是不是音频文件格式(10)

89a标准允许我们将图片上的文字信息额外储存在扩展区域里,但实际渲染时依赖解码器的字体环境,所以实际情况中很少使用。

以上扩展块都是可选的,只有Label置位的情况下,解码器才会去渲染

Glide 加载Gif原理

Glide 解析Gif文件格式代码:

/** * Reads GIF file header information. */ private void readHeader() { StringBuilder id = new StringBuilder(); for (int i = 0; i < 6; i ) { id.append((char) read()); } if (!id.toString().startsWith("GIF")) { header.status = STATUS_FORMAT_ERROR; return; } } /** * Reads Logical Screen Descriptor. */ private void readLSD() { // Logical screen size. header.width = readShort(); header.height = readShort(); /* * Logical Screen Descriptor packed field: * 7 6 5 4 3 2 1 0 * --------------- * 4 | | | | | * * Global Color Table Flag 1 Bit * Color Resolution 3 Bits * Sort Flag 1 Bit * Size of Global Color Table 3 Bits */ int packed = read(); header.gctFlag = (packed & LSD_MASK_GCT_FLAG) != 0; header.gctSize = (int) Math.pow(2, (packed & LSD_MASK_GCT_SIZE) 1); // Background color index. header.bgIndex = read(); // Pixel aspect ratio header.pixelAspect = read(); } /** * Reads color table as 256 RGB integer values. * * @param nColors int number of colors to read. * @return int array containing 256 colors (packed ARGB with full alpha). */ @Nullable private int[] readColorTable(int nColors) { int nbytes = 3 * nColors; int[] tab = null; byte[] c = new byte[nBytes]; try { rawData.get(c); // TODO: what bounds checks are we avoiding if we know the number of colors? // Max size to avoid bounds checks. tab = new int[MAX_BLOCK_SIZE]; int i = 0; int j = 0; while (i < nColors) { int r = ((int) c[j ]) & MASK_INT_LOWEST_BYTE; int g = ((int) c[j ]) & MASK_INT_LOWEST_BYTE; int b = ((int) c[j ]) & MASK_INT_LOWEST_BYTE; tab[i ] = 0xFF000000 | (r << 16) | (g << 8) | b; } } catch (BufferUnderflowException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Format Error Reading Color Table", e); } header.status = STATUS_FORMAT_ERROR; } return tab; } /** * Reads next frame image. */ private void readbitmap() { // (sub)image position & size. header.currentFrame.ix = readShort(); header.currentFrame.iy = readShort(); header.currentFrame.iw = readShort(); header.currentFrame.ih = readShort(); /* * Image Descriptor packed field: * 7 6 5 4 3 2 1 0 * --------------- * 9 | | | | | | * * Local Color Table Flag 1 Bit * Interlace Flag 1 Bit * Sort Flag 1 Bit * Reserved 2 Bits * Size of Local Color Table 3 Bits */ int packed = read(); boolean lctFlag = (packed & DESCRIPTOR_MASK_LCT_FLAG) != 0; int lctSize = (int) Math.pow(2, (packed & DESCRIPTOR_MASK_LCT_SIZE) 1); header.currentFrame.interlace = (packed & DESCRIPTOR_MASK_INTERLACE_FLAG) != 0; if (lctFlag) { header.currentFrame.lct = readColorTable(lctSize); } else { // No local color table. header.currentFrame.lct = null; } // Save this as the decoding position pointer. header.currentFrame.bufferFrameStart = rawData.position(); // False decode pixel data to advance buffer. skipImageData(); if (err()) { return; } header.frameCount ; // Add image to frame. header.frames.add(header.currentFrame); }

Glide渲染Gif主要是通过GifDrawable这个类,该类实现了Animatable接口,当调用start()方法后开始循环播放,在draw()方法里进行绘制bitmap。

@Override public void start() { isStarted = true; resetLoopCount(); if (isVisible) { startRunning(); } } private void startRunning() { // 当 Gif 只有一帧的时候,会直接调用绘制方法 if (State.frameLoader.getFrameCount() == 1) { invalidateSelf(); } else if (!isRunning) { isRunning = true; // Gif 不止一帧的时候,就开启了订阅 state.frameLoader.subscribe(this); invalidateSelf(); } } @Override public void draw(@NonNull Canvas canvas) { if (isRecycled) { return; } if (applyGravity) { Gravity.apply(GRAVITY, getIntrinsicWidth(), getIntrinsicHeight(), getBounds(), getDestRect()); applyGravity = false; } Bitmap currentFrame = state.frameLoader.getCurrentFrame(); canvas.drawBitmap(currentFrame, null, getDestRect(), getPaint()); }

Gif图片中的每一帧的bitmap就是GifFrameLoader里GifDecoder解析出来的,GifDrawable调用start()方法后,会在GifFrameLoader注册监听,每一帧图片解析完后会回调再进行绘制

void subscribe(FrameCallback frameCallback) { if (isCleared) { throw new IllegalStateException("Cannot subscribe to a cleared frame loader"); } if (callbacks.contains(frameCallback)) { throw new IllegalStateException("Cannot subscribe twice in a row"); } boolean start = callbacks.isEmpty(); callbacks.add(frameCallback); if (start) { start(); } } start() { loadNextFrame(); } private void loadNextFrame() { if (!isRunning || isLoadPending) { return; } if (startFromFirstFrame) { Preconditions.checkArgument( pendingTarget == null, "Pending target must be null when starting from the first frame"); gifDecoder.resetFrameIndex(); startFromFirstFrame = false; } if (pendingTarget != null) { DelayTarget temp = pendingTarget; pendingTarget = null; onFrameReady(temp); return; } isLoadPending = true; // Get the delay before incrementing the pointer because the delay indicates the amount of time // we want to spend on the current frame. int delay = gifDecoder.getNextDelay(); long targetTime = SystemClock.uptimeMillis() delay; gifDecoder.advance(); next = new DelayTarget(Handler, gifDecoder.getCurrentFrameIndex(), targetTime); requestBuilder.apply(signatureOf(getFrameSignature())).load(gifDecoder).into(next); }

Glide 加载 Gif 的原理比较简单,就是将 Gif 解码成多张图片进行无限轮播,每帧切换都是一次图片加载请求,再加载到新的一帧数据之后会对旧的一帧数据进行清除,然后再继续下一帧数据的加载请求,以此类推,使用 Handler 发送消息实现循环播放。

android-gif-drawable

也是用sdk内部的Gifdrawable来进行渲染的,与glide类似,该类也是继承了Animatable接口,在适当的时候调用start()方法就会开始循环绘制,然后在draw()方法里进行bitmap绘制

/** * Starts the animation. Does nothing if GIF is not animated. * This method is thread-safe. */ @Override public void start() { synchronized (this) { if (mIsRunning) { return; } mIsRunning = true; } final long lastFrameRemainder = mNativeInfoHandle.restoreRemainder(); startAnimation(lastFrameRemainder); } void startAnimation(long lastFrameRemainder) { if (mIsRenderingTriggeredOnDraw) { mNextFrameRenderTime = 0; mInvalidationHandler.sendEmptyMessageAtTime(MSG_TYPE_INVALIDATION, 0); } else { cancelPendingRenderTask(); mRenderTaskSchedule = mExecutor.schedule(mRenderTask, Math.max(lastFrameRemainder, 0), TimeUnit.MILLISECONDS); } } public void draw(@NonNull Canvas canvas) { final boolean clearColorFilter; if (mTintFilter != null && mPaint.getColorFilter() == null) { mPaint.setColorFilter(mTintFilter); clearColorFilter = true; } else { clearColorFilter = false; } if (mTransform == null) { canvas.drawBitmap(mBuffer, mSrcRect, mDstRect, mPaint); } else { mTransform.onDraw(canvas, mPaint, mBuffer); } if (clearColorFilter) { mPaint.setColorFilter(null); } }

与glide不同的是,android-gif-drawable的bitmap解析是在native层进行的,并且Bitmap对象只会存在一个

Java_pl_droidsonroids_gif_GifInfoHandle_renderFrame(JNIEnv *env, jclass __unused handleClass, jlong gifInfo, jobject jbitmap) { GifInfo *info = (GifInfo *) (intptr_t) gifInfo; if (info == NULL) return -1; long renderStartTime = getRealTime(); void *pixels; if (lockPixels(env, jbitmap, info, &pixels) != 0) { return 0; } DDGifSlurp(info, true, false); if (info->currentIndex == 0) { prepareCanvas(pixels, info); } const uint_fast32_t frameDuration = getBitmap(pixels, info); unlockPixels(env, jbitmap); return calculateInvalidationDelay(info, renderStartTime, frameDuration); }

上一页123末页

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.