1/* 2 * Copyright (c) 2024 - 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import type { ImageInfo, ImageData } from '../../../model/Interfaces'; 17import { readInt16LE, readUInt24LE, toHexString, toUTF8String } from '../BytesUtils'; 18 19function calculateExtended(input: Uint8Array): ImageInfo { 20 return { 21 height: 1 + readUInt24LE(input, 7), 22 width: 1 + readUInt24LE(input, 4), 23 }; 24} 25 26function calculateLossLess(input: Uint8Array): ImageInfo { 27 return { 28 height: 1 + (((input[4] & 0xf) << 10) | (input[3] << 2) | ((input[2] & 0xc0) >> 6)), 29 width: 1 + (((input[2] & 0x3f) << 8) | input[1]), 30 }; 31} 32 33function calculateLossy(input: Uint8Array): ImageInfo { 34 return { 35 height: readInt16LE(input, 8) & 0x3fff, 36 width: readInt16LE(input, 6) & 0x3fff, 37 }; 38} 39 40export const WEBP: ImageData = { 41 validate(input) { 42 const riffHeader = 'RIFF' === toUTF8String(input, 0, 4); 43 const webpHeader = 'WEBP' === toUTF8String(input, 8, 12); 44 const vp8Header = 'VP8' === toUTF8String(input, 12, 15); 45 return riffHeader && webpHeader && vp8Header; 46 }, 47 calculate(_input) { 48 const chunkHeader = toUTF8String(_input, 12, 16); 49 const input = _input.slice(20, 30); 50 if (chunkHeader === 'VP8X') { 51 const extendedHeader = input[0]; 52 const validStart = (extendedHeader & 0xc0) === 0; 53 const validEnd = (extendedHeader & 0x01) === 0; 54 if (validStart && validEnd) { 55 return calculateExtended(input); 56 } 57 throw new TypeError('Invalid WebP'); 58 } 59 if (chunkHeader === 'VP8' && input[0] !== 0x2f) { 60 return calculateLossy(input); 61 } 62 const signature = toHexString(input, 3, 6); 63 if (chunkHeader === 'VP8L' && signature !== '9d012a') { 64 return calculateLossLess(input); 65 } 66 throw new TypeError('Invalid WebP'); 67 }, 68};