zhangjian
2023-05-30 dabbcc356af21f9f2f88ac69ff07994e6e32e4fc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
'use strict';
 
// based on https://developers.google.com/speed/webp/docs/riff_container
 
function isWebP (buffer) {
  var riffHeader = 'RIFF' === buffer.toString('ascii', 0, 4);
  var webpHeader = 'WEBP' === buffer.toString('ascii', 8, 12);
  var vp8Header  = 'VP8'  === buffer.toString('ascii', 12, 15);
  return (riffHeader && webpHeader && vp8Header);
}
 
function calculate (buffer) {
  var chunkHeader = buffer.toString('ascii', 12, 16);
  buffer = buffer.slice(20, 30);
 
  // Extended webp stream signature
  if (chunkHeader === 'VP8X') {
    var extendedHeader = buffer[0];
    var validStart = (extendedHeader & 0xc0) === 0;
    var validEnd = (extendedHeader & 0x01) === 0;
    if (validStart && validEnd) {
      return calculateExtended(buffer);
    } else {
      return false;
    }
  }
 
  // Lossless webp stream signature
  if (chunkHeader === 'VP8 ' && buffer[0] !== 0x2f) {
    return calculateLossy(buffer);
  }
 
  // Lossy webp stream signature
  var signature = buffer.toString('hex', 3, 6);
  if (chunkHeader === 'VP8L' && signature !== '9d012a') {
    return calculateLossless(buffer);
  }
 
  return false;
}
 
function calculateExtended (buffer) {
  return {
    'width': 1 + buffer.readUIntLE(4, 3),
    'height': 1 + buffer.readUIntLE(7, 3)
  }
}
 
function calculateLossless (buffer) {
  return {
    'width': 1 + (((buffer[2] & 0x3F) << 8) | buffer[1]),
    'height': 1 + (((buffer[4] & 0xF) << 10) | (buffer[3] << 2) |
                  ((buffer[2] & 0xC0) >> 6))
  };
}
 
function calculateLossy (buffer) {
  // `& 0x3fff` returns the last 14 bits
  // TO-DO: include webp scaling in the calculations
  return {
    'width': buffer.readInt16LE(6) & 0x3fff,
    'height': buffer.readInt16LE(8) & 0x3fff
  };
}
 
module.exports = {
  'detect': isWebP,
  'calculate': calculate
};