@flighthq/image 0.5.1-next.1069.554a2ac → 0.5.1-next.1197.6a425ce
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.
- package/README.md +2 -2
- package/dist/contract.d.ts +2 -1
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +2 -1
- package/dist/contract.js.map +1 -1
- package/dist/imageResource.d.ts +2 -2
- package/dist/imageResource.d.ts.map +1 -1
- package/dist/imageResource.js +17 -13
- package/dist/imageResource.js.map +1 -1
- package/dist/imageResourceFrom.d.ts +6 -12
- package/dist/imageResourceFrom.d.ts.map +1 -1
- package/dist/imageResourceFrom.js +10 -56
- package/dist/imageResourceFrom.js.map +1 -1
- package/dist/imageResourceReference.d.ts +2 -3
- package/dist/imageResourceReference.d.ts.map +1 -1
- package/dist/imageResourceReference.js +9 -23
- package/dist/imageResourceReference.js.map +1 -1
- package/dist/imageSourceDimensions.d.ts +6 -0
- package/dist/imageSourceDimensions.d.ts.map +1 -0
- package/dist/imageSourceDimensions.js +24 -0
- package/dist/imageSourceDimensions.js.map +1 -0
- package/dist/imageTestHelper.d.ts +5 -0
- package/dist/imageTestHelper.d.ts.map +1 -0
- package/dist/imageTestHelper.js +29 -0
- package/dist/imageTestHelper.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/imageResource.test.ts +13 -0
- package/src/imageResourceFrom.test.ts +40 -121
- package/src/imageResourceReference.test.ts +31 -85
- package/src/imageSourceDimensions.test.ts +88 -0
- package/src/imageTestHelper.test.ts +50 -0
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/image`. The `@flight
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/image@0.5.1-next.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/image@0.5.1-next.1197.6a425ce](https://github.com/flighthq/flight/tree/6a425ce64e6d501dac8218fe625e73d58da88639/packages/image)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/6a425ce64e6d501dac8218fe625e73d58da88639/LICENSE.md)
|
package/dist/contract.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export * from './imageResource';
|
|
2
2
|
export * from './imageResourceFrom';
|
|
3
|
+
export * from './imageSourceDimensions';
|
|
4
|
+
export * from './imageTestHelper';
|
|
3
5
|
export * from './imageResourceReference';
|
|
4
6
|
export { initializeCompressedImageResource } from './imageResource';
|
|
5
7
|
export { initializeEmbeddedImageResourceReference, initializeExternalImageResourceReference, } from './imageResourceReference';
|
|
6
|
-
export { initializeImageResourceFromCanvas, initializeImageResourceFromImageBitmap, initializeImageResourceFromImageElement, } from './imageResourceFrom';
|
|
7
8
|
//# sourceMappingURL=contract.d.ts.map
|
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAAE,iCAAiC,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EACL,wCAAwC,EACxC,wCAAwC,GACzC,MAAM,0BAA0B,CAAC
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAAE,iCAAiC,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EACL,wCAAwC,EACxC,wCAAwC,GACzC,MAAM,0BAA0B,CAAC"}
|
package/dist/contract.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export * from './imageResource';
|
|
2
2
|
export * from './imageResourceFrom';
|
|
3
|
+
export * from './imageSourceDimensions';
|
|
4
|
+
export * from './imageTestHelper';
|
|
3
5
|
export * from './imageResourceReference';
|
|
4
6
|
export { initializeCompressedImageResource } from './imageResource';
|
|
5
7
|
export { initializeEmbeddedImageResourceReference, initializeExternalImageResourceReference, } from './imageResourceReference';
|
|
6
|
-
export { initializeImageResourceFromCanvas, initializeImageResourceFromImageBitmap, initializeImageResourceFromImageElement, } from './imageResourceFrom';
|
|
7
8
|
//# sourceMappingURL=contract.js.map
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAAE,iCAAiC,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EACL,wCAAwC,EACxC,wCAAwC,GACzC,MAAM,0BAA0B,CAAC
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAAE,iCAAiC,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EACL,wCAAwC,EACxC,wCAAwC,GACzC,MAAM,0BAA0B,CAAC"}
|
package/dist/imageResource.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { CompressedImageData, CompressedImageResource, EntityConstruction, ImageResource } from '@flighthq/types/contract';
|
|
1
|
+
import type { CompressedImageData, CompressedImageResource, EntityConstruction, HostImageSource, ImageResource } from '@flighthq/types/contract';
|
|
2
2
|
export declare function cloneImageResource(resource: Readonly<ImageResource>): ImageResource;
|
|
3
3
|
export declare function createCompressedImageResource(compressed: Readonly<CompressedImageData>): CompressedImageResource;
|
|
4
|
-
export declare function createImageResource(image:
|
|
4
|
+
export declare function createImageResource(image: HostImageSource): ImageResource;
|
|
5
5
|
export declare function initializeCompressedImageResource(out: EntityConstruction<CompressedImageResource>, compressed: Readonly<CompressedImageData>): void;
|
|
6
6
|
export declare function invalidateImageResource(resource: ImageResource): void;
|
|
7
7
|
export declare function isImageResourceEmpty(resource: Readonly<ImageResource>): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageResource.d.ts","sourceRoot":"","sources":["../src/imageResource.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"imageResource.d.ts","sourceRoot":"","sources":["../src/imageResource.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAElB,eAAe,EACf,aAAa,EACd,MAAM,0BAA0B,CAAC;AAOlC,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,aAAa,CAUnF;AAED,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GAAG,uBAAuB,CAIhH;AAKD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,aAAa,CAWzE;AAID,wBAAgB,iCAAiC,CAC/C,GAAG,EAAE,kBAAkB,CAAC,uBAAuB,CAAC,EAChD,UAAU,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GACxC,IAAI,CAQN;AAGD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAGrE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,OAAO,CAE/E"}
|
package/dist/imageResource.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
2
|
import { CompressedImageTextureSourceKind, ImageTextureSourceKind } from '@flighthq/types/contract';
|
|
3
|
+
import { getHostImageSourceDimensions } from './imageSourceDimensions';
|
|
3
4
|
// Allocates a new resource identity over the same borrowed host image. The host handle is shared by
|
|
4
5
|
// reference; the clone owns an independent version counter for renderer cache invalidation.
|
|
5
6
|
export function cloneImageResource(resource) {
|
|
@@ -18,6 +19,9 @@ export function createCompressedImageResource(compressed) {
|
|
|
18
19
|
initializeCompressedImageResource(out, compressed);
|
|
19
20
|
return finishEntity(out);
|
|
20
21
|
}
|
|
22
|
+
// Wraps a borrowed host handle whose size is read through the registered host dimension resolver. A
|
|
23
|
+
// caller that already holds the size — a host factory building from its own canvas or decoded bitmap —
|
|
24
|
+
// sets width and height directly instead and needs no resolver.
|
|
21
25
|
export function createImageResource(image) {
|
|
22
26
|
const resource = allocateEntity();
|
|
23
27
|
resource.alphaType = DECODED_ALPHA_TYPE;
|
|
@@ -49,21 +53,19 @@ export function invalidateImageResource(resource) {
|
|
|
49
53
|
export function isImageResourceEmpty(resource) {
|
|
50
54
|
return resource.width <= 0 || resource.height <= 0;
|
|
51
55
|
}
|
|
52
|
-
//
|
|
53
|
-
//
|
|
56
|
+
// Re-reads pixel dimensions from the borrowed handle through the host resolver. With no resolver
|
|
57
|
+
// registered the resource keeps the dimensions it already carries, which is what a host factory that
|
|
58
|
+
// measured its own source at construction wants.
|
|
54
59
|
function updateImageResourceSize(resource) {
|
|
55
|
-
const
|
|
56
|
-
if (
|
|
60
|
+
const source = resource.source;
|
|
61
|
+
if (source === null)
|
|
57
62
|
return;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
resource.width = sized.width;
|
|
65
|
-
resource.height = sized.height;
|
|
66
|
-
}
|
|
63
|
+
_measured.height = resource.height;
|
|
64
|
+
_measured.width = resource.width;
|
|
65
|
+
if (!getHostImageSourceDimensions(source, _measured))
|
|
66
|
+
return;
|
|
67
|
+
resource.height = _measured.height;
|
|
68
|
+
resource.width = _measured.width;
|
|
67
69
|
}
|
|
68
70
|
// What a HOST decode yields, and the only honest default for a source whose pixels we never touch.
|
|
69
71
|
// Every browser image/canvas/ImageBitmap decode is straight-alpha sRGB, so declaring it is a statement
|
|
@@ -72,4 +74,6 @@ function updateImageResourceSize(resource) {
|
|
|
72
74
|
// `alphaType !== 'premultiplied'` guard starts protecting ImageResource the way it already protects Bitmap.
|
|
73
75
|
const DECODED_ALPHA_TYPE = 'straight';
|
|
74
76
|
const DECODED_GAMUT = 'srgb';
|
|
77
|
+
// Scratch pair reused by the size re-read; measuring a resource never allocates.
|
|
78
|
+
const _measured = { height: 0, width: 0 };
|
|
75
79
|
//# sourceMappingURL=imageResource.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageResource.js","sourceRoot":"","sources":["../src/imageResource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"imageResource.js","sourceRoot":"","sources":["../src/imageResource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AASzE,OAAO,EAAE,gCAAgC,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEpG,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAC;AAEvE,oGAAoG;AACpG,4FAA4F;AAC5F,MAAM,UAAU,kBAAkB,CAAC,QAAiC;IAClE,MAAM,GAAG,GAAG,cAAc,EAAiB,CAAC;IAC5C,GAAG,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC;IACnC,GAAG,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC3B,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC7B,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;IACzB,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC7B,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;IAC/B,GAAG,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC3B,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,UAAyC;IACrF,MAAM,GAAG,GAAG,cAAc,EAA2B,CAAC;IACtD,iCAAiC,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACnD,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED,oGAAoG;AACpG,uGAAuG;AACvG,gEAAgE;AAChE,MAAM,UAAU,mBAAmB,CAAC,KAAsB;IACxD,MAAM,QAAQ,GAAG,cAAc,EAAiB,CAAC;IACjD,QAAQ,CAAC,SAAS,GAAG,kBAAkB,CAAC;IACxC,QAAQ,CAAC,KAAK,GAAG,aAAa,CAAC;IAC/B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACpB,QAAQ,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACvC,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC;IACxB,QAAQ,CAAC,OAAO,GAAG,CAAC,CAAC;IACrB,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;IACnB,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAClC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,kGAAkG;AAClG,iDAAiD;AACjD,MAAM,UAAU,iCAAiC,CAC/C,GAAgD,EAChD,UAAyC;IAEzC,GAAG,CAAC,SAAS,GAAG,kBAAkB,CAAC;IACnC,GAAG,CAAC,UAAU,GAAG,UAAU,CAAC;IAC5B,GAAG,CAAC,KAAK,GAAG,aAAa,CAAC;IAC1B,GAAG,CAAC,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC;IACzC,GAAG,CAAC,IAAI,GAAG,gCAAgC,CAAC;IAC5C,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC;IAChB,GAAG,CAAC,KAAK,GAAG,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC;AACzC,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,uBAAuB,CAAC,QAAuB;IAC7D,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAClC,QAAQ,CAAC,OAAO,GAAG,CAAC,QAAQ,CAAC,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAiC;IACpE,OAAO,QAAQ,CAAC,KAAK,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,iGAAiG;AACjG,qGAAqG;AACrG,iDAAiD;AACjD,SAAS,uBAAuB,CAAC,QAAuB;IACtD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC/B,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO;IAC5B,SAAS,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IACnC,SAAS,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IACjC,IAAI,CAAC,4BAA4B,CAAC,MAAM,EAAE,SAAS,CAAC;QAAE,OAAO;IAC7D,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;IACnC,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;AACnC,CAAC;AAED,mGAAmG;AACnG,uGAAuG;AACvG,oGAAoG;AACpG,sGAAsG;AACtG,4GAA4G;AAC5G,MAAM,kBAAkB,GAAG,UAAU,CAAC;AACtC,MAAM,aAAa,GAAG,MAAM,CAAC;AAE7B,iFAAiF;AACjF,MAAM,SAAS,GAAwB,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC"}
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import type { Bitmap,
|
|
2
|
-
export declare function createImageResourceFromBitmap(
|
|
3
|
-
export declare function createImageResourceFromCanvas(canvas: HTMLCanvasElement): ImageResource;
|
|
4
|
-
export declare function createImageResourceFromImageBitmap(bitmap: ImageBitmap): ImageResource;
|
|
5
|
-
export declare function createImageResourceFromImageElement(img: HTMLImageElement): ImageResource;
|
|
6
|
-
export declare function initializeImageResourceFromCanvas(out: EntityConstruction<ImageResource>, canvas: HTMLCanvasElement): void;
|
|
7
|
-
export declare function initializeImageResourceFromImageBitmap(out: EntityConstruction<ImageResource>, bitmap: ImageBitmap): void;
|
|
8
|
-
export declare function initializeImageResourceFromImageElement(out: EntityConstruction<ImageResource>, img: HTMLImageElement): void;
|
|
1
|
+
import type { Bitmap, HostImageProvider, ImageResource } from '@flighthq/types/contract';
|
|
2
|
+
export declare function createImageResourceFromBitmap(hostImage: Readonly<HostImageProvider>, bitmap: Readonly<Bitmap>): ImageResource | null;
|
|
9
3
|
export declare function isImageUrlSameOrigin(url: string): boolean;
|
|
10
|
-
export declare function loadImageResourceFromBase64(
|
|
11
|
-
export declare function loadImageResourceFromBlob(
|
|
12
|
-
export declare function loadImageResourceFromBytes(
|
|
13
|
-
export declare function loadImageResourceFromUrl(
|
|
4
|
+
export declare function loadImageResourceFromBase64(hostImage: Readonly<HostImageProvider>, base64: string, mimeType: string, signal?: AbortSignal): Promise<ImageResource>;
|
|
5
|
+
export declare function loadImageResourceFromBlob(hostImage: Readonly<HostImageProvider>, blob: Blob, signal?: AbortSignal): Promise<ImageResource>;
|
|
6
|
+
export declare function loadImageResourceFromBytes(hostImage: Readonly<HostImageProvider>, bytes: Uint8Array, mimeType?: string, signal?: AbortSignal): Promise<ImageResource>;
|
|
7
|
+
export declare function loadImageResourceFromUrl(hostImage: Readonly<HostImageProvider>, url: string, crossOrigin?: 'anonymous' | 'use-credentials', signal?: AbortSignal): Promise<ImageResource>;
|
|
14
8
|
//# sourceMappingURL=imageResourceFrom.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageResourceFrom.d.ts","sourceRoot":"","sources":["../src/imageResourceFrom.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"imageResourceFrom.d.ts","sourceRoot":"","sources":["../src/imageResourceFrom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzF,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,QAAQ,CAAC,iBAAiB,CAAC,EACtC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GACvB,aAAa,GAAG,IAAI,CAItB;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOzD;AAED,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,QAAQ,CAAC,iBAAiB,CAAC,EACtC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,aAAa,CAAC,CAExB;AAED,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,QAAQ,CAAC,iBAAiB,CAAC,EACtC,IAAI,EAAE,IAAI,EACV,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,aAAa,CAAC,CAOxB;AAED,wBAAsB,0BAA0B,CAC9C,SAAS,EAAE,QAAQ,CAAC,iBAAiB,CAAC,EACtC,KAAK,EAAE,UAAU,EACjB,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,aAAa,CAAC,CAOxB;AAED,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,QAAQ,CAAC,iBAAiB,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,EAC7C,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,aAAa,CAAC,CAExB"}
|
|
@@ -1,54 +1,10 @@
|
|
|
1
|
-
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
1
|
import { detectImageMimeType } from '@flighthq/image-codec/contract';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const backend = host.graphics.image;
|
|
2
|
+
export function createImageResourceFromBitmap(hostImage, bitmap) {
|
|
3
|
+
const backend = hostImage;
|
|
6
4
|
if (backend.createImageFromBitmap === undefined)
|
|
7
5
|
return null;
|
|
8
6
|
return backend.createImageFromBitmap(bitmap);
|
|
9
7
|
}
|
|
10
|
-
export function createImageResourceFromCanvas(canvas) {
|
|
11
|
-
const out = allocateEntity();
|
|
12
|
-
initializeImageResourceFromCanvas(out, canvas);
|
|
13
|
-
return finishEntity(out);
|
|
14
|
-
}
|
|
15
|
-
export function createImageResourceFromImageBitmap(bitmap) {
|
|
16
|
-
const out = allocateEntity();
|
|
17
|
-
initializeImageResourceFromImageBitmap(out, bitmap);
|
|
18
|
-
return finishEntity(out);
|
|
19
|
-
}
|
|
20
|
-
export function createImageResourceFromImageElement(img) {
|
|
21
|
-
const out = allocateEntity();
|
|
22
|
-
initializeImageResourceFromImageElement(out, img);
|
|
23
|
-
return finishEntity(out);
|
|
24
|
-
}
|
|
25
|
-
export function initializeImageResourceFromCanvas(out, canvas) {
|
|
26
|
-
out.alphaType = DECODED_ALPHA_TYPE;
|
|
27
|
-
out.gamut = DECODED_GAMUT;
|
|
28
|
-
out.height = canvas.height;
|
|
29
|
-
out.kind = ImageTextureSourceKind;
|
|
30
|
-
out.source = canvas;
|
|
31
|
-
out.version = 0;
|
|
32
|
-
out.width = canvas.width;
|
|
33
|
-
}
|
|
34
|
-
export function initializeImageResourceFromImageBitmap(out, bitmap) {
|
|
35
|
-
out.alphaType = DECODED_ALPHA_TYPE;
|
|
36
|
-
out.gamut = DECODED_GAMUT;
|
|
37
|
-
out.height = bitmap.height;
|
|
38
|
-
out.kind = ImageTextureSourceKind;
|
|
39
|
-
out.source = bitmap;
|
|
40
|
-
out.version = 0;
|
|
41
|
-
out.width = bitmap.width;
|
|
42
|
-
}
|
|
43
|
-
export function initializeImageResourceFromImageElement(out, img) {
|
|
44
|
-
out.alphaType = DECODED_ALPHA_TYPE;
|
|
45
|
-
out.gamut = DECODED_GAMUT;
|
|
46
|
-
out.height = img.height;
|
|
47
|
-
out.kind = ImageTextureSourceKind;
|
|
48
|
-
out.source = img;
|
|
49
|
-
out.version = 0;
|
|
50
|
-
out.width = img.width;
|
|
51
|
-
}
|
|
52
8
|
export function isImageUrlSameOrigin(url) {
|
|
53
9
|
if (url.startsWith('data:') || url.startsWith('blob:'))
|
|
54
10
|
return true;
|
|
@@ -59,29 +15,27 @@ export function isImageUrlSameOrigin(url) {
|
|
|
59
15
|
return true;
|
|
60
16
|
}
|
|
61
17
|
}
|
|
62
|
-
export async function loadImageResourceFromBase64(
|
|
63
|
-
return loadImageResourceFromUrl(
|
|
18
|
+
export async function loadImageResourceFromBase64(hostImage, base64, mimeType, signal) {
|
|
19
|
+
return loadImageResourceFromUrl(hostImage, `data:${mimeType};base64,${base64}`, undefined, signal);
|
|
64
20
|
}
|
|
65
|
-
export async function loadImageResourceFromBlob(
|
|
21
|
+
export async function loadImageResourceFromBlob(hostImage, blob, signal) {
|
|
66
22
|
const url = URL.createObjectURL(blob);
|
|
67
23
|
try {
|
|
68
|
-
return await loadImageResourceFromUrl(
|
|
24
|
+
return await loadImageResourceFromUrl(hostImage, url, undefined, signal);
|
|
69
25
|
}
|
|
70
26
|
finally {
|
|
71
27
|
URL.revokeObjectURL(url);
|
|
72
28
|
}
|
|
73
29
|
}
|
|
74
|
-
export async function loadImageResourceFromBytes(
|
|
30
|
+
export async function loadImageResourceFromBytes(hostImage, bytes, mimeType, signal) {
|
|
75
31
|
const type = mimeType ?? detectImageMimeType(bytes);
|
|
76
32
|
if (type === null) {
|
|
77
33
|
throw new Error('Unable to determine image type from bytes');
|
|
78
34
|
}
|
|
79
35
|
const buf = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
80
|
-
return loadImageResourceFromBlob(
|
|
36
|
+
return loadImageResourceFromBlob(hostImage, new Blob([buf], { type }), signal);
|
|
81
37
|
}
|
|
82
|
-
export async function loadImageResourceFromUrl(
|
|
83
|
-
return
|
|
38
|
+
export async function loadImageResourceFromUrl(hostImage, url, crossOrigin, signal) {
|
|
39
|
+
return hostImage.loadImageFromUrl(url, crossOrigin, signal);
|
|
84
40
|
}
|
|
85
|
-
const DECODED_ALPHA_TYPE = 'straight';
|
|
86
|
-
const DECODED_GAMUT = 'srgb';
|
|
87
41
|
//# sourceMappingURL=imageResourceFrom.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageResourceFrom.js","sourceRoot":"","sources":["../src/imageResourceFrom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"imageResourceFrom.js","sourceRoot":"","sources":["../src/imageResourceFrom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAGrE,MAAM,UAAU,6BAA6B,CAC3C,SAAsC,EACtC,MAAwB;IAExB,MAAM,OAAO,GAAG,SAAS,CAAC;IAC1B,IAAI,OAAO,CAAC,qBAAqB,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC7D,OAAO,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,SAAsC,EACtC,MAAc,EACd,QAAgB,EAChB,MAAoB;IAEpB,OAAO,wBAAwB,CAAC,SAAS,EAAE,QAAQ,QAAQ,WAAW,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AACrG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,SAAsC,EACtC,IAAU,EACV,MAAoB;IAEpB,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,OAAO,MAAM,wBAAwB,CAAC,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC3E,CAAC;YAAS,CAAC;QACT,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,SAAsC,EACtC,KAAiB,EACjB,QAAiB,EACjB,MAAoB;IAEpB,MAAM,IAAI,GAAG,QAAQ,IAAI,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACpD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,GAAG,GAAI,KAAK,CAAC,MAAsB,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;IACvG,OAAO,yBAAyB,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,SAAsC,EACtC,GAAW,EACX,WAA6C,EAC7C,MAAoB;IAEpB,OAAO,SAAS,CAAC,gBAAgB,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import type { AlphaType, EmbeddedImageResourceReference, ExternalImageResourceReference, ImageResourceFailure, ImageResourceFetch, ImageResourceReference, ImageResourceReferenceResolutionExplanation, TextureSource, EntityConstruction } from '@flighthq/types/contract';
|
|
1
|
+
import type { AlphaType, EmbeddedImageResourceReference, ExternalImageResourceReference, ImageResourceFailure, ImageResourceFetch, ImageResourceReference, ImageBitmapCompositionResolver, ImageResourceReferenceResolutionExplanation, TextureSource, EntityConstruction } from '@flighthq/types/contract';
|
|
2
2
|
export declare function createEmbeddedImageResourceReference(bytes: Uint8Array, mimeType?: string | null, alphaType?: AlphaType): EmbeddedImageResourceReference;
|
|
3
3
|
export declare function createExternalImageResourceReference(uri: string, basePath?: string | null): ExternalImageResourceReference;
|
|
4
4
|
export declare function createImageResourceFailure(cause: unknown): ImageResourceFailure;
|
|
5
|
-
export declare function disableImageBitmapComposition(): void;
|
|
6
|
-
export declare function enableImageBitmapComposition(): void;
|
|
7
5
|
export declare function explainImageResourceReferenceResolution(ref: Readonly<ImageResourceReference>): ImageResourceReferenceResolutionExplanation;
|
|
8
6
|
export declare function initializeEmbeddedImageResourceReference(out: EntityConstruction<EmbeddedImageResourceReference>, bytes: Uint8Array, mimeType?: string | null, alphaType?: AlphaType): void;
|
|
9
7
|
export declare function initializeExternalImageResourceReference(out: EntityConstruction<ExternalImageResourceReference>, uri: string, basePath?: string | null): void;
|
|
8
|
+
export declare function registerImageBitmapCompositionResolver(resolver: ImageBitmapCompositionResolver | null): void;
|
|
10
9
|
export declare function resetFailedImageResourceReference(ref: ImageResourceReference): boolean;
|
|
11
10
|
export declare function resolveImageResourceReference(ref: ImageResourceReference, fetch: ImageResourceFetch, signal: AbortSignal): Promise<TextureSource | null>;
|
|
12
11
|
//# sourceMappingURL=imageResourceReference.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageResourceReference.d.ts","sourceRoot":"","sources":["../src/imageResourceReference.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"imageResourceReference.d.ts","sourceRoot":"","sources":["../src/imageResourceReference.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,SAAS,EAET,8BAA8B,EAC9B,8BAA8B,EAC9B,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,8BAA8B,EAC9B,2CAA2C,EAC3C,aAAa,EACb,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAQlC,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,UAAU,EACjB,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,SAAS,GAAE,SAAsB,GAChC,8BAA8B,CAIhC;AAED,wBAAgB,oCAAoC,CAClD,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAM,GAAG,IAAW,GAC7B,8BAA8B,CAIhC;AAiCD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAa/E;AAID,wBAAgB,uCAAuC,CACrD,GAAG,EAAE,QAAQ,CAAC,sBAAsB,CAAC,GACpC,2CAA2C,CAO7C;AAOD,wBAAgB,wCAAwC,CACtD,GAAG,EAAE,kBAAkB,CAAC,8BAA8B,CAAC,EACvD,KAAK,EAAE,UAAU,EACjB,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,SAAS,GAAE,SAAsB,GAChC,IAAI,CAQN;AAED,wBAAgB,wCAAwC,CACtD,GAAG,EAAE,kBAAkB,CAAC,8BAA8B,CAAC,EACvD,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAM,GAAG,IAAW,GAC7B,IAAI,CAQN;AAMD,wBAAgB,sCAAsC,CAAC,QAAQ,EAAE,8BAA8B,GAAG,IAAI,GAAG,IAAI,CAE5G;AAID,wBAAgB,iCAAiC,CAAC,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAKtF;AAUD,wBAAsB,6BAA6B,CACjD,GAAG,EAAE,sBAAsB,EAC3B,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAkC/B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
|
-
import { decodeImage, decodeImagePremultiplied, explainImageDecodeFailure
|
|
2
|
+
import { decodeImage, decodeImagePremultiplied, explainImageDecodeFailure } from '@flighthq/image-codec/contract';
|
|
3
3
|
import { BitmapTextureSourceKind, ImageResourceFailureKind, ImageResourceReferenceKind, ResourceResolutionState, } from '@flighthq/types/contract';
|
|
4
4
|
export function createEmbeddedImageResourceReference(bytes, mimeType = null, alphaType = 'straight') {
|
|
5
5
|
const out = allocateEntity();
|
|
@@ -53,28 +53,6 @@ export function createImageResourceFailure(cause) {
|
|
|
53
53
|
out.name = null;
|
|
54
54
|
return finishEntity(out);
|
|
55
55
|
}
|
|
56
|
-
export function disableImageBitmapComposition() {
|
|
57
|
-
_resolveImageBitmapComposition = null;
|
|
58
|
-
}
|
|
59
|
-
// Installs the optional decoded-pixel join without making an ordinary embedded-image consumer retain
|
|
60
|
-
// its registry lookup or straight-decode branch. A format package calls this beside its composer
|
|
61
|
-
// registrations; until then the nullable hook leaves the original hot path byte-for-byte tree-shakable.
|
|
62
|
-
export function enableImageBitmapComposition() {
|
|
63
|
-
_resolveImageBitmapComposition = resolveImageBitmapComposition;
|
|
64
|
-
}
|
|
65
|
-
async function resolveImageBitmapComposition(ref, signal) {
|
|
66
|
-
signal.throwIfAborted();
|
|
67
|
-
const composition = ref.bitmapComposition;
|
|
68
|
-
const composer = getImageBitmapComposer(composition.kind);
|
|
69
|
-
if (composer === null)
|
|
70
|
-
return null;
|
|
71
|
-
// A composer always receives straight decoded pixels. It may also own a raw raster with no MIME
|
|
72
|
-
// decoder, in which case decoded is null and its plain payload is the complete input.
|
|
73
|
-
const decoded = await decodeImage(ref.bytes, ref.mimeType ?? undefined);
|
|
74
|
-
signal.throwIfAborted();
|
|
75
|
-
return composer(decoded, composition.payload);
|
|
76
|
-
}
|
|
77
|
-
let _resolveImageBitmapComposition = null;
|
|
78
56
|
// Returns a detached plain-data explanation suitable for logs, tools, and serialization. It never throws
|
|
79
57
|
// and exposes no resolver runtime or raw thrown value.
|
|
80
58
|
export function explainImageResourceReferenceResolution(ref) {
|
|
@@ -85,6 +63,7 @@ export function explainImageResourceReferenceResolution(ref) {
|
|
|
85
63
|
state: ref.state,
|
|
86
64
|
};
|
|
87
65
|
}
|
|
66
|
+
let _resolveImageBitmapComposition = null;
|
|
88
67
|
// `bytes` is retained as the view the container handed over, not a copy: a parser carves an image payload
|
|
89
68
|
// out of its source and the reference borrows it, so a document that never resolves an image never pays for
|
|
90
69
|
// its pixels. `textures` starts empty and each waiting Texture subscribes itself.
|
|
@@ -106,6 +85,13 @@ export function initializeExternalImageResourceReference(out, uri, basePath = nu
|
|
|
106
85
|
out.textures = [];
|
|
107
86
|
out.uri = uri;
|
|
108
87
|
}
|
|
88
|
+
// The slot the optional decoded-pixel join installs into, so an ordinary embedded-image consumer keeps
|
|
89
|
+
// neither a composer registry nor a second decode branch. Whoever owns the composers — the web host
|
|
90
|
+
// today, through enableWebImageBitmapComposition — puts its resolver here and passes null to take it back;
|
|
91
|
+
// until something does, the nullable hook leaves the original hot path byte-for-byte tree-shakable.
|
|
92
|
+
export function registerImageBitmapCompositionResolver(resolver) {
|
|
93
|
+
_resolveImageBitmapComposition = resolver;
|
|
94
|
+
}
|
|
109
95
|
// Returns a failed reference to the requestable state. Loading/resolved/unresolved references are unchanged
|
|
110
96
|
// so this atom cannot invalidate live work or a successfully bound resource accidentally.
|
|
111
97
|
export function resetFailedImageResourceReference(ref) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageResourceReference.js","sourceRoot":"","sources":["../src/imageResourceReference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,
|
|
1
|
+
{"version":3,"file":"imageResourceReference.js","sourceRoot":"","sources":["../src/imageResourceReference.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HostImageDimensionResolver, HostImageDimensions, HostImageSource } from '@flighthq/types/contract';
|
|
2
|
+
export declare function getHostImageSourceDimensions(source: HostImageSource, out: HostImageDimensions): boolean;
|
|
3
|
+
export declare function hasHostImageDimensionResolver(): boolean;
|
|
4
|
+
export declare function registerHostImageDimensionResolver(resolver: HostImageDimensionResolver): void;
|
|
5
|
+
export declare function unregisterHostImageDimensionResolver(): void;
|
|
6
|
+
//# sourceMappingURL=imageSourceDimensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageSourceDimensions.d.ts","sourceRoot":"","sources":["../src/imageSourceDimensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAKjH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAGvG;AAID,wBAAgB,6BAA6B,IAAI,OAAO,CAEvD;AAKD,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,0BAA0B,GAAG,IAAI,CAE7F;AAED,wBAAgB,oCAAoC,IAAI,IAAI,CAE3D"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Reads the pixel size of a borrowed host handle through the registered host resolver, returning false
|
|
2
|
+
// when no resolver is registered or the registered one does not recognize this handle. `out` is left
|
|
3
|
+
// untouched in both cases, so a caller keeps whatever dimensions it already had.
|
|
4
|
+
export function getHostImageSourceDimensions(source, out) {
|
|
5
|
+
if (_resolver === null)
|
|
6
|
+
return false;
|
|
7
|
+
return _resolver(source, out);
|
|
8
|
+
}
|
|
9
|
+
// True once a host has installed its resolver. A caller that measures sources can require this rather
|
|
10
|
+
// than discovering silently-zero dimensions at draw time.
|
|
11
|
+
export function hasHostImageDimensionResolver() {
|
|
12
|
+
return _resolver !== null;
|
|
13
|
+
}
|
|
14
|
+
// Installs the host's measurer. Opt-in, like every other host capability: an application registers the
|
|
15
|
+
// one its platform provides (registerWebImageDimensionResolver for the browser) and nothing in this
|
|
16
|
+
// package reaches for a platform global. Last registration wins.
|
|
17
|
+
export function registerHostImageDimensionResolver(resolver) {
|
|
18
|
+
_resolver = resolver;
|
|
19
|
+
}
|
|
20
|
+
export function unregisterHostImageDimensionResolver() {
|
|
21
|
+
_resolver = null;
|
|
22
|
+
}
|
|
23
|
+
let _resolver = null;
|
|
24
|
+
//# sourceMappingURL=imageSourceDimensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageSourceDimensions.js","sourceRoot":"","sources":["../src/imageSourceDimensions.ts"],"names":[],"mappings":"AAEA,uGAAuG;AACvG,qGAAqG;AACrG,iFAAiF;AACjF,MAAM,UAAU,4BAA4B,CAAC,MAAuB,EAAE,GAAwB;IAC5F,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,sGAAsG;AACtG,0DAA0D;AAC1D,MAAM,UAAU,6BAA6B;IAC3C,OAAO,SAAS,KAAK,IAAI,CAAC;AAC5B,CAAC;AAED,uGAAuG;AACvG,oGAAoG;AACpG,iEAAiE;AACjE,MAAM,UAAU,kCAAkC,CAAC,QAAoC;IACrF,SAAS,GAAG,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,oCAAoC;IAClD,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,IAAI,SAAS,GAAsC,IAAI,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { HostImageDimensionResolver } from '@flighthq/types/contract';
|
|
2
|
+
export declare function registerTestImageDimensionResolver(): void;
|
|
3
|
+
export declare const testImageDimensionResolver: HostImageDimensionResolver;
|
|
4
|
+
export declare function unregisterTestImageDimensionResolver(): void;
|
|
5
|
+
//# sourceMappingURL=imageTestHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageTestHelper.d.ts","sourceRoot":"","sources":["../src/imageTestHelper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAc3E,wBAAgB,kCAAkC,IAAI,IAAI,CAEzD;AAID,eAAO,MAAM,0BAA0B,EAAE,0BAMxC,CAAC;AAGF,wBAAgB,oCAAoC,IAAI,IAAI,CAE3D"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { registerHostImageDimensionResolver, unregisterHostImageDimensionResolver } from './imageSourceDimensions';
|
|
2
|
+
// Measuring a borrowed handle belongs to the host, which leaves every portable package's tests needing a
|
|
3
|
+
// host they must not depend on: pulling @flighthq/host-web into a renderer package's tests would invert
|
|
4
|
+
// the layering the seam cleanup established. This is the portable stand-in — the same structural read a
|
|
5
|
+
// browser host performs, owned by the package that owns the seam, so a test names one dependency instead
|
|
6
|
+
// of hand-rolling a resolver literal.
|
|
7
|
+
//
|
|
8
|
+
// Register in beforeEach and clear in afterEach. Clearing is not tidiness: the slot is module state
|
|
9
|
+
// shared by every test file in a Vitest process, so a file that registers and never clears can satisfy a
|
|
10
|
+
// LATER file's missing registration. That is not hypothetical — it hid a real defect, where
|
|
11
|
+
// scene2d-canvas passed in the broad suite and failed run on its own.
|
|
12
|
+
export function registerTestImageDimensionResolver() {
|
|
13
|
+
registerHostImageDimensionResolver(testImageDimensionResolver);
|
|
14
|
+
}
|
|
15
|
+
// Reads width and height off any handle that carries them, and declines one that does not, exactly as a
|
|
16
|
+
// host resolver must.
|
|
17
|
+
export const testImageDimensionResolver = (source, out) => {
|
|
18
|
+
const sized = source;
|
|
19
|
+
if (typeof sized.width !== 'number' || typeof sized.height !== 'number')
|
|
20
|
+
return false;
|
|
21
|
+
out.height = sized.height;
|
|
22
|
+
out.width = sized.width;
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
// Pairs with registerTestImageDimensionResolver so a suite leaves the slot as it found it.
|
|
26
|
+
export function unregisterTestImageDimensionResolver() {
|
|
27
|
+
unregisterHostImageDimensionResolver();
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=imageTestHelper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageTestHelper.js","sourceRoot":"","sources":["../src/imageTestHelper.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kCAAkC,EAAE,oCAAoC,EAAE,MAAM,yBAAyB,CAAC;AAEnH,yGAAyG;AACzG,wGAAwG;AACxG,wGAAwG;AACxG,yGAAyG;AACzG,sCAAsC;AACtC,EAAE;AACF,oGAAoG;AACpG,yGAAyG;AACzG,4FAA4F;AAC5F,sEAAsE;AACtE,MAAM,UAAU,kCAAkC;IAChD,kCAAkC,CAAC,0BAA0B,CAAC,CAAC;AACjE,CAAC;AAED,wGAAwG;AACxG,sBAAsB;AACtB,MAAM,CAAC,MAAM,0BAA0B,GAA+B,CAAC,MAAM,EAAE,GAAG,EAAW,EAAE;IAC7F,MAAM,KAAK,GAAG,MAA+D,CAAC;IAC9E,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtF,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC1B,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACxB,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,2FAA2F;AAC3F,MAAM,UAAU,oCAAoC;IAClD,oCAAoC,EAAE,CAAC;AACzC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { cloneImageResource, createCompressedImageResource, createEmbeddedImageResourceReference, createExternalImageResourceReference, createImageResource, createImageResourceFailure, createImageResourceFromBitmap,
|
|
1
|
+
export { cloneImageResource, createCompressedImageResource, createEmbeddedImageResourceReference, createExternalImageResourceReference, createImageResource, createImageResourceFailure, createImageResourceFromBitmap, explainImageResourceReferenceResolution, isImageResourceEmpty, isImageUrlSameOrigin, loadImageResourceFromBase64, loadImageResourceFromBlob, loadImageResourceFromBytes, loadImageResourceFromUrl, resetFailedImageResourceReference, resolveImageResourceReference, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,oCAAoC,EACpC,oCAAoC,EACpC,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,oCAAoC,EACpC,oCAAoC,EACpC,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,uCAAuC,EACvC,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,iCAAiC,EACjC,6BAA6B,GAC9B,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { cloneImageResource, createCompressedImageResource, createEmbeddedImageResourceReference, createExternalImageResourceReference, createImageResource, createImageResourceFailure, createImageResourceFromBitmap,
|
|
1
|
+
export { cloneImageResource, createCompressedImageResource, createEmbeddedImageResourceReference, createExternalImageResourceReference, createImageResource, createImageResourceFailure, createImageResourceFromBitmap, explainImageResourceReferenceResolution, isImageResourceEmpty, isImageUrlSameOrigin, loadImageResourceFromBase64, loadImageResourceFromBlob, loadImageResourceFromBytes, loadImageResourceFromUrl, resetFailedImageResourceReference, resolveImageResourceReference, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,oCAAoC,EACpC,oCAAoC,EACpC,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,oCAAoC,EACpC,oCAAoC,EACpC,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,uCAAuC,EACvC,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,iCAAiC,EACjC,6BAA6B,GAC9B,MAAM,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/image",
|
|
3
|
-
"version": "0.5.1-next.
|
|
3
|
+
"version": "0.5.1-next.1197.6a425ce",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/entity": "0.5.1-next.
|
|
42
|
-
"@flighthq/image-codec": "0.5.1-next.
|
|
43
|
-
"@flighthq/types": "0.5.1-next.
|
|
41
|
+
"@flighthq/entity": "0.5.1-next.1197.6a425ce",
|
|
42
|
+
"@flighthq/image-codec": "0.5.1-next.1197.6a425ce",
|
|
43
|
+
"@flighthq/types": "0.5.1-next.1197.6a425ce"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"typescript": "^5.3.0"
|
|
@@ -9,6 +9,19 @@ import {
|
|
|
9
9
|
invalidateImageResource,
|
|
10
10
|
isImageResourceEmpty,
|
|
11
11
|
} from './imageResource';
|
|
12
|
+
import { registerTestImageDimensionResolver, unregisterTestImageDimensionResolver } from './imageTestHelper';
|
|
13
|
+
|
|
14
|
+
// Measuring a borrowed handle is the host's job, so these tests install the portable stand-in. Without
|
|
15
|
+
// one a resource keeps whatever size it was constructed with, which is the behavior
|
|
16
|
+
// imageSourceDimensions.test.ts covers. Clearing after each test is what keeps this file from silently
|
|
17
|
+
// supplying a resolver to a later one.
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
registerTestImageDimensionResolver();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
unregisterTestImageDimensionResolver();
|
|
24
|
+
});
|
|
12
25
|
|
|
13
26
|
function makeCompressed(): CompressedImageData {
|
|
14
27
|
return {
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
|
-
import type { Bitmap,
|
|
2
|
+
import type { Bitmap, HostImageProvider, ImageResource } from '@flighthq/types/contract';
|
|
3
3
|
import { BitmapTextureSourceKind, EntityRuntimeKey } from '@flighthq/types/contract';
|
|
4
4
|
|
|
5
5
|
import { createImageResource } from './imageResource';
|
|
6
6
|
import {
|
|
7
7
|
createImageResourceFromBitmap,
|
|
8
|
-
createImageResourceFromCanvas,
|
|
9
|
-
createImageResourceFromImageBitmap,
|
|
10
|
-
createImageResourceFromImageElement,
|
|
11
|
-
initializeImageResourceFromCanvas,
|
|
12
|
-
initializeImageResourceFromImageBitmap,
|
|
13
|
-
initializeImageResourceFromImageElement,
|
|
14
8
|
isImageUrlSameOrigin,
|
|
15
9
|
loadImageResourceFromBase64,
|
|
16
10
|
loadImageResourceFromBlob,
|
|
17
11
|
loadImageResourceFromBytes,
|
|
18
12
|
loadImageResourceFromUrl,
|
|
19
13
|
} from './imageResourceFrom';
|
|
14
|
+
import { registerTestImageDimensionResolver, unregisterTestImageDimensionResolver } from './imageTestHelper';
|
|
20
15
|
|
|
21
|
-
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
registerTestImageDimensionResolver();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
unregisterTestImageDimensionResolver();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function webHost(): { readonly graphics: { readonly image: HostImageProvider } } {
|
|
22
25
|
return {
|
|
23
26
|
graphics: {
|
|
24
27
|
image: {
|
|
@@ -30,7 +33,7 @@ function webHost(): HasGraphicsImage {
|
|
|
30
33
|
const domImageData = new globalThis.ImageData(bitmap.width, bitmap.height);
|
|
31
34
|
domImageData.data.set(bitmap.data);
|
|
32
35
|
canvas.getContext('2d')!.putImageData(domImageData, 0, 0);
|
|
33
|
-
return
|
|
36
|
+
return createImageResource(canvas);
|
|
34
37
|
},
|
|
35
38
|
async loadImageFromUrl(
|
|
36
39
|
url: string,
|
|
@@ -59,14 +62,14 @@ function webHost(): HasGraphicsImage {
|
|
|
59
62
|
} else {
|
|
60
63
|
await img.decode();
|
|
61
64
|
}
|
|
62
|
-
return
|
|
65
|
+
return createImageResource(img);
|
|
63
66
|
},
|
|
64
67
|
},
|
|
65
68
|
},
|
|
66
|
-
} as
|
|
69
|
+
} as { readonly graphics: { readonly image: HostImageProvider } };
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
let host:
|
|
72
|
+
let host: { readonly graphics: { readonly image: HostImageProvider } };
|
|
70
73
|
|
|
71
74
|
beforeEach(() => {
|
|
72
75
|
host = webHost();
|
|
@@ -84,25 +87,25 @@ describe('createImageResourceFromBitmap', () => {
|
|
|
84
87
|
const bitmap = createTestBitmap(3, 2);
|
|
85
88
|
const expected = {} as ImageResource;
|
|
86
89
|
const createImageFromBitmap = vi.fn((_bitmap: Readonly<Bitmap>) => expected);
|
|
87
|
-
const backend:
|
|
90
|
+
const backend: HostImageProvider = {
|
|
88
91
|
[EntityRuntimeKey]: undefined,
|
|
89
92
|
createImageFromBitmap,
|
|
90
93
|
loadImageFromUrl: vi.fn(),
|
|
91
94
|
};
|
|
92
|
-
const customHost = { graphics: { image: backend } } as
|
|
95
|
+
const customHost = { graphics: { image: backend } } as { readonly graphics: { readonly image: HostImageProvider } };
|
|
93
96
|
vi.stubGlobal('document', undefined);
|
|
94
97
|
|
|
95
|
-
expect(createImageResourceFromBitmap(customHost, bitmap)).toBe(expected);
|
|
98
|
+
expect(createImageResourceFromBitmap(customHost.graphics.image, bitmap)).toBe(expected);
|
|
96
99
|
expect(createImageFromBitmap).toHaveBeenCalledWith(bitmap);
|
|
97
100
|
});
|
|
98
101
|
|
|
99
102
|
it('returns null for backend absence without throwing or falling back to DOM', () => {
|
|
100
103
|
const createElement = vi.spyOn(document, 'createElement');
|
|
101
|
-
const backend:
|
|
102
|
-
const customHost = { graphics: { image: backend } } as
|
|
104
|
+
const backend: HostImageProvider = { [EntityRuntimeKey]: undefined, loadImageFromUrl: vi.fn() };
|
|
105
|
+
const customHost = { graphics: { image: backend } } as { readonly graphics: { readonly image: HostImageProvider } };
|
|
103
106
|
|
|
104
|
-
expect(() => createImageResourceFromBitmap(customHost, createTestBitmap(1, 1))).not.toThrow();
|
|
105
|
-
expect(createImageResourceFromBitmap(customHost, createTestBitmap(1, 1))).toBeNull();
|
|
107
|
+
expect(() => createImageResourceFromBitmap(customHost.graphics.image, createTestBitmap(1, 1))).not.toThrow();
|
|
108
|
+
expect(createImageResourceFromBitmap(customHost.graphics.image, createTestBitmap(1, 1))).toBeNull();
|
|
106
109
|
expect(createElement).not.toHaveBeenCalled();
|
|
107
110
|
});
|
|
108
111
|
|
|
@@ -116,7 +119,7 @@ describe('createImageResourceFromBitmap', () => {
|
|
|
116
119
|
bitmap.kind = BitmapTextureSourceKind;
|
|
117
120
|
bitmap.version = 0;
|
|
118
121
|
bitmap.width = 4;
|
|
119
|
-
const resource = createImageResourceFromBitmap(host, bitmap);
|
|
122
|
+
const resource = createImageResourceFromBitmap(host.graphics.image, bitmap);
|
|
120
123
|
expect(resource).not.toBeNull();
|
|
121
124
|
if (resource === null) return;
|
|
122
125
|
expect(resource.width).toBe(4);
|
|
@@ -138,92 +141,6 @@ function createTestBitmap(width: number, height: number): Bitmap {
|
|
|
138
141
|
return finishEntity(out);
|
|
139
142
|
}
|
|
140
143
|
|
|
141
|
-
describe('createImageResourceFromCanvas', () => {
|
|
142
|
-
it('wraps a canvas with correct dimensions', () => {
|
|
143
|
-
const canvas = document.createElement('canvas');
|
|
144
|
-
canvas.width = 320;
|
|
145
|
-
canvas.height = 240;
|
|
146
|
-
const resource = createImageResourceFromCanvas(canvas);
|
|
147
|
-
|
|
148
|
-
expect(resource.source).toBe(canvas);
|
|
149
|
-
expect(resource.width).toBe(320);
|
|
150
|
-
expect(resource.height).toBe(240);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('reflects the canvas dimensions at wrap time', () => {
|
|
154
|
-
const canvas = document.createElement('canvas');
|
|
155
|
-
canvas.width = 0;
|
|
156
|
-
canvas.height = 0;
|
|
157
|
-
const resource = createImageResourceFromCanvas(canvas);
|
|
158
|
-
|
|
159
|
-
expect(resource.width).toBe(0);
|
|
160
|
-
expect(resource.height).toBe(0);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('returns a new object each call', () => {
|
|
164
|
-
const canvas = document.createElement('canvas');
|
|
165
|
-
expect(createImageResourceFromCanvas(canvas)).not.toBe(createImageResourceFromCanvas(canvas));
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe('createImageResourceFromImageBitmap', () => {
|
|
170
|
-
it('wraps an ImageBitmap with correct dimensions', () => {
|
|
171
|
-
const bitmap = { width: 64, height: 128, close: () => {} } as ImageBitmap;
|
|
172
|
-
const resource = createImageResourceFromImageBitmap(bitmap);
|
|
173
|
-
|
|
174
|
-
expect(resource.source).toBe(bitmap);
|
|
175
|
-
expect(resource.width).toBe(64);
|
|
176
|
-
expect(resource.height).toBe(128);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('returns a new object each call', () => {
|
|
180
|
-
const bitmap = { width: 1, height: 1, close: () => {} } as ImageBitmap;
|
|
181
|
-
expect(createImageResourceFromImageBitmap(bitmap)).not.toBe(createImageResourceFromImageBitmap(bitmap));
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
describe('createImageResourceFromImageElement', () => {
|
|
186
|
-
it('wraps an HTMLImageElement with correct dimensions', () => {
|
|
187
|
-
const img = { width: 200, height: 100 } as HTMLImageElement;
|
|
188
|
-
const resource = createImageResourceFromImageElement(img);
|
|
189
|
-
|
|
190
|
-
expect(resource.source).toBe(img);
|
|
191
|
-
expect(resource.width).toBe(200);
|
|
192
|
-
expect(resource.height).toBe(100);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('reflects zero dimensions for an unloaded image element', () => {
|
|
196
|
-
const img = document.createElement('img');
|
|
197
|
-
const resource = createImageResourceFromImageElement(img);
|
|
198
|
-
|
|
199
|
-
expect(resource.width).toBe(0);
|
|
200
|
-
expect(resource.height).toBe(0);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('returns a new object each call', () => {
|
|
204
|
-
const img = document.createElement('img');
|
|
205
|
-
expect(createImageResourceFromImageElement(img)).not.toBe(createImageResourceFromImageElement(img));
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
describe('initializeImageResourceFromCanvas', () => {
|
|
210
|
-
it('is the construction initializer of createImageResourceFromCanvas', () => {
|
|
211
|
-
expect(typeof initializeImageResourceFromCanvas).toBe('function');
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe('initializeImageResourceFromImageBitmap', () => {
|
|
216
|
-
it('is the construction initializer of createImageResourceFromImageBitmap', () => {
|
|
217
|
-
expect(typeof initializeImageResourceFromImageBitmap).toBe('function');
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
describe('initializeImageResourceFromImageElement', () => {
|
|
222
|
-
it('is the construction initializer of createImageResourceFromImageElement', () => {
|
|
223
|
-
expect(typeof initializeImageResourceFromImageElement).toBe('function');
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
144
|
describe('isImageUrlSameOrigin', () => {
|
|
228
145
|
it('returns true for data: URLs', () => {
|
|
229
146
|
expect(isImageUrlSameOrigin('data:image/png;base64,abc')).toBe(true);
|
|
@@ -244,20 +161,20 @@ describe('isImageUrlSameOrigin', () => {
|
|
|
244
161
|
|
|
245
162
|
describe('loadImageResourceFromBase64', () => {
|
|
246
163
|
it('resolves to an ImageResource', async () => {
|
|
247
|
-
const resource = await loadImageResourceFromBase64(host, 'abc123', 'image/png');
|
|
164
|
+
const resource = await loadImageResourceFromBase64(host.graphics.image, 'abc123', 'image/png');
|
|
248
165
|
expect(resource).not.toBeNull();
|
|
249
166
|
expect(resource.source).toBeInstanceOf(HTMLImageElement);
|
|
250
167
|
});
|
|
251
168
|
|
|
252
169
|
it('builds a data: URL from the base64 string and mime type', async () => {
|
|
253
|
-
const resource = await loadImageResourceFromBase64(host, 'aGVsbG8=', 'image/png');
|
|
170
|
+
const resource = await loadImageResourceFromBase64(host.graphics.image, 'aGVsbG8=', 'image/png');
|
|
254
171
|
expect(resource.source).toBeInstanceOf(HTMLImageElement);
|
|
255
172
|
});
|
|
256
173
|
});
|
|
257
174
|
describe('loadImageResourceFromBlob', () => {
|
|
258
175
|
it('resolves to an ImageResource', async () => {
|
|
259
176
|
const blob = new Blob([], { type: 'image/png' });
|
|
260
|
-
const resource = await loadImageResourceFromBlob(host, blob);
|
|
177
|
+
const resource = await loadImageResourceFromBlob(host.graphics.image, blob);
|
|
261
178
|
expect(resource).not.toBeNull();
|
|
262
179
|
expect(resource.source).toBeInstanceOf(HTMLImageElement);
|
|
263
180
|
});
|
|
@@ -265,7 +182,7 @@ describe('loadImageResourceFromBlob', () => {
|
|
|
265
182
|
it('revokes the object URL after loading', async () => {
|
|
266
183
|
const revokeSpy = vi.spyOn(URL, 'revokeObjectURL');
|
|
267
184
|
const blob = new Blob([], { type: 'image/png' });
|
|
268
|
-
await loadImageResourceFromBlob(host, blob);
|
|
185
|
+
await loadImageResourceFromBlob(host.graphics.image, blob);
|
|
269
186
|
expect(revokeSpy).toHaveBeenCalledOnce();
|
|
270
187
|
});
|
|
271
188
|
|
|
@@ -273,7 +190,7 @@ describe('loadImageResourceFromBlob', () => {
|
|
|
273
190
|
HTMLImageElement.prototype.decode = vi.fn().mockRejectedValue(new Error('load failed'));
|
|
274
191
|
const revokeSpy = vi.spyOn(URL, 'revokeObjectURL');
|
|
275
192
|
const blob = new Blob([], { type: 'image/png' });
|
|
276
|
-
await expect(loadImageResourceFromBlob(host, blob)).rejects.toThrow('load failed');
|
|
193
|
+
await expect(loadImageResourceFromBlob(host.graphics.image, blob)).rejects.toThrow('load failed');
|
|
277
194
|
expect(revokeSpy).toHaveBeenCalledOnce();
|
|
278
195
|
});
|
|
279
196
|
});
|
|
@@ -281,24 +198,26 @@ describe('loadImageResourceFromBlob', () => {
|
|
|
281
198
|
describe('loadImageResourceFromBytes', () => {
|
|
282
199
|
it('throws when mime type cannot be detected and none is provided', async () => {
|
|
283
200
|
const bytes = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
|
|
284
|
-
await expect(loadImageResourceFromBytes(host, bytes)).rejects.toThrow(
|
|
201
|
+
await expect(loadImageResourceFromBytes(host.graphics.image, bytes)).rejects.toThrow(
|
|
202
|
+
'Unable to determine image type',
|
|
203
|
+
);
|
|
285
204
|
});
|
|
286
205
|
|
|
287
206
|
it('uses the provided mimeType and bypasses detection', async () => {
|
|
288
207
|
const bytes = new Uint8Array(16);
|
|
289
|
-
const resource = await loadImageResourceFromBytes(host, bytes, 'image/png');
|
|
208
|
+
const resource = await loadImageResourceFromBytes(host.graphics.image, bytes, 'image/png');
|
|
290
209
|
expect(resource.source).toBeInstanceOf(HTMLImageElement);
|
|
291
210
|
});
|
|
292
211
|
|
|
293
212
|
it('detects PNG and resolves', async () => {
|
|
294
213
|
const bytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0]);
|
|
295
|
-
const resource = await loadImageResourceFromBytes(host, bytes);
|
|
214
|
+
const resource = await loadImageResourceFromBytes(host.graphics.image, bytes);
|
|
296
215
|
expect(resource.source).toBeInstanceOf(HTMLImageElement);
|
|
297
216
|
});
|
|
298
217
|
|
|
299
218
|
it('detects JPEG and resolves', async () => {
|
|
300
219
|
const bytes = new Uint8Array([0xff, 0xd8, 0xff, 0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
|
301
|
-
const resource = await loadImageResourceFromBytes(host, bytes);
|
|
220
|
+
const resource = await loadImageResourceFromBytes(host.graphics.image, bytes);
|
|
302
221
|
expect(resource.source).toBeInstanceOf(HTMLImageElement);
|
|
303
222
|
});
|
|
304
223
|
});
|
|
@@ -315,7 +234,7 @@ describe('loadImageResourceFromUrl', () => {
|
|
|
315
234
|
},
|
|
316
235
|
}) as typeof Image;
|
|
317
236
|
|
|
318
|
-
await loadImageResourceFromUrl(host, '/images/logo.png');
|
|
237
|
+
await loadImageResourceFromUrl(host.graphics.image, '/images/logo.png');
|
|
319
238
|
expect(capturedImg?.crossOrigin).toBeNull();
|
|
320
239
|
|
|
321
240
|
globalThis.Image = origImage;
|
|
@@ -325,7 +244,7 @@ describe('loadImageResourceFromUrl', () => {
|
|
|
325
244
|
const controller = new AbortController();
|
|
326
245
|
controller.abort(new Error('cancelled'));
|
|
327
246
|
await expect(
|
|
328
|
-
loadImageResourceFromUrl(host, 'data:image/png;base64,abc', undefined, controller.signal),
|
|
247
|
+
loadImageResourceFromUrl(host.graphics.image, 'data:image/png;base64,abc', undefined, controller.signal),
|
|
329
248
|
).rejects.toThrow('cancelled');
|
|
330
249
|
});
|
|
331
250
|
|
|
@@ -343,7 +262,7 @@ describe('loadImageResourceFromUrl', () => {
|
|
|
343
262
|
const controller = new AbortController();
|
|
344
263
|
|
|
345
264
|
try {
|
|
346
|
-
const promise = loadImageResourceFromUrl(host, '/images/slow.png', undefined, controller.signal);
|
|
265
|
+
const promise = loadImageResourceFromUrl(host.graphics.image, '/images/slow.png', undefined, controller.signal);
|
|
347
266
|
controller.abort(new Error('cancelled'));
|
|
348
267
|
await expect(promise).rejects.toThrow('cancelled');
|
|
349
268
|
expect(capturedImg?.getAttribute('src')).toBe('');
|
|
@@ -357,7 +276,7 @@ describe('loadImageResourceFromUrl', () => {
|
|
|
357
276
|
const add = vi.spyOn(controller.signal, 'addEventListener');
|
|
358
277
|
const remove = vi.spyOn(controller.signal, 'removeEventListener');
|
|
359
278
|
|
|
360
|
-
await loadImageResourceFromUrl(host, '/images/logo.png', undefined, controller.signal);
|
|
279
|
+
await loadImageResourceFromUrl(host.graphics.image, '/images/logo.png', undefined, controller.signal);
|
|
361
280
|
|
|
362
281
|
expect(add).toHaveBeenCalledOnce();
|
|
363
282
|
expect(remove).toHaveBeenCalledOnce();
|
|
@@ -365,7 +284,7 @@ describe('loadImageResourceFromUrl', () => {
|
|
|
365
284
|
});
|
|
366
285
|
|
|
367
286
|
it('resolves to an ImageResource whose source is an HTMLImageElement', async () => {
|
|
368
|
-
const resource = await loadImageResourceFromUrl(host, 'data:image/png;base64,abc');
|
|
287
|
+
const resource = await loadImageResourceFromUrl(host.graphics.image, 'data:image/png;base64,abc');
|
|
369
288
|
expect(resource.source).toBeInstanceOf(HTMLImageElement);
|
|
370
289
|
});
|
|
371
290
|
|
|
@@ -380,7 +299,7 @@ describe('loadImageResourceFromUrl', () => {
|
|
|
380
299
|
},
|
|
381
300
|
}) as typeof Image;
|
|
382
301
|
|
|
383
|
-
await loadImageResourceFromUrl(host, 'https://cdn.other-domain.com/image.png', crossOrigin);
|
|
302
|
+
await loadImageResourceFromUrl(host.graphics.image, 'https://cdn.other-domain.com/image.png', crossOrigin);
|
|
384
303
|
expect(capturedImg?.crossOrigin).toBe(crossOrigin);
|
|
385
304
|
|
|
386
305
|
globalThis.Image = origImage;
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
|
-
import {
|
|
3
|
-
clearImageBitmapComposers,
|
|
4
|
-
clearImageDecoders,
|
|
5
|
-
registerImageBitmapComposer,
|
|
6
|
-
registerImageDecoder,
|
|
7
|
-
} from '@flighthq/image-codec/contract';
|
|
2
|
+
import { clearImageDecoders, registerImageDecoder } from '@flighthq/image-codec/contract';
|
|
8
3
|
import type { Bitmap, ImageResource, ImageDecoder } from '@flighthq/types/contract';
|
|
9
4
|
import {
|
|
10
5
|
BitmapTextureSourceKind,
|
|
@@ -17,11 +12,10 @@ import {
|
|
|
17
12
|
createEmbeddedImageResourceReference,
|
|
18
13
|
createExternalImageResourceReference,
|
|
19
14
|
createImageResourceFailure,
|
|
20
|
-
disableImageBitmapComposition,
|
|
21
|
-
enableImageBitmapComposition,
|
|
22
15
|
explainImageResourceReferenceResolution,
|
|
23
16
|
initializeEmbeddedImageResourceReference,
|
|
24
17
|
initializeExternalImageResourceReference,
|
|
18
|
+
registerImageBitmapCompositionResolver,
|
|
25
19
|
resetFailedImageResourceReference,
|
|
26
20
|
resolveImageResourceReference,
|
|
27
21
|
} from './imageResourceReference';
|
|
@@ -38,8 +32,7 @@ beforeEach(() => {
|
|
|
38
32
|
});
|
|
39
33
|
|
|
40
34
|
afterEach(() => {
|
|
41
|
-
|
|
42
|
-
clearImageBitmapComposers();
|
|
35
|
+
registerImageBitmapCompositionResolver(null);
|
|
43
36
|
clearImageDecoders();
|
|
44
37
|
});
|
|
45
38
|
|
|
@@ -91,37 +84,6 @@ describe('createImageResourceFailure', () => {
|
|
|
91
84
|
});
|
|
92
85
|
});
|
|
93
86
|
|
|
94
|
-
describe('disableImageBitmapComposition', () => {
|
|
95
|
-
it('restores the ordinary decoder path for a reference that carries composition data', async () => {
|
|
96
|
-
const composer = vi.fn().mockReturnValue(createTestBitmap('straight'));
|
|
97
|
-
registerImageBitmapComposer('acme/alpha-plane', composer);
|
|
98
|
-
enableImageBitmapComposition();
|
|
99
|
-
disableImageBitmapComposition();
|
|
100
|
-
const ref = createEmbeddedImageResourceReference(new Uint8Array([1]), 'image/png');
|
|
101
|
-
ref.bitmapComposition = { kind: 'acme/alpha-plane', payload: new Uint8Array([7]) };
|
|
102
|
-
|
|
103
|
-
const source = await resolveImageResourceReference(ref, unusedFetch, new AbortController().signal);
|
|
104
|
-
|
|
105
|
-
expect(composer).not.toHaveBeenCalled();
|
|
106
|
-
expect(decoder).toHaveBeenCalledWith(ref.bytes);
|
|
107
|
-
expect(source?.kind).toBe(BitmapTextureSourceKind);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe('enableImageBitmapComposition', () => {
|
|
112
|
-
it('installs the registered decoded-pixel composition route', async () => {
|
|
113
|
-
const bitmap = createTestBitmap('straight');
|
|
114
|
-
const composer = vi.fn().mockReturnValue(bitmap);
|
|
115
|
-
registerImageBitmapComposer('acme/alpha-plane', composer);
|
|
116
|
-
enableImageBitmapComposition();
|
|
117
|
-
const ref = createEmbeddedImageResourceReference(new Uint8Array([1]), 'image/png');
|
|
118
|
-
ref.bitmapComposition = { kind: 'acme/alpha-plane', payload: new Uint8Array([7]) };
|
|
119
|
-
|
|
120
|
-
expect(await resolveImageResourceReference(ref, unusedFetch, new AbortController().signal)).toBe(bitmap);
|
|
121
|
-
expect(composer).toHaveBeenCalledOnce();
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
87
|
describe('explainImageResourceReferenceResolution', () => {
|
|
126
88
|
it('detaches the failure so a caller cannot mutate the reference', () => {
|
|
127
89
|
const ref = createEmbeddedImageResourceReference(new Uint8Array(1));
|
|
@@ -164,6 +126,34 @@ function createTestBitmap(alphaType: Bitmap['alphaType']): Bitmap {
|
|
|
164
126
|
out.width = 1;
|
|
165
127
|
return finishEntity(out);
|
|
166
128
|
}
|
|
129
|
+
describe('registerImageBitmapCompositionResolver', () => {
|
|
130
|
+
it('routes a reference that carries composition data through the installed resolver', async () => {
|
|
131
|
+
const bitmap = createTestBitmap('straight');
|
|
132
|
+
const resolver = vi.fn().mockResolvedValue(bitmap);
|
|
133
|
+
registerImageBitmapCompositionResolver(resolver);
|
|
134
|
+
const ref = createEmbeddedImageResourceReference(new Uint8Array([1]), 'image/png');
|
|
135
|
+
ref.bitmapComposition = { kind: 'acme/alpha-plane', payload: new Uint8Array([7]) };
|
|
136
|
+
|
|
137
|
+
expect(await resolveImageResourceReference(ref, unusedFetch, new AbortController().signal)).toBe(bitmap);
|
|
138
|
+
expect(resolver).toHaveBeenCalledOnce();
|
|
139
|
+
expect(decoder).not.toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Null is how the owner takes the join back out. The slot is the whole of image's knowledge about
|
|
143
|
+
// composition, so an emptied slot must leave the ordinary straight-decode path exactly as it was.
|
|
144
|
+
it('restores the ordinary decode path when the resolver is taken back out', async () => {
|
|
145
|
+
registerImageBitmapCompositionResolver(vi.fn().mockResolvedValue(createTestBitmap('straight')));
|
|
146
|
+
registerImageBitmapCompositionResolver(null);
|
|
147
|
+
const ref = createEmbeddedImageResourceReference(new Uint8Array([1]), 'image/png');
|
|
148
|
+
ref.bitmapComposition = { kind: 'acme/alpha-plane', payload: new Uint8Array([7]) };
|
|
149
|
+
|
|
150
|
+
const source = await resolveImageResourceReference(ref, unusedFetch, new AbortController().signal);
|
|
151
|
+
|
|
152
|
+
expect(decoder).toHaveBeenCalledWith(ref.bytes);
|
|
153
|
+
expect(source?.kind).toBe(BitmapTextureSourceKind);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
167
157
|
describe('resetFailedImageResourceReference', () => {
|
|
168
158
|
it('clears a failed reference back to unresolved', () => {
|
|
169
159
|
const ref = createExternalImageResourceReference('a.png');
|
|
@@ -213,50 +203,6 @@ describe('resolveImageResourceReference', () => {
|
|
|
213
203
|
expect(source?.alphaType).toBe('premultiplied');
|
|
214
204
|
});
|
|
215
205
|
|
|
216
|
-
it('hands straight decoded pixels and plain payload bytes to a registered Bitmap composer', async () => {
|
|
217
|
-
const payload = new Uint8Array([7, 8, 9]);
|
|
218
|
-
const bitmap = createTestBitmap('straight');
|
|
219
|
-
const composer = vi.fn().mockReturnValue(bitmap);
|
|
220
|
-
registerImageBitmapComposer('acme/alpha-plane', composer);
|
|
221
|
-
enableImageBitmapComposition();
|
|
222
|
-
const ref = createEmbeddedImageResourceReference(new Uint8Array([1]), 'image/png', 'premultiplied');
|
|
223
|
-
ref.bitmapComposition = { kind: 'acme/alpha-plane', payload };
|
|
224
|
-
|
|
225
|
-
const source = await resolveImageResourceReference(ref, unusedFetch, new AbortController().signal);
|
|
226
|
-
|
|
227
|
-
expect(decoder).toHaveBeenCalledWith(ref.bytes);
|
|
228
|
-
expect(composer).toHaveBeenCalledWith(
|
|
229
|
-
{ data: new Uint8ClampedArray([0x11, 0x22, 0x33, 0x44]), height: 1, width: 1 },
|
|
230
|
-
payload,
|
|
231
|
-
);
|
|
232
|
-
expect(source).toBe(bitmap);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('lets a registered raw-pixel producer return a Bitmap when no MIME decoder recognizes the bytes', async () => {
|
|
236
|
-
const bitmap = createTestBitmap('opaque');
|
|
237
|
-
const composer = vi.fn().mockReturnValue(bitmap);
|
|
238
|
-
registerImageBitmapComposer('acme/raw-raster', composer);
|
|
239
|
-
enableImageBitmapComposition();
|
|
240
|
-
const ref = createEmbeddedImageResourceReference(new Uint8Array([1]));
|
|
241
|
-
ref.bitmapComposition = { kind: 'acme/raw-raster', payload: ref.bytes };
|
|
242
|
-
|
|
243
|
-
const source = await resolveImageResourceReference(ref, unusedFetch, new AbortController().signal);
|
|
244
|
-
|
|
245
|
-
expect(composer).toHaveBeenCalledWith(null, ref.bytes);
|
|
246
|
-
expect(source).toBe(bitmap);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it('reports an unavailable resource when its declared Bitmap composer is not registered', async () => {
|
|
250
|
-
enableImageBitmapComposition();
|
|
251
|
-
const ref = createEmbeddedImageResourceReference(new Uint8Array([1]));
|
|
252
|
-
ref.bitmapComposition = { kind: 'acme/missing', payload: ref.bytes };
|
|
253
|
-
|
|
254
|
-
expect(await resolveImageResourceReference(ref, unusedFetch, new AbortController().signal)).toBeNull();
|
|
255
|
-
expect(decoder).not.toHaveBeenCalled();
|
|
256
|
-
expect(ref.state).toBe(ResourceResolutionState.Failed);
|
|
257
|
-
expect(ref.failure?.kind).toBe(ImageResourceFailureKind.Unavailable);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
206
|
it('retains the missing-decoder cause when embedded bytes cannot be decoded', async () => {
|
|
261
207
|
clearImageDecoders();
|
|
262
208
|
const ref = createEmbeddedImageResourceReference(new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]));
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { HostImageSource } from '@flighthq/types/contract';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getHostImageSourceDimensions,
|
|
5
|
+
hasHostImageDimensionResolver,
|
|
6
|
+
registerHostImageDimensionResolver,
|
|
7
|
+
unregisterHostImageDimensionResolver,
|
|
8
|
+
} from './imageSourceDimensions';
|
|
9
|
+
|
|
10
|
+
// The slot is module state, so every test starts from an empty one rather than from whatever the
|
|
11
|
+
// previous test installed.
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
unregisterHostImageDimensionResolver();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const sizedSource = { height: 4, width: 8 } as unknown as HostImageSource;
|
|
17
|
+
|
|
18
|
+
describe('getHostImageSourceDimensions', () => {
|
|
19
|
+
it('measures through the registered resolver', () => {
|
|
20
|
+
registerHostImageDimensionResolver((source, out) => {
|
|
21
|
+
const sized = source as unknown as { height: number; width: number };
|
|
22
|
+
out.height = sized.height;
|
|
23
|
+
out.width = sized.width;
|
|
24
|
+
return true;
|
|
25
|
+
});
|
|
26
|
+
const out = { height: 0, width: 0 };
|
|
27
|
+
|
|
28
|
+
expect(getHostImageSourceDimensions(sizedSource, out)).toBe(true);
|
|
29
|
+
expect(out).toStrictEqual({ height: 4, width: 8 });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Reporting false rather than zero is the whole contract: a caller that already measured its source
|
|
33
|
+
// keeps that size instead of having it overwritten with a number no host produced.
|
|
34
|
+
it('reports false and leaves out untouched with no resolver registered', () => {
|
|
35
|
+
const out = { height: 11, width: 22 };
|
|
36
|
+
|
|
37
|
+
expect(getHostImageSourceDimensions(sizedSource, out)).toBe(false);
|
|
38
|
+
expect(out).toStrictEqual({ height: 11, width: 22 });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('reports false when the registered resolver declines the handle', () => {
|
|
42
|
+
registerHostImageDimensionResolver(() => false);
|
|
43
|
+
const out = { height: 11, width: 22 };
|
|
44
|
+
|
|
45
|
+
expect(getHostImageSourceDimensions(sizedSource, out)).toBe(false);
|
|
46
|
+
expect(out).toStrictEqual({ height: 11, width: 22 });
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('hasHostImageDimensionResolver', () => {
|
|
51
|
+
it('is false before a host registers one', () => {
|
|
52
|
+
expect(hasHostImageDimensionResolver()).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('is true once a host registers one', () => {
|
|
56
|
+
registerHostImageDimensionResolver(() => true);
|
|
57
|
+
expect(hasHostImageDimensionResolver()).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('registerHostImageDimensionResolver', () => {
|
|
62
|
+
it('replaces a previously registered resolver', () => {
|
|
63
|
+
registerHostImageDimensionResolver((_source, out) => {
|
|
64
|
+
out.height = 1;
|
|
65
|
+
out.width = 1;
|
|
66
|
+
return true;
|
|
67
|
+
});
|
|
68
|
+
registerHostImageDimensionResolver((_source, out) => {
|
|
69
|
+
out.height = 2;
|
|
70
|
+
out.width = 3;
|
|
71
|
+
return true;
|
|
72
|
+
});
|
|
73
|
+
const out = { height: 0, width: 0 };
|
|
74
|
+
getHostImageSourceDimensions(sizedSource, out);
|
|
75
|
+
|
|
76
|
+
expect(out).toStrictEqual({ height: 2, width: 3 });
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('unregisterHostImageDimensionResolver', () => {
|
|
81
|
+
it('empties the slot so measuring reports false again', () => {
|
|
82
|
+
registerHostImageDimensionResolver(() => true);
|
|
83
|
+
unregisterHostImageDimensionResolver();
|
|
84
|
+
|
|
85
|
+
expect(hasHostImageDimensionResolver()).toBe(false);
|
|
86
|
+
expect(getHostImageSourceDimensions(sizedSource, { height: 0, width: 0 })).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { HostImageSource } from '@flighthq/types/contract';
|
|
2
|
+
|
|
3
|
+
import { getHostImageSourceDimensions, hasHostImageDimensionResolver } from './imageSourceDimensions';
|
|
4
|
+
import {
|
|
5
|
+
registerTestImageDimensionResolver,
|
|
6
|
+
testImageDimensionResolver,
|
|
7
|
+
unregisterTestImageDimensionResolver,
|
|
8
|
+
} from './imageTestHelper';
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
unregisterTestImageDimensionResolver();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('registerTestImageDimensionResolver', () => {
|
|
15
|
+
it('fills the slot so a portable test can measure a sized handle', () => {
|
|
16
|
+
registerTestImageDimensionResolver();
|
|
17
|
+
const out = { height: 0, width: 0 };
|
|
18
|
+
|
|
19
|
+
expect(hasHostImageDimensionResolver()).toBe(true);
|
|
20
|
+
expect(getHostImageSourceDimensions({ height: 3, width: 9 } as unknown as HostImageSource, out)).toBe(true);
|
|
21
|
+
expect(out).toStrictEqual({ height: 3, width: 9 });
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('testImageDimensionResolver', () => {
|
|
26
|
+
it('reads width and height off a sized handle', () => {
|
|
27
|
+
const out = { height: 0, width: 0 };
|
|
28
|
+
|
|
29
|
+
expect(testImageDimensionResolver({ height: 6, width: 12 } as unknown as HostImageSource, out)).toBe(true);
|
|
30
|
+
expect(out).toStrictEqual({ height: 6, width: 12 });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Declining rather than reporting zero is the resolver contract, and the stand-in has to honor it or a
|
|
34
|
+
// test would prove something the real host does not do.
|
|
35
|
+
it('declines a handle carrying no numeric size and leaves out untouched', () => {
|
|
36
|
+
const out = { height: 5, width: 7 };
|
|
37
|
+
|
|
38
|
+
expect(testImageDimensionResolver({} as unknown as HostImageSource, out)).toBe(false);
|
|
39
|
+
expect(out).toStrictEqual({ height: 5, width: 7 });
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('unregisterTestImageDimensionResolver', () => {
|
|
44
|
+
it('empties the slot so no later test file inherits this one', () => {
|
|
45
|
+
registerTestImageDimensionResolver();
|
|
46
|
+
unregisterTestImageDimensionResolver();
|
|
47
|
+
|
|
48
|
+
expect(hasHostImageDimensionResolver()).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|