background

Apng is becoming the solution for most businesses to achieve complex dynamic effects and animation. This scheme has the following advantages:

  1. Better picture quality than GIF, especially for transparent images. Please Google for more details
  2. It’s actually apng file, and on devices that don’t support apng, it can degrade to apng still (more on that later).
  3. Can be directly inserted into the web page as IMG tags, no logical control animation, low development cost
  4. Directly produced by the designer, the design reduction degree is 100%

Our intelligent auxiliary broadcast business also has such a use scenario. The following figure

Images may be demoted click to view: gw.alicdn.com/imgextra/i1…

The image above is an endless loop of APNG files created by the designer through software. So when you show it directly on the device without processing it will play in a loop. The following image is designed to be a one-play animation (if you don’t see the action you can just copy the image link and open it in your browser.

Images may be demoted, click to view: gw.alicdn.com/imgextra/i1…

A good web page should follow basic specifications, such as those specified in the W3C Accessibility Specification:

Don’t design content that will cause seizures or physical reactions.

Pages do not contain any content that flashes more than 3 times/SEC, or flashes below the normal flash and red flash thresholds. .

Interactive animation triggered by an interaction can be disabled unless the animation is critical to the function or the message conveyed.

So animations on a page should not be played repeatedly (on the one hand, distracting the user, and on the other, annoying). In the intelligent auxiliary broadcast business, we stipulate that the animation only plays once when we get the new dialogue content of the assistant.

In the WEEX environment, our designers directly produce a non-looping APNG file, the front-end only need to load. In the H5 environment, we can directly control the playback of APNG.

apng-canvas

Apng-canvas is a library for controlling apNG file playback behavior in a browser environment. It takes an APNG buffer, extracts the data from each frame, and assembs the data into PNG format frame by frame to draw on canvas. It also exposes methods for controlling the playback times, pausing, and so on. Specific use is not described in this article, interested in the link to try.

(A) the PNG specification

I have studied the decoding ideas of APNG-Canvas in detail, and looked at the specification documents of PNG and APNG, and I probably have a concept. (A)PNG file data flow is actually composed of chunks and file signatures. The signature of such a file is represented by an 8-bit byte array.

export const PNG_SIGNATURE_BYTES = new Uint8Array([0x89.0x50.0x4e.0x47.0x0d.0x0a.0x1a.0x0a]);
// The corresponding decimal is:
export const _PNG_SIGNATURE_BYTES = new Uint8Array([137.80.78.71.13.10.26.10]);
Copy the code

Specification of apng

  • The APNG specification document is stamped here
  • The PNG specification is stamped here

Compared to PNG, APNG has the following types of blocks

Block types Must be meaning Location and Requirements
acTL is Animation control block It follows the IHDR block
fcTL is Frame control block 1. The first fcTL follows the acTL

2. All subsequent FCTLS are at the beginning of each frame
fdAT is The frame data block Immediately after fcTL, and at least one

Constitute the core of a apng block below (reference source: segmentfault.com/a/119000002…

The order of these blocks in the APNG file stream is as follows:

When I tried to synthesize APNG, I stomped on several points for a long time:

  1. You must have an IDAT block, which is usually taken from the IDAT block of the first PNG frame. This block is intended to be used as a degraded PNG in environments where APNG is not supported

  2. FcTL and fdAT blocks share sequence numbers (sequence). The sequence number starts from 0, that is, the SEQUENCE of fcTL before the first IDAT is 0

  3. Multiple idAts may exist and need to be added to the data flow in sequence

  4. It is necessary to pay attention to whether the size of the image is set correctly. If the size of the image is set incorrectly, the sequence frame parsed out will have problems. Meanwhile, apng will automatically degrade to the static image represented by the first IDAT, as follows :(the first is the actual effect of apng in the browser, and the last three are the PNG rendering effect obtained by parsing this apng)

Apng was synthesized from PNG

Apng-canvas provides the ability to parse and play Apng in canvas. We can generate an Apng in reverse according to the author’s idea. The core code is as follows, complete code please stamp: apng-handler

interface Params {
  /* png buffers */
  buffers: ArrayBuffer[];
  /* Number of plays: 0 indicates an infinite loop */playNum? :number;
  /* We assume that all frames are the same size */
  width: number;
  height: number;
}

/** * PNG buffers to APng buffer */
export function apngAssembler(params: Params) {
  const { buffers = [], playNum = 0, width, height } = params;
  const bb: BlobPart[] = [];

  /* 1. Place the first 8 bytes in a PNG signature */
  bb.push(PNG_SIGNATURE_BYTES);

  // Use the IHDR, IEND, IDAT block of the first frame. Note that there may be multiple IDAT blocks
  let IDATParts: Uint8Array[] = [];
  let IHDR: Uint8Array;
  let IEND: Uint8Array;
  parseChunks(new Uint8Array(buffers[0]), ({ type, bytes, off, length }) = > {
    if (type= = ="IHDR") {
      /* 8: 4-byte length information + 4-byte type string information */
      IHDR = bytes.subarray(off + 8, off + 8 + length);
    }
    if (type= = ="IDAT") {
      IDATParts.push(bytes.subarray(off + 8, off + 8 + length));
    }
    if (type= = ="IEND") {
      IEND = bytes.subarray(off + 8, off + 8 + length);
    }
    return true;
  });

  PNG after the signature, add the header information IHDR block */
  bb.push(makeChunkBytes("IHDR", IHDR));

  /* 3. Put the acTL block */ after the header information
  bb.push(createAcTL(buffers.length, playNum));

  /* add the first fcTL control block. The first seq is 0 */
  bb.push(createFcTL({ seq: 0, width, height }));

  /* 5. Add the IDAT block */
  for (let IDAT of IDATParts) {
    bb.push(makeChunkBytes("IDAT", IDAT));
  }

  /* fcTL and fdAT */
  // Note that seq is already 1
  let seq = 1;
  for (let i = 1; i < buffers.length; i++) {
    /* 6.1 Insert fcTL */
    bb.push(createFcTL({ seq, width, height }));
    // Note that fcTL and fdAT share seq
    seq += 1;

    // Get the IDAT block list of the current frame buffer
    let iDatParts: Uint8Array[] = [];
    parseChunks(new Uint8Array(buffers[i]), ({ type, bytes, off, length }) = > {
      if (type= = ="IDAT") {
        iDatParts.push(bytes.subarray(off + 8, off + 8 + length));
      }
      return true;
    });

    /* 6.2 Using this IDAT block, generate fdAT */
    for (let j = 0; j < iDatParts.length; j++) { bb.push(createFdAT(seq, iDatParts[j])); seq++; }}/* add the last part of the IEND block */
  bb.push(makeChunkBytes("IEND", IEND));

  // Return a Blob object
  return new Blob(bb, { type: "image/apng" });
}
Copy the code

The key here are fcTL and acTL, which control the playback behavior of the entire APNG. For example, fcTL uses two parameters to control frame rendering:

/ * * *@see https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk * how to deal with the current frame before rendering the next frame * /
export enum DisposeOP {
  /* No processing is done on this frame until the next frame is rendered; The contents of the output buffer remain unchanged. * /
  NONE,
  /* Clear the frame area of the output buffer to a fully transparent black before rendering the next frame. * /
  TRANSPARENT,
  /* Returns the frame area of the output buffer to its previous contents before rendering the next frame. * /
  PREVIOUS,
}

/ * * *@see When the current frame render mixed mode * https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk * /
export enum BlendOP {
  /* All color components of the frame (including alpha) will overwrite the current contents of the frame's output buffer */
  SOURCE,
  /* overwrites */ directly
  OVER,
}
Copy the code

At the end

Apng-canvas is a great library, but it usually writes business logic code, rarely involves byte arrays, bit operations, and the library author has almost no comments, so it takes some time to understand some of the methods in this library.

For example, the bit-to-decimal version of an 8-bit byte array is as follows

export const bytes2Decimal = function (bytes: Uint8Array, off: number, bLen = 4) {
  let x = 0;
  // Force the most-significant byte to unsigned.
  x += (bytes[0 + off] << 24) > > >0;
  for (let i = 1; i < bLen; i++) x += bytes[i + off] << ((3 - i) * 8);
  return x;
};
Copy the code

Write it in the more understandable way we usually do:

export const _bytes2Decimal = (bytes: Uint8Array, off: number, bLen = 4) = > {
  let x = "";
  for (let i = off; i < off + bLen; i++) {
    // Each bit is converted to base 2 and padded to 8 bits
    x += ("00000000" + bytes[i].toString(2)).slice(-8);
  }
  // Convert the string to a decimal number
  return parseInt(x, 2);
};
Copy the code

I put this library, plus the core method of PNG synthesis APNG, in a new repository. I rewrote it with TS, changing some method names and some code structures to make it easier to read and understand. Warehouse address: APng-handler. Hope to gain some pr for compressing APNG in browser environments.

Attached is a rendering of APNG composed with code (Delay0.1s, Dispose with TRANSPARENT (1) mode: clean canvas before next frame rendering) :

The appendix

  1. APNG specification

    Most importantly, it explains in detail some of the specifications added to each APNG compared to PNG.

  2. The W3C PNG specification

    W3C documentation, which you must read to learn more about. But it was too professional, I didn’t read all of them, I mainly looked at some conceptual things. I think I’ll definitely look at it again if I need to learn about compression in the future.

  3. APNG Wikipedia

    The main one is the explanatory chart, which is referenced in many articles. I added it to README

  4. Implementation principle of Web APNG playback

    The explanation of APNG-Canvas by the domestic netease cloud front-end team shows a very good picture

  5. ezgif.com

    An online tool to generate APNG

  6. APNG Assembler

    Generate, parse apNG software

  7. Join up PNG images to an APNG animated image

    Answer an encode method in a Node environment

  8. UPNG.js

    I tried once but failed, may be the usage problem, in addition this code is not very easy to understand, I did not look closely.