@loaders.gl/images 3.4.0-alpha.1 → 3.4.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/dist.min.js +1 -1
  2. package/dist/dist.min.js.map +3 -3
  3. package/dist/es5/image-loader.js +2 -2
  4. package/dist/es5/image-loader.js.map +1 -1
  5. package/dist/es5/index.js +12 -6
  6. package/dist/es5/index.js.map +1 -1
  7. package/dist/es5/lib/category-api/binary-image-api.js +15 -1
  8. package/dist/es5/lib/category-api/binary-image-api.js.map +1 -1
  9. package/dist/es5/lib/category-api/image-format.js +170 -20
  10. package/dist/es5/lib/category-api/image-format.js.map +1 -1
  11. package/dist/es5/lib/category-api/parse-isobmff-binary.js +55 -0
  12. package/dist/es5/lib/category-api/parse-isobmff-binary.js.map +1 -0
  13. package/dist/es5/lib/utils/version.js +1 -1
  14. package/dist/esm/image-loader.js +2 -2
  15. package/dist/esm/image-loader.js.map +1 -1
  16. package/dist/esm/index.js +2 -1
  17. package/dist/esm/index.js.map +1 -1
  18. package/dist/esm/lib/category-api/binary-image-api.js +16 -1
  19. package/dist/esm/lib/category-api/binary-image-api.js.map +1 -1
  20. package/dist/esm/lib/category-api/image-format.js +58 -21
  21. package/dist/esm/lib/category-api/image-format.js.map +1 -1
  22. package/dist/esm/lib/category-api/parse-isobmff-binary.js +45 -0
  23. package/dist/esm/lib/category-api/parse-isobmff-binary.js.map +1 -0
  24. package/dist/esm/lib/utils/version.js +1 -1
  25. package/dist/image-loader.d.ts.map +1 -1
  26. package/dist/image-loader.js +2 -1
  27. package/dist/index.d.ts +2 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +5 -3
  30. package/dist/lib/category-api/binary-image-api.d.ts +2 -6
  31. package/dist/lib/category-api/binary-image-api.d.ts.map +1 -1
  32. package/dist/lib/category-api/binary-image-api.js +22 -2
  33. package/dist/lib/category-api/image-format.d.ts +4 -2
  34. package/dist/lib/category-api/image-format.d.ts.map +1 -1
  35. package/dist/lib/category-api/image-format.js +86 -39
  36. package/dist/lib/category-api/parse-isobmff-binary.d.ts +19 -0
  37. package/dist/lib/category-api/parse-isobmff-binary.d.ts.map +1 -0
  38. package/dist/lib/category-api/parse-isobmff-binary.js +94 -0
  39. package/package.json +6 -3
  40. package/src/image-loader.ts +2 -1
  41. package/src/index.ts +3 -2
  42. package/src/lib/category-api/binary-image-api.ts +25 -6
  43. package/src/lib/category-api/image-format.ts +92 -39
  44. package/src/lib/category-api/parse-isobmff-binary.ts +105 -0
@@ -0,0 +1,45 @@
1
+
2
+
3
+ export function getISOBMFFMediaType(buffer) {
4
+ if (!checkString(buffer, 'ftyp', 4)) {
5
+ return null;
6
+ }
7
+
8
+ if ((buffer[8] & 0x60) === 0x00) {
9
+ return null;
10
+ }
11
+
12
+ return decodeMajorBrand(buffer);
13
+ }
14
+
15
+ export function decodeMajorBrand(buffer) {
16
+ const brandMajor = getUTF8String(buffer, 8, 12).replace('\0', ' ').trim();
17
+ switch (brandMajor) {
18
+ case 'avif':
19
+ case 'avis':
20
+ return {
21
+ extension: 'avif',
22
+ mimeType: 'image/avif'
23
+ };
24
+ default:
25
+ return null;
26
+ }
27
+ }
28
+
29
+ function getUTF8String(array, start, end) {
30
+ return String.fromCharCode(...array.slice(start, end));
31
+ }
32
+ function stringToBytes(string) {
33
+ return [...string].map(character => character.charCodeAt(0));
34
+ }
35
+ function checkString(buffer, header) {
36
+ let offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
37
+ const headerBytes = stringToBytes(header);
38
+ for (let i = 0; i < headerBytes.length; ++i) {
39
+ if (headerBytes[i] !== buffer[i + offset]) {
40
+ return false;
41
+ }
42
+ }
43
+ return true;
44
+ }
45
+ //# sourceMappingURL=parse-isobmff-binary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-isobmff-binary.js","names":["getISOBMFFMediaType","buffer","checkString","decodeMajorBrand","brandMajor","getUTF8String","replace","trim","extension","mimeType","array","start","end","String","fromCharCode","slice","stringToBytes","string","map","character","charCodeAt","header","offset","headerBytes","i","length"],"sources":["../../../../src/lib/category-api/parse-isobmff-binary.ts"],"sourcesContent":["// loaders.gl, MIT license\n// code adapted from https://github.com/sindresorhus/file-type under MIT license\n\n/**\n * Box is a container format that can contain a variety of media related files,\n * so we want to return information about which type of file is actually contained inside\n */\nexport type BoxFileType = {extension: string; mimeType: string};\n\n/**\n * Tests if a buffer is in ISO base media file format (ISOBMFF) @see https://en.wikipedia.org/wiki/ISO_base_media_file_format\n * (ISOBMFF is a media container standard based on the Apple QuickTime container format)\n */\nexport function getISOBMFFMediaType(buffer: Uint8Array): BoxFileType | null {\n // Almost all ISO base media files start with `ftyp` box. (It's not required to be first, but it's recommended to be.)\n if (!checkString(buffer, 'ftyp', 4)) {\n return null;\n }\n\n // Extra check: test for 8859-1 printable characters (for simplicity, it's a mask which also catches one non-printable character).\n if ((buffer[8] & 0x60) === 0x00) {\n return null;\n }\n\n // `ftyp` box must contain a brand major identifier, which must consist of ISO 8859-1 printable characters.\n return decodeMajorBrand(buffer);\n}\n\n/**\n * brands explained @see https://github.com/strukturag/libheif/issues/83\n * code adapted from @see https://github.com/sindresorhus/file-type/blob/main/core.js#L489-L492\n */\nexport function decodeMajorBrand(buffer: Uint8Array): BoxFileType | null {\n const brandMajor = getUTF8String(buffer, 8, 12).replace('\\0', ' ').trim();\n\n switch (brandMajor) {\n case 'avif':\n case 'avis':\n return {extension: 'avif', mimeType: 'image/avif'};\n default:\n return null;\n }\n // We don't need these now, but they are easy to add\n // case 'mif1':\n // return {extension: 'heic', mimeType: 'image/heif'};\n // case 'msf1':\n // return {extension: 'heic', mimeType: 'image/heif-sequence'};\n // case 'heic':\n // case 'heix':\n // return {extension: 'heic', mimeType: 'image/heic'};\n // case 'hevc':\n // case 'hevx':\n // return {extension: 'heic', mimeType: 'image/heic-sequence'};\n // case 'qt':\n // return {ext: 'mov', mime: 'video/quicktime'};\n // case 'M4V':\n // case 'M4VH':\n // case 'M4VP':\n // return {ext: 'm4v', mime: 'video/x-m4v'};\n // case 'M4P':\n // return {ext: 'm4p', mime: 'video/mp4'};\n // case 'M4B':\n // return {ext: 'm4b', mime: 'audio/mp4'};\n // case 'M4A':\n // return {ext: 'm4a', mime: 'audio/x-m4a'};\n // case 'F4V':\n // return {ext: 'f4v', mime: 'video/mp4'};\n // case 'F4P':\n // return {ext: 'f4p', mime: 'video/mp4'};\n // case 'F4A':\n // return {ext: 'f4a', mime: 'audio/mp4'};\n // case 'F4B':\n // return {ext: 'f4b', mime: 'audio/mp4'};\n // case 'crx':\n // return {ext: 'cr3', mime: 'image/x-canon-cr3'};\n // default:\n // if (brandMajor.startsWith('3g')) {\n // if (brandMajor.startsWith('3g2')) {\n // return {ext: '3g2', mime: 'video/3gpp2'};\n // }\n // return {ext: '3gp', mime: 'video/3gpp'};\n // }\n // return {ext: 'mp4', mime: 'video/mp4'};\n}\n\n/** Interpret a chunk of bytes as a UTF8 string */\nfunction getUTF8String(array: Uint8Array, start: number, end: number): string {\n return String.fromCharCode(...array.slice(start, end));\n}\n\nfunction stringToBytes(string: string): number[] {\n return [...string].map((character) => character.charCodeAt(0));\n}\n\nfunction checkString(buffer: ArrayLike<number>, header: string, offset: number = 0): boolean {\n const headerBytes = stringToBytes(header);\n\n for (let i = 0; i < headerBytes.length; ++i) {\n if (headerBytes[i] !== buffer[i + offset]) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":";;AAaA,OAAO,SAASA,mBAAmB,CAACC,MAAkB,EAAsB;EAE1E,IAAI,CAACC,WAAW,CAACD,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE;IACnC,OAAO,IAAI;EACb;;EAGA,IAAI,CAACA,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,IAAI,EAAE;IAC/B,OAAO,IAAI;EACb;;EAGA,OAAOE,gBAAgB,CAACF,MAAM,CAAC;AACjC;;AAMA,OAAO,SAASE,gBAAgB,CAACF,MAAkB,EAAsB;EACvE,MAAMG,UAAU,GAAGC,aAAa,CAACJ,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAACK,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAACC,IAAI,EAAE;EAEzE,QAAQH,UAAU;IAChB,KAAK,MAAM;IACX,KAAK,MAAM;MACT,OAAO;QAACI,SAAS,EAAE,MAAM;QAAEC,QAAQ,EAAE;MAAY,CAAC;IACpD;MACE,OAAO,IAAI;EAAC;AA2ClB;;AAGA,SAASJ,aAAa,CAACK,KAAiB,EAAEC,KAAa,EAAEC,GAAW,EAAU;EAC5E,OAAOC,MAAM,CAACC,YAAY,CAAC,GAAGJ,KAAK,CAACK,KAAK,CAACJ,KAAK,EAAEC,GAAG,CAAC,CAAC;AACxD;AAEA,SAASI,aAAa,CAACC,MAAc,EAAY;EAC/C,OAAO,CAAC,GAAGA,MAAM,CAAC,CAACC,GAAG,CAAEC,SAAS,IAAKA,SAAS,CAACC,UAAU,CAAC,CAAC,CAAC,CAAC;AAChE;AAEA,SAASlB,WAAW,CAACD,MAAyB,EAAEoB,MAAc,EAA+B;EAAA,IAA7BC,MAAc,uEAAG,CAAC;EAChF,MAAMC,WAAW,GAAGP,aAAa,CAACK,MAAM,CAAC;EAEzC,KAAK,IAAIG,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGD,WAAW,CAACE,MAAM,EAAE,EAAED,CAAC,EAAE;IAC3C,IAAID,WAAW,CAACC,CAAC,CAAC,KAAKvB,MAAM,CAACuB,CAAC,GAAGF,MAAM,CAAC,EAAE;MACzC,OAAO,KAAK;IACd;EACF;EAEA,OAAO,IAAI;AACb"}
@@ -1,3 +1,3 @@
1
1
 
2
- export const VERSION = typeof "3.4.0-alpha.1" !== 'undefined' ? "3.4.0-alpha.1" : 'latest';
2
+ export const VERSION = typeof "3.4.0-alpha.2" !== 'undefined' ? "3.4.0-alpha.2" : 'latest';
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-loader.d.ts","sourceRoot":"","sources":["../src/image-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAE,gBAAgB,EAAC,MAAM,0BAA0B,CAAC;AAE9E,OAAO,UAAU,MAAM,2BAA2B,CAAC;AAcnD,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG;IAC/C,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC;QACjD,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC,CAAC;AAUF;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;CAWvB,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,gBAA8B,CAAC"}
1
+ {"version":3,"file":"image-loader.d.ts","sourceRoot":"","sources":["../src/image-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAE,gBAAgB,EAAC,MAAM,0BAA0B,CAAC;AAE9E,OAAO,UAAU,MAAM,2BAA2B,CAAC;AAenD,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG;IAC/C,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC;QACjD,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC,CAAC;AAUF;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;CAWvB,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,gBAA8B,CAAC"}
@@ -7,12 +7,13 @@ exports._typecheckImageLoader = exports.ImageLoader = void 0;
7
7
  const version_1 = require("./lib/utils/version");
8
8
  const parse_image_1 = __importDefault(require("./lib/parsers/parse-image"));
9
9
  const binary_image_api_1 = require("./lib/category-api/binary-image-api");
10
- const EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'ico', 'svg'];
10
+ const EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'ico', 'svg', 'avif'];
11
11
  const MIME_TYPES = [
12
12
  'image/png',
13
13
  'image/jpeg',
14
14
  'image/gif',
15
15
  'image/webp',
16
+ 'image/avif',
16
17
  'image/bmp',
17
18
  'image/vnd.microsoft.icon',
18
19
  'image/svg+xml'
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export { ImageWriter } from './image-writer';
5
5
  export { getBinaryImageMetadata } from './lib/category-api/binary-image-api';
6
6
  export { isImageTypeSupported, getDefaultImageType } from './lib/category-api/image-type';
7
7
  export { isImage, getImageType, getImageSize, getImageData } from './lib/category-api/parsed-image-api';
8
- export { _isImageFormatSupported } from './lib/category-api/image-format';
8
+ export { getSupportedImageFormats } from './lib/category-api/image-format';
9
+ export { isImageFormatSupported } from './lib/category-api/image-format';
9
10
  export { loadImage } from './lib/texture-api/load-image';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAC,aAAa,EAAE,SAAS,EAAE,aAAa,EAAC,MAAM,SAAS,CAAC;AACrE,YAAY,EAAC,kBAAkB,EAAC,MAAM,gBAAgB,CAAC;AAGvD,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAK3C,OAAO,EAAC,sBAAsB,EAAC,MAAM,qCAAqC,CAAC;AAG3E,OAAO,EAAC,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,+BAA+B,CAAC;AAExF,OAAO,EACL,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,qCAAqC,CAAC;AAG7C,OAAO,EAAC,uBAAuB,EAAC,MAAM,iCAAiC,CAAC;AAGxE,OAAO,EAAC,SAAS,EAAC,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAC,aAAa,EAAE,SAAS,EAAE,aAAa,EAAC,MAAM,SAAS,CAAC;AACrE,YAAY,EAAC,kBAAkB,EAAC,MAAM,gBAAgB,CAAC;AAGvD,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAK3C,OAAO,EAAC,sBAAsB,EAAC,MAAM,qCAAqC,CAAC;AAG3E,OAAO,EAAC,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,+BAA+B,CAAC;AAExF,OAAO,EACL,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,qCAAqC,CAAC;AAG7C,OAAO,EAAC,wBAAwB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAC,sBAAsB,EAAC,MAAM,iCAAiC,CAAC;AAGvE,OAAO,EAAC,SAAS,EAAC,MAAM,8BAA8B,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadImage = exports._isImageFormatSupported = exports.getImageData = exports.getImageSize = exports.getImageType = exports.isImage = exports.getDefaultImageType = exports.isImageTypeSupported = exports.getBinaryImageMetadata = exports.ImageWriter = exports.ImageLoader = void 0;
3
+ exports.loadImage = exports.isImageFormatSupported = exports.getSupportedImageFormats = exports.getImageData = exports.getImageSize = exports.getImageType = exports.isImage = exports.getDefaultImageType = exports.isImageTypeSupported = exports.getBinaryImageMetadata = exports.ImageWriter = exports.ImageLoader = void 0;
4
4
  // LOADERS AND WRITERS
5
5
  var image_loader_1 = require("./image-loader");
6
6
  Object.defineProperty(exports, "ImageLoader", { enumerable: true, get: function () { return image_loader_1.ImageLoader; } });
@@ -21,7 +21,9 @@ Object.defineProperty(exports, "getImageSize", { enumerable: true, get: function
21
21
  Object.defineProperty(exports, "getImageData", { enumerable: true, get: function () { return parsed_image_api_1.getImageData; } });
22
22
  // EXPERIMENTAL
23
23
  var image_format_1 = require("./lib/category-api/image-format");
24
- Object.defineProperty(exports, "_isImageFormatSupported", { enumerable: true, get: function () { return image_format_1._isImageFormatSupported; } });
25
- // DEPRECATED - Remove in V3 (fix dependency in luma.gl)
24
+ Object.defineProperty(exports, "getSupportedImageFormats", { enumerable: true, get: function () { return image_format_1.getSupportedImageFormats; } });
25
+ var image_format_2 = require("./lib/category-api/image-format");
26
+ Object.defineProperty(exports, "isImageFormatSupported", { enumerable: true, get: function () { return image_format_2.isImageFormatSupported; } });
27
+ // DEPRECATED - Remove in V4 (fix dependency in luma.gl)
26
28
  var load_image_1 = require("./lib/texture-api/load-image");
27
29
  Object.defineProperty(exports, "loadImage", { enumerable: true, get: function () { return load_image_1.loadImage; } });
@@ -7,13 +7,9 @@ export type BinaryImageMetadata = {
7
7
  /**
8
8
  * Extracts `{mimeType, width and height}` from a memory buffer containing a known image format
9
9
  * Currently supports `image/png`, `image/jpeg`, `image/bmp` and `image/gif`.
10
- * @param binaryData image file memory to parse
10
+ * @param binaryData: DataView | ArrayBuffer image file memory to parse
11
11
  * @returns metadata or null if memory is not a valid image file format layout.
12
12
  */
13
13
  export declare function getBinaryImageMetadata(binaryData: DataView | ArrayBuffer): BinaryImageMetadata | null;
14
- export declare function getBmpMetadata(binaryData: any): {
15
- mimeType: string;
16
- width: number;
17
- height: number;
18
- } | null;
14
+ export declare function getBmpMetadata(binaryData: DataView | ArrayBuffer): BinaryImageMetadata | null;
19
15
  //# sourceMappingURL=binary-image-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"binary-image-api.d.ts","sourceRoot":"","sources":["../../../src/lib/category-api/binary-image-api.ts"],"names":[],"mappings":"AAQA,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAKF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,QAAQ,GAAG,WAAW,GACjC,mBAAmB,GAAG,IAAI,CAQ5B;AA2CD,wBAAgB,cAAc,CAAC,UAAU,KAAA;;;;SAmBxC"}
1
+ {"version":3,"file":"binary-image-api.d.ts","sourceRoot":"","sources":["../../../src/lib/category-api/binary-image-api.ts"],"names":[],"mappings":"AAUA,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAKF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,QAAQ,GAAG,WAAW,GACjC,mBAAmB,GAAG,IAAI,CAS5B;AA2DD,wBAAgB,cAAc,CAAC,UAAU,EAAE,QAAQ,GAAG,WAAW,GAAG,mBAAmB,GAAG,IAAI,CAmB7F"}
@@ -3,12 +3,17 @@
3
3
  // * Based on binary-gltf-utils under MIT license: Copyright (c) 2016-17 Karl Cheng
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.getBmpMetadata = exports.getBinaryImageMetadata = void 0;
6
+ // TODO: make these functions work for Node.js buffers?
7
+ // Quarantine references to Buffer to prevent bundler from adding big polyfills
8
+ // import {bufferToArrayBuffer} from '../node/buffer-to-array-buffer';
9
+ // TODO - this should be handled in @loaders.gl/polyfills
10
+ const parse_isobmff_binary_1 = require("./parse-isobmff-binary");
6
11
  const BIG_ENDIAN = false;
7
12
  const LITTLE_ENDIAN = true;
8
13
  /**
9
14
  * Extracts `{mimeType, width and height}` from a memory buffer containing a known image format
10
15
  * Currently supports `image/png`, `image/jpeg`, `image/bmp` and `image/gif`.
11
- * @param binaryData image file memory to parse
16
+ * @param binaryData: DataView | ArrayBuffer image file memory to parse
12
17
  * @returns metadata or null if memory is not a valid image file format layout.
13
18
  */
14
19
  function getBinaryImageMetadata(binaryData) {
@@ -16,9 +21,24 @@ function getBinaryImageMetadata(binaryData) {
16
21
  return (getPngMetadata(dataView) ||
17
22
  getJpegMetadata(dataView) ||
18
23
  getGifMetadata(dataView) ||
19
- getBmpMetadata(dataView));
24
+ getBmpMetadata(dataView) ||
25
+ getISOBMFFMetadata(dataView));
20
26
  }
21
27
  exports.getBinaryImageMetadata = getBinaryImageMetadata;
28
+ // ISOBMFF
29
+ function getISOBMFFMetadata(binaryData) {
30
+ const buffer = new Uint8Array(binaryData instanceof DataView ? binaryData.buffer : binaryData);
31
+ const mediaType = (0, parse_isobmff_binary_1.getISOBMFFMediaType)(buffer);
32
+ if (!mediaType) {
33
+ return null;
34
+ }
35
+ return {
36
+ mimeType: mediaType.mimeType,
37
+ // TODO - decode width and height
38
+ width: 0,
39
+ height: 0
40
+ };
41
+ }
22
42
  // PNG
23
43
  function getPngMetadata(binaryData) {
24
44
  const dataView = toDataView(binaryData);
@@ -1,5 +1,7 @@
1
+ /** Run-time browser detection of file formats requires async tests for most precise results */
2
+ export declare function getSupportedImageFormats(): Promise<Set<string>>;
1
3
  /**
2
- * Check if image MIME type is supported. Result is cached.
4
+ * Check if image MIME type is supported. Result is cached to avoid repeated tests.
3
5
  */
4
- export declare function _isImageFormatSupported(mimeType: string): boolean;
6
+ export declare function isImageFormatSupported(mimeType: string): boolean;
5
7
  //# sourceMappingURL=image-format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-format.d.ts","sourceRoot":"","sources":["../../../src/lib/category-api/image-format.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAKjE"}
1
+ {"version":3,"file":"image-format.d.ts","sourceRoot":"","sources":["../../../src/lib/category-api/image-format.ts"],"names":[],"mappings":"AAqBA,+FAA+F;AAC/F,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAgBrE;AAKD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQhE"}
@@ -1,63 +1,110 @@
1
1
  "use strict";
2
+ // loaders.gl, MIT license
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._isImageFormatSupported = void 0;
4
+ exports.isImageFormatSupported = exports.getSupportedImageFormats = void 0;
4
5
  const loader_utils_1 = require("@loaders.gl/loader-utils");
5
- // The following formats are supported by loaders.gl polyfills
6
- const NODE_FORMAT_SUPPORT = ['image/png', 'image/jpeg', 'image/gif'];
7
- /** Cache values for speed */
8
- const mimeTypeSupported = {};
6
+ const MIME_TYPES = [
7
+ 'image/png',
8
+ 'image/jpeg',
9
+ 'image/gif',
10
+ 'image/webp',
11
+ 'image/avif',
12
+ 'image/tiff',
13
+ // TODO - what is the correct type for SVG
14
+ 'image/svg',
15
+ 'image/svg+xml',
16
+ 'image/bmp',
17
+ 'image/vnd.microsoft.icon'
18
+ ];
19
+ /** Only one round of tests is performed */
20
+ const mimeTypeSupportedPromise = null;
21
+ /** Run-time browser detection of file formats requires async tests for most precise results */
22
+ async function getSupportedImageFormats() {
23
+ if (mimeTypeSupportedPromise) {
24
+ return await mimeTypeSupportedPromise;
25
+ }
26
+ const supportedMimeTypes = new Set();
27
+ for (const mimeType of MIME_TYPES) {
28
+ const supported = loader_utils_1.isBrowser
29
+ ? await checkBrowserImageFormatSupportAsync(mimeType)
30
+ : checkNodeImageFormatSupport(mimeType);
31
+ if (supported) {
32
+ supportedMimeTypes.add(mimeType);
33
+ }
34
+ }
35
+ return supportedMimeTypes;
36
+ }
37
+ exports.getSupportedImageFormats = getSupportedImageFormats;
38
+ /** Cache sync values for speed */
39
+ const mimeTypeSupportedSync = {};
9
40
  /**
10
- * Check if image MIME type is supported. Result is cached.
41
+ * Check if image MIME type is supported. Result is cached to avoid repeated tests.
11
42
  */
12
- function _isImageFormatSupported(mimeType) {
13
- if (mimeTypeSupported[mimeType] === undefined) {
14
- mimeTypeSupported[mimeType] = checkFormatSupport(mimeType);
43
+ function isImageFormatSupported(mimeType) {
44
+ if (mimeTypeSupportedSync[mimeType] === undefined) {
45
+ const supported = loader_utils_1.isBrowser
46
+ ? checkBrowserImageFormatSupport(mimeType)
47
+ : checkNodeImageFormatSupport(mimeType);
48
+ mimeTypeSupportedSync[mimeType] = supported;
15
49
  }
16
- return mimeTypeSupported[mimeType];
50
+ return mimeTypeSupportedSync[mimeType];
17
51
  }
18
- exports._isImageFormatSupported = _isImageFormatSupported;
52
+ exports.isImageFormatSupported = isImageFormatSupported;
19
53
  /**
20
- * Check if image MIME type is supported.
54
+ * Checks that polyfills are installed and that mimeType is supported by polyfills
55
+ * @todo Ideally polyfills should declare what formats they support, instead of storing that data here.
21
56
  */
22
- function checkFormatSupport(mimeType) {
57
+ function checkNodeImageFormatSupport(mimeType) {
58
+ /** @deprecated Remove these in 4.0 and rely on polyfills to inject them */
59
+ const NODE_FORMAT_SUPPORT = ['image/png', 'image/jpeg', 'image/gif'];
60
+ // @ts-ignore
61
+ const { _parseImageNode, _imageFormatsNode = NODE_FORMAT_SUPPORT } = globalThis;
62
+ return Boolean(_parseImageNode) && _imageFormatsNode.includes(mimeType);
63
+ }
64
+ /** Checks image format support synchronously.
65
+ * @note Unreliable, fails on AVIF
66
+ */
67
+ function checkBrowserImageFormatSupport(mimeType) {
23
68
  switch (mimeType) {
69
+ case 'image/avif': // Will fail
24
70
  case 'image/webp':
25
- return checkWebPSupport();
26
- case 'image/svg':
27
- return loader_utils_1.isBrowser;
71
+ return testBrowserImageFormatSupport(mimeType);
28
72
  default:
29
- if (!loader_utils_1.isBrowser) {
30
- // @ts-ignore
31
- const { _parseImageNode } = globalThis;
32
- return Boolean(_parseImageNode) && NODE_FORMAT_SUPPORT.includes(mimeType);
33
- }
34
73
  return true;
35
74
  }
36
75
  }
37
- /** Check WebPSupport synchronously */
38
- function checkWebPSupport() {
39
- if (!loader_utils_1.isBrowser) {
40
- return false;
41
- }
76
+ const TEST_IMAGE = {
77
+ 'image/avif': 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=',
78
+ // Lossy test image. Support for lossy images doesn't guarantee support for all WebP images.
79
+ 'image/webp': 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'
80
+ };
81
+ /** Checks WebP and AVIF support asynchronously */
82
+ async function checkBrowserImageFormatSupportAsync(mimeType) {
83
+ const dataURL = TEST_IMAGE[mimeType];
84
+ return dataURL ? await testBrowserImageFormatSupportAsync(dataURL) : true;
85
+ }
86
+ /**
87
+ * Checks browser synchronously
88
+ * Checks if toDataURL supports the mimeType.
89
+ * @note Imperfect testOn Chrome this is true for WebP but not for AVIF
90
+ */
91
+ function testBrowserImageFormatSupport(mimeType) {
42
92
  try {
43
93
  const element = document.createElement('canvas');
44
- return element.toDataURL('image/webp').indexOf('data:image/webp') === 0;
94
+ const dataURL = element.toDataURL(mimeType);
95
+ return dataURL.indexOf(`data:${mimeType}`) === 0;
45
96
  }
46
97
  catch {
47
98
  // Probably Safari...
48
99
  return false;
49
100
  }
50
101
  }
51
- // Note: better test but asynchronous
52
- // Lossy test image. Support for lossy images doesn't guarantee support for all WebP images.
53
- // https://stackoverflow.com/questions/5573096/detecting-webp-support
54
- // const WEBP_TEST_IMAGE = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA';
55
102
  // Check WebPSupport asynchronously
56
- // async function isWebPSupported() {
57
- // return new Promise( resolve => {
58
- // const image = new Image();
59
- // image.src = WEBP_TEST_IMAGE;
60
- // image.onload = image.onerror = function () {
61
- // resolve( image.height === 1 );
62
- // }
63
- // }
103
+ async function testBrowserImageFormatSupportAsync(testImageDataURL) {
104
+ return new Promise((resolve) => {
105
+ const image = new Image();
106
+ image.src = testImageDataURL;
107
+ image.onload = () => resolve(image.height > 0);
108
+ image.onerror = () => resolve(false);
109
+ });
110
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Box is a container format that can contain a variety of media related files,
3
+ * so we want to return information about which type of file is actually contained inside
4
+ */
5
+ export type BoxFileType = {
6
+ extension: string;
7
+ mimeType: string;
8
+ };
9
+ /**
10
+ * Tests if a buffer is in ISO base media file format (ISOBMFF) @see https://en.wikipedia.org/wiki/ISO_base_media_file_format
11
+ * (ISOBMFF is a media container standard based on the Apple QuickTime container format)
12
+ */
13
+ export declare function getISOBMFFMediaType(buffer: Uint8Array): BoxFileType | null;
14
+ /**
15
+ * brands explained @see https://github.com/strukturag/libheif/issues/83
16
+ * code adapted from @see https://github.com/sindresorhus/file-type/blob/main/core.js#L489-L492
17
+ */
18
+ export declare function decodeMajorBrand(buffer: Uint8Array): BoxFileType | null;
19
+ //# sourceMappingURL=parse-isobmff-binary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-isobmff-binary.d.ts","sourceRoot":"","sources":["../../../src/lib/category-api/parse-isobmff-binary.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAa1E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAmDvE"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ // loaders.gl, MIT license
3
+ // code adapted from https://github.com/sindresorhus/file-type under MIT license
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.decodeMajorBrand = exports.getISOBMFFMediaType = void 0;
6
+ /**
7
+ * Tests if a buffer is in ISO base media file format (ISOBMFF) @see https://en.wikipedia.org/wiki/ISO_base_media_file_format
8
+ * (ISOBMFF is a media container standard based on the Apple QuickTime container format)
9
+ */
10
+ function getISOBMFFMediaType(buffer) {
11
+ // Almost all ISO base media files start with `ftyp` box. (It's not required to be first, but it's recommended to be.)
12
+ if (!checkString(buffer, 'ftyp', 4)) {
13
+ return null;
14
+ }
15
+ // Extra check: test for 8859-1 printable characters (for simplicity, it's a mask which also catches one non-printable character).
16
+ if ((buffer[8] & 0x60) === 0x00) {
17
+ return null;
18
+ }
19
+ // `ftyp` box must contain a brand major identifier, which must consist of ISO 8859-1 printable characters.
20
+ return decodeMajorBrand(buffer);
21
+ }
22
+ exports.getISOBMFFMediaType = getISOBMFFMediaType;
23
+ /**
24
+ * brands explained @see https://github.com/strukturag/libheif/issues/83
25
+ * code adapted from @see https://github.com/sindresorhus/file-type/blob/main/core.js#L489-L492
26
+ */
27
+ function decodeMajorBrand(buffer) {
28
+ const brandMajor = getUTF8String(buffer, 8, 12).replace('\0', ' ').trim();
29
+ switch (brandMajor) {
30
+ case 'avif':
31
+ case 'avis':
32
+ return { extension: 'avif', mimeType: 'image/avif' };
33
+ default:
34
+ return null;
35
+ }
36
+ // We don't need these now, but they are easy to add
37
+ // case 'mif1':
38
+ // return {extension: 'heic', mimeType: 'image/heif'};
39
+ // case 'msf1':
40
+ // return {extension: 'heic', mimeType: 'image/heif-sequence'};
41
+ // case 'heic':
42
+ // case 'heix':
43
+ // return {extension: 'heic', mimeType: 'image/heic'};
44
+ // case 'hevc':
45
+ // case 'hevx':
46
+ // return {extension: 'heic', mimeType: 'image/heic-sequence'};
47
+ // case 'qt':
48
+ // return {ext: 'mov', mime: 'video/quicktime'};
49
+ // case 'M4V':
50
+ // case 'M4VH':
51
+ // case 'M4VP':
52
+ // return {ext: 'm4v', mime: 'video/x-m4v'};
53
+ // case 'M4P':
54
+ // return {ext: 'm4p', mime: 'video/mp4'};
55
+ // case 'M4B':
56
+ // return {ext: 'm4b', mime: 'audio/mp4'};
57
+ // case 'M4A':
58
+ // return {ext: 'm4a', mime: 'audio/x-m4a'};
59
+ // case 'F4V':
60
+ // return {ext: 'f4v', mime: 'video/mp4'};
61
+ // case 'F4P':
62
+ // return {ext: 'f4p', mime: 'video/mp4'};
63
+ // case 'F4A':
64
+ // return {ext: 'f4a', mime: 'audio/mp4'};
65
+ // case 'F4B':
66
+ // return {ext: 'f4b', mime: 'audio/mp4'};
67
+ // case 'crx':
68
+ // return {ext: 'cr3', mime: 'image/x-canon-cr3'};
69
+ // default:
70
+ // if (brandMajor.startsWith('3g')) {
71
+ // if (brandMajor.startsWith('3g2')) {
72
+ // return {ext: '3g2', mime: 'video/3gpp2'};
73
+ // }
74
+ // return {ext: '3gp', mime: 'video/3gpp'};
75
+ // }
76
+ // return {ext: 'mp4', mime: 'video/mp4'};
77
+ }
78
+ exports.decodeMajorBrand = decodeMajorBrand;
79
+ /** Interpret a chunk of bytes as a UTF8 string */
80
+ function getUTF8String(array, start, end) {
81
+ return String.fromCharCode(...array.slice(start, end));
82
+ }
83
+ function stringToBytes(string) {
84
+ return [...string].map((character) => character.charCodeAt(0));
85
+ }
86
+ function checkString(buffer, header, offset = 0) {
87
+ const headerBytes = stringToBytes(header);
88
+ for (let i = 0; i < headerBytes.length; ++i) {
89
+ if (headerBytes[i] !== buffer[i + offset]) {
90
+ return false;
91
+ }
92
+ }
93
+ return true;
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loaders.gl/images",
3
- "version": "3.4.0-alpha.1",
3
+ "version": "3.4.0-alpha.2",
4
4
  "description": "Framework-independent loaders and writers for images (PNG, JPG, ...)",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -32,7 +32,10 @@
32
32
  "build-bundle": "esbuild src/bundle.ts --outfile=dist/dist.min.js --bundle --minify --sourcemap"
33
33
  },
34
34
  "dependencies": {
35
- "@loaders.gl/loader-utils": "3.4.0-alpha.1"
35
+ "@loaders.gl/loader-utils": "3.4.0-alpha.2"
36
36
  },
37
- "gitHead": "4085b0323050e4361614471319a1fb4729547bbf"
37
+ "gitHead": "f1c00c124d8d0c41a138ff40afb0d1a00711bf2e",
38
+ "devDependencies": {
39
+ "@types/get-pixels": "^3.3.2"
40
+ }
38
41
  }
@@ -3,12 +3,13 @@ import {VERSION} from './lib/utils/version';
3
3
  import parseImage from './lib/parsers/parse-image';
4
4
  import {getBinaryImageMetadata} from './lib/category-api/binary-image-api';
5
5
 
6
- const EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'ico', 'svg'];
6
+ const EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'ico', 'svg', 'avif'];
7
7
  const MIME_TYPES = [
8
8
  'image/png',
9
9
  'image/jpeg',
10
10
  'image/gif',
11
11
  'image/webp',
12
+ 'image/avif',
12
13
  'image/bmp',
13
14
  'image/vnd.microsoft.icon',
14
15
  'image/svg+xml'
package/src/index.ts CHANGED
@@ -22,7 +22,8 @@ export {
22
22
  } from './lib/category-api/parsed-image-api';
23
23
 
24
24
  // EXPERIMENTAL
25
- export {_isImageFormatSupported} from './lib/category-api/image-format';
25
+ export {getSupportedImageFormats} from './lib/category-api/image-format';
26
+ export {isImageFormatSupported} from './lib/category-api/image-format';
26
27
 
27
- // DEPRECATED - Remove in V3 (fix dependency in luma.gl)
28
+ // DEPRECATED - Remove in V4 (fix dependency in luma.gl)
28
29
  export {loadImage} from './lib/texture-api/load-image';
@@ -6,6 +6,8 @@
6
6
  // import {bufferToArrayBuffer} from '../node/buffer-to-array-buffer';
7
7
  // TODO - this should be handled in @loaders.gl/polyfills
8
8
 
9
+ import {getISOBMFFMediaType} from './parse-isobmff-binary';
10
+
9
11
  /** MIME type, width and height extracted from binary compressed image data */
10
12
  export type BinaryImageMetadata = {
11
13
  mimeType: string;
@@ -19,7 +21,7 @@ const LITTLE_ENDIAN = true;
19
21
  /**
20
22
  * Extracts `{mimeType, width and height}` from a memory buffer containing a known image format
21
23
  * Currently supports `image/png`, `image/jpeg`, `image/bmp` and `image/gif`.
22
- * @param binaryData image file memory to parse
24
+ * @param binaryData: DataView | ArrayBuffer image file memory to parse
23
25
  * @returns metadata or null if memory is not a valid image file format layout.
24
26
  */
25
27
  export function getBinaryImageMetadata(
@@ -30,13 +32,30 @@ export function getBinaryImageMetadata(
30
32
  getPngMetadata(dataView) ||
31
33
  getJpegMetadata(dataView) ||
32
34
  getGifMetadata(dataView) ||
33
- getBmpMetadata(dataView)
35
+ getBmpMetadata(dataView) ||
36
+ getISOBMFFMetadata(dataView)
34
37
  );
35
38
  }
36
39
 
40
+ // ISOBMFF
41
+
42
+ function getISOBMFFMetadata(binaryData: DataView | ArrayBuffer): BinaryImageMetadata | null {
43
+ const buffer = new Uint8Array(binaryData instanceof DataView ? binaryData.buffer : binaryData);
44
+ const mediaType = getISOBMFFMediaType(buffer);
45
+ if (!mediaType) {
46
+ return null;
47
+ }
48
+ return {
49
+ mimeType: mediaType.mimeType,
50
+ // TODO - decode width and height
51
+ width: 0,
52
+ height: 0
53
+ };
54
+ }
55
+
37
56
  // PNG
38
57
 
39
- function getPngMetadata(binaryData) {
58
+ function getPngMetadata(binaryData: DataView | ArrayBuffer): BinaryImageMetadata | null {
40
59
  const dataView = toDataView(binaryData);
41
60
  // Check file contains the first 4 bytes of the PNG signature.
42
61
  const isPng = dataView.byteLength >= 24 && dataView.getUint32(0, BIG_ENDIAN) === 0x89504e47;
@@ -56,7 +75,7 @@ function getPngMetadata(binaryData) {
56
75
 
57
76
  // Extract size from a binary GIF file
58
77
  // TODO: GIF is not this simple
59
- function getGifMetadata(binaryData) {
78
+ function getGifMetadata(binaryData: DataView | ArrayBuffer): BinaryImageMetadata | null {
60
79
  const dataView = toDataView(binaryData);
61
80
  // Check first 4 bytes of the GIF signature ("GIF8").
62
81
  const isGif = dataView.byteLength >= 10 && dataView.getUint32(0, BIG_ENDIAN) === 0x47494638;
@@ -75,7 +94,7 @@ function getGifMetadata(binaryData) {
75
94
  // BMP
76
95
 
77
96
  // TODO: BMP is not this simple
78
- export function getBmpMetadata(binaryData) {
97
+ export function getBmpMetadata(binaryData: DataView | ArrayBuffer): BinaryImageMetadata | null {
79
98
  const dataView = toDataView(binaryData);
80
99
  // Check magic number is valid (first 2 characters should be "BM").
81
100
  // The mandatory bitmap file header is 14 bytes long.
@@ -99,7 +118,7 @@ export function getBmpMetadata(binaryData) {
99
118
  // JPEG
100
119
 
101
120
  // Extract width and height from a binary JPEG file
102
- function getJpegMetadata(binaryData) {
121
+ function getJpegMetadata(binaryData: DataView | ArrayBuffer): BinaryImageMetadata | null {
103
122
  const dataView = toDataView(binaryData);
104
123
  // Check file contains the JPEG "start of image" (SOI) marker
105
124
  // followed by another marker.