import { ByteType, ClampToEdgeWrapping, DataTexture, FileLoader, FloatType, HalfFloatType, IntType, LinearFilter, LinearMipMapLinearFilter, LinearSRGBColorSpace, Loader, LoadingManager, Mesh, MeshBasicMaterial, NoBlending, OrthographicCamera, PlaneGeometry, RGBAFormat, SRGBColorSpace, Scene, ShaderMaterial, ShortType, Texture, UVMapping, UnsignedByteType, UnsignedIntType, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";

//#region node_modules/@monogrid/gainmap-js/dist/QuadRenderer-B3h-sJtp.js
/**
* @monogrid/gainmap-js v3.0.6
* With ❤️, by MONOGRID <rnd@monogrid.com>
*/
const getBufferForType = (type, width, height) => {
	let out;
	switch (type) {
		case UnsignedByteType:
			out = new Uint8ClampedArray(width * height * 4);
			break;
		case HalfFloatType:
			out = new Uint16Array(width * height * 4);
			break;
		case UnsignedIntType:
			out = new Uint32Array(width * height * 4);
			break;
		case ByteType:
			out = new Int8Array(width * height * 4);
			break;
		case ShortType:
			out = new Int16Array(width * height * 4);
			break;
		case IntType:
			out = new Int32Array(width * height * 4);
			break;
		case FloatType:
			out = new Float32Array(width * height * 4);
			break;
		default: throw new Error("Unsupported data type");
	}
	return out;
};
let _canReadPixelsResult;
/**
* Test if this browser implementation can correctly read pixels from the specified
* Render target type.
*
* Runs only once
*
* @param type
* @param renderer
* @param camera
* @param renderTargetOptions
* @returns
*/
const canReadPixels = (type, renderer, camera, renderTargetOptions) => {
	if (_canReadPixelsResult !== void 0) return _canReadPixelsResult;
	const testRT = new WebGLRenderTarget(1, 1, renderTargetOptions);
	renderer.setRenderTarget(testRT);
	const mesh = new Mesh(new PlaneGeometry(), new MeshBasicMaterial({ color: 16777215 }));
	renderer.render(mesh, camera);
	renderer.setRenderTarget(null);
	const out = getBufferForType(type, testRT.width, testRT.height);
	renderer.readRenderTargetPixels(testRT, 0, 0, testRT.width, testRT.height, out);
	testRT.dispose();
	mesh.geometry.dispose();
	mesh.material.dispose();
	_canReadPixelsResult = out[0] !== 0;
	return _canReadPixelsResult;
};
/**
* Utility class used for rendering a texture with a material
*
* @category Core
* @group Core
*/
var QuadRenderer = class QuadRenderer {
	/**
	* Constructs a new QuadRenderer
	*
	* @param options Parameters for this QuadRenderer
	*/
	constructor(options) {
		var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
		this._rendererIsDisposable = false;
		this._supportsReadPixels = true;
		/**
		* Renders the input texture using the specified material
		*/
		this.render = () => {
			this._renderer.setRenderTarget(this._renderTarget);
			try {
				this._renderer.render(this._scene, this._camera);
			} catch (e) {
				this._renderer.setRenderTarget(null);
				throw e;
			}
			this._renderer.setRenderTarget(null);
		};
		this._width = options.width;
		this._height = options.height;
		this._type = options.type;
		this._colorSpace = options.colorSpace;
		const rtOptions = {
			format: RGBAFormat,
			depthBuffer: false,
			stencilBuffer: false,
			type: this._type,
			colorSpace: this._colorSpace,
			anisotropy: ((_a = options.renderTargetOptions) === null || _a === void 0 ? void 0 : _a.anisotropy) !== void 0 ? (_b = options.renderTargetOptions) === null || _b === void 0 ? void 0 : _b.anisotropy : 1,
			generateMipmaps: ((_c = options.renderTargetOptions) === null || _c === void 0 ? void 0 : _c.generateMipmaps) !== void 0 ? (_d = options.renderTargetOptions) === null || _d === void 0 ? void 0 : _d.generateMipmaps : false,
			magFilter: ((_e = options.renderTargetOptions) === null || _e === void 0 ? void 0 : _e.magFilter) !== void 0 ? (_f = options.renderTargetOptions) === null || _f === void 0 ? void 0 : _f.magFilter : LinearFilter,
			minFilter: ((_g = options.renderTargetOptions) === null || _g === void 0 ? void 0 : _g.minFilter) !== void 0 ? (_h = options.renderTargetOptions) === null || _h === void 0 ? void 0 : _h.minFilter : LinearFilter,
			samples: ((_j = options.renderTargetOptions) === null || _j === void 0 ? void 0 : _j.samples) !== void 0 ? (_k = options.renderTargetOptions) === null || _k === void 0 ? void 0 : _k.samples : void 0,
			wrapS: ((_l = options.renderTargetOptions) === null || _l === void 0 ? void 0 : _l.wrapS) !== void 0 ? (_m = options.renderTargetOptions) === null || _m === void 0 ? void 0 : _m.wrapS : ClampToEdgeWrapping,
			wrapT: ((_o = options.renderTargetOptions) === null || _o === void 0 ? void 0 : _o.wrapT) !== void 0 ? (_p = options.renderTargetOptions) === null || _p === void 0 ? void 0 : _p.wrapT : ClampToEdgeWrapping
		};
		this._material = options.material;
		if (options.renderer) this._renderer = options.renderer;
		else {
			this._renderer = QuadRenderer.instantiateRenderer();
			this._rendererIsDisposable = true;
		}
		this._scene = new Scene();
		this._camera = new OrthographicCamera();
		this._camera.position.set(0, 0, 10);
		this._camera.left = -.5;
		this._camera.right = .5;
		this._camera.top = .5;
		this._camera.bottom = -.5;
		this._camera.updateProjectionMatrix();
		if (!canReadPixels(this._type, this._renderer, this._camera, rtOptions)) {
			let alternativeType;
			switch (this._type) {
				case HalfFloatType:
					alternativeType = this._renderer.extensions.has("EXT_color_buffer_float") ? FloatType : void 0;
					break;
			}
			if (alternativeType !== void 0) {
				console.warn(`This browser does not support reading pixels from ${this._type} RenderTargets, switching to ${FloatType}`);
				this._type = alternativeType;
			} else {
				this._supportsReadPixels = false;
				console.warn("This browser dos not support toArray or toDataTexture, calls to those methods will result in an error thrown");
			}
		}
		this._quad = new Mesh(new PlaneGeometry(), this._material);
		this._quad.geometry.computeBoundingBox();
		this._scene.add(this._quad);
		this._renderTarget = new WebGLRenderTarget(this.width, this.height, rtOptions);
		this._renderTarget.texture.mapping = ((_q = options.renderTargetOptions) === null || _q === void 0 ? void 0 : _q.mapping) !== void 0 ? (_r = options.renderTargetOptions) === null || _r === void 0 ? void 0 : _r.mapping : UVMapping;
	}
	/**
	* Instantiates a temporary renderer
	*
	* @returns
	*/
	static instantiateRenderer() {
		const renderer = new WebGLRenderer();
		renderer.setSize(128, 128);
		return renderer;
	}
	/**
	* Obtains a Buffer containing the rendered texture.
	*
	* @throws Error if the browser cannot read pixels from this RenderTarget type.
	* @returns a TypedArray containing RGBA values from this renderer
	*/
	toArray() {
		if (!this._supportsReadPixels) throw new Error("Can't read pixels in this browser");
		const out = getBufferForType(this._type, this._width, this._height);
		this._renderer.readRenderTargetPixels(this._renderTarget, 0, 0, this._width, this._height, out);
		return out;
	}
	/**
	* Performs a readPixel operation in the renderTarget
	* and returns a DataTexture containing the read data
	*
	* @param options options
	* @returns
	*/
	toDataTexture(options) {
		const returnValue = new DataTexture(this.toArray(), this.width, this.height, RGBAFormat, this._type, (options === null || options === void 0 ? void 0 : options.mapping) || UVMapping, (options === null || options === void 0 ? void 0 : options.wrapS) || ClampToEdgeWrapping, (options === null || options === void 0 ? void 0 : options.wrapT) || ClampToEdgeWrapping, (options === null || options === void 0 ? void 0 : options.magFilter) || LinearFilter, (options === null || options === void 0 ? void 0 : options.minFilter) || LinearFilter, (options === null || options === void 0 ? void 0 : options.anisotropy) || 1, LinearSRGBColorSpace);
		returnValue.generateMipmaps = (options === null || options === void 0 ? void 0 : options.generateMipmaps) !== void 0 ? options === null || options === void 0 ? void 0 : options.generateMipmaps : false;
		return returnValue;
	}
	/**
	* If using a disposable renderer, it will dispose it.
	*/
	disposeOnDemandRenderer() {
		this._renderer.setRenderTarget(null);
		if (this._rendererIsDisposable) {
			this._renderer.dispose();
			this._renderer.forceContextLoss();
		}
	}
	/**
	* Will dispose of **all** assets used by this renderer.
	*
	*
	* @param disposeRenderTarget will dispose of the renderTarget which will not be usable later
	* set this to true if you passed the `renderTarget.texture` to a `PMREMGenerator`
	* or are otherwise done with it.
	*
	* @example
	* ```js
	* const loader = new HDRJPGLoader(renderer)
	* const result = await loader.loadAsync('gainmap.jpeg')
	* const mesh = new Mesh(geometry, new MeshBasicMaterial({ map: result.renderTarget.texture }) )
	* // DO NOT dispose the renderTarget here,
	* // it is used directly in the material
	* result.dispose()
	* ```
	*
	* @example
	* ```js
	* const loader = new HDRJPGLoader(renderer)
	* const pmremGenerator = new PMREMGenerator( renderer );
	* const result = await loader.loadAsync('gainmap.jpeg')
	* const envMap = pmremGenerator.fromEquirectangular(result.renderTarget.texture)
	* const mesh = new Mesh(geometry, new MeshStandardMaterial({ envMap }) )
	* // renderTarget can be disposed here
	* // because it was used to generate a PMREM texture
	* result.dispose(true)
	* ```
	*/
	dispose(disposeRenderTarget) {
		this.disposeOnDemandRenderer();
		if (disposeRenderTarget) this.renderTarget.dispose();
		if (this.material instanceof ShaderMaterial) Object.values(this.material.uniforms).forEach((v) => {
			if (v.value instanceof Texture) v.value.dispose();
		});
		Object.values(this.material).forEach((value) => {
			if (value instanceof Texture) value.dispose();
		});
		this.material.dispose();
		this._quad.geometry.dispose();
	}
	/**
	* Width of the texture
	*/
	get width() {
		return this._width;
	}
	set width(value) {
		this._width = value;
		this._renderTarget.setSize(this._width, this._height);
	}
	/**
	* Height of the texture
	*/
	get height() {
		return this._height;
	}
	set height(value) {
		this._height = value;
		this._renderTarget.setSize(this._width, this._height);
	}
	/**
	* The renderer used
	*/
	get renderer() {
		return this._renderer;
	}
	/**
	* The `WebGLRenderTarget` used.
	*/
	get renderTarget() {
		return this._renderTarget;
	}
	set renderTarget(value) {
		this._renderTarget = value;
		this._width = value.width;
		this._height = value.height;
	}
	/**
	* The `Material` used.
	*/
	get material() {
		return this._material;
	}
	/**
	*
	*/
	get type() {
		return this._type;
	}
	get colorSpace() {
		return this._colorSpace;
	}
};

//#endregion
//#region node_modules/@monogrid/gainmap-js/dist/decode.js
/**
* @monogrid/gainmap-js v3.0.6
* With ❤️, by MONOGRID <rnd@monogrid.com>
*/
const vertexShader = `
varying vec2 vUv;

void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
// min half float value
#define HALF_FLOAT_MIN vec3( -65504, -65504, -65504 )
// max half float value
#define HALF_FLOAT_MAX vec3( 65504, 65504, 65504 )

uniform sampler2D sdr;
uniform sampler2D gainMap;
uniform vec3 gamma;
uniform vec3 offsetHdr;
uniform vec3 offsetSdr;
uniform vec3 gainMapMin;
uniform vec3 gainMapMax;
uniform float weightFactor;

varying vec2 vUv;

void main() {
  vec3 rgb = texture2D( sdr, vUv ).rgb;
  vec3 recovery = texture2D( gainMap, vUv ).rgb;
  vec3 logRecovery = pow( recovery, gamma );
  vec3 logBoost = gainMapMin * ( 1.0 - logRecovery ) + gainMapMax * logRecovery;
  vec3 hdrColor = (rgb + offsetSdr) * exp2( logBoost * weightFactor ) - offsetHdr;
  vec3 clampedHdrColor = max( HALF_FLOAT_MIN, min( HALF_FLOAT_MAX, hdrColor ));
  gl_FragColor = vec4( clampedHdrColor , 1.0 );
}
`;
/**
* A Material which is able to decode the Gainmap into a full HDR Representation
*
* @category Materials
* @group Materials
*/
var GainMapDecoderMaterial = class extends ShaderMaterial {
	/**
	*
	* @param params
	*/
	constructor({ gamma, offsetHdr, offsetSdr, gainMapMin, gainMapMax, maxDisplayBoost, hdrCapacityMin, hdrCapacityMax, sdr, gainMap }) {
		super({
			name: "GainMapDecoderMaterial",
			vertexShader,
			fragmentShader,
			uniforms: {
				sdr: { value: sdr },
				gainMap: { value: gainMap },
				gamma: { value: new Vector3(1 / gamma[0], 1 / gamma[1], 1 / gamma[2]) },
				offsetHdr: { value: new Vector3().fromArray(offsetHdr) },
				offsetSdr: { value: new Vector3().fromArray(offsetSdr) },
				gainMapMin: { value: new Vector3().fromArray(gainMapMin) },
				gainMapMax: { value: new Vector3().fromArray(gainMapMax) },
				weightFactor: { value: (Math.log2(maxDisplayBoost) - hdrCapacityMin) / (hdrCapacityMax - hdrCapacityMin) }
			},
			blending: NoBlending,
			depthTest: false,
			depthWrite: false
		});
		this._maxDisplayBoost = maxDisplayBoost;
		this._hdrCapacityMin = hdrCapacityMin;
		this._hdrCapacityMax = hdrCapacityMax;
		this.needsUpdate = true;
		this.uniformsNeedUpdate = true;
	}
	get sdr() {
		return this.uniforms.sdr.value;
	}
	set sdr(value) {
		this.uniforms.sdr.value = value;
	}
	get gainMap() {
		return this.uniforms.gainMap.value;
	}
	set gainMap(value) {
		this.uniforms.gainMap.value = value;
	}
	/**
	* @see {@link GainMapMetadata.offsetHdr}
	*/
	get offsetHdr() {
		return this.uniforms.offsetHdr.value.toArray();
	}
	set offsetHdr(value) {
		this.uniforms.offsetHdr.value.fromArray(value);
	}
	/**
	* @see {@link GainMapMetadata.offsetSdr}
	*/
	get offsetSdr() {
		return this.uniforms.offsetSdr.value.toArray();
	}
	set offsetSdr(value) {
		this.uniforms.offsetSdr.value.fromArray(value);
	}
	/**
	* @see {@link GainMapMetadata.gainMapMin}
	*/
	get gainMapMin() {
		return this.uniforms.gainMapMin.value.toArray();
	}
	set gainMapMin(value) {
		this.uniforms.gainMapMin.value.fromArray(value);
	}
	/**
	* @see {@link GainMapMetadata.gainMapMax}
	*/
	get gainMapMax() {
		return this.uniforms.gainMapMax.value.toArray();
	}
	set gainMapMax(value) {
		this.uniforms.gainMapMax.value.fromArray(value);
	}
	/**
	* @see {@link GainMapMetadata.gamma}
	*/
	get gamma() {
		const g = this.uniforms.gamma.value;
		return [
			1 / g.x,
			1 / g.y,
			1 / g.z
		];
	}
	set gamma(value) {
		const g = this.uniforms.gamma.value;
		g.x = 1 / value[0];
		g.y = 1 / value[1];
		g.z = 1 / value[2];
	}
	/**
	* @see {@link GainMapMetadata.hdrCapacityMin}
	* @remarks Logarithmic space
	*/
	get hdrCapacityMin() {
		return this._hdrCapacityMin;
	}
	set hdrCapacityMin(value) {
		this._hdrCapacityMin = value;
		this.calculateWeight();
	}
	/**
	* @see {@link GainMapMetadata.hdrCapacityMin}
	* @remarks Logarithmic space
	*/
	get hdrCapacityMax() {
		return this._hdrCapacityMax;
	}
	set hdrCapacityMax(value) {
		this._hdrCapacityMax = value;
		this.calculateWeight();
	}
	/**
	* @see {@link GainmapDecodingParameters.maxDisplayBoost}
	* @remarks Non Logarithmic space
	*/
	get maxDisplayBoost() {
		return this._maxDisplayBoost;
	}
	set maxDisplayBoost(value) {
		this._maxDisplayBoost = Math.max(1, Math.min(65504, value));
		this.calculateWeight();
	}
	calculateWeight() {
		const val = (Math.log2(this._maxDisplayBoost) - this._hdrCapacityMin) / (this._hdrCapacityMax - this._hdrCapacityMin);
		this.uniforms.weightFactor.value = Math.max(0, Math.min(1, val));
	}
};
var GainMapNotFoundError = class extends Error {};
var XMPMetadataNotFoundError = class extends Error {};
const getAttribute = (description, name, defaultValue) => {
	var _a;
	let returnValue;
	const parsedValue = (_a = description.attributes.getNamedItem(name)) === null || _a === void 0 ? void 0 : _a.nodeValue;
	if (!parsedValue) {
		const node = description.getElementsByTagName(name)[0];
		if (node) {
			const values = node.getElementsByTagName("rdf:li");
			if (values.length === 3) returnValue = Array.from(values).map((v) => v.innerHTML);
			else throw new Error(`Gainmap metadata contains an array of items for ${name} but its length is not 3`);
		} else if (defaultValue) return defaultValue;
		else throw new Error(`Can't find ${name} in gainmap metadata`);
	} else returnValue = parsedValue;
	return returnValue;
};
/**
*
* @param input
* @returns
*/
const extractXMP = (input) => {
	var _a, _b;
	let str;
	if (typeof TextDecoder !== "undefined") str = new TextDecoder().decode(input);
	else str = input.toString();
	let start = str.indexOf("<x:xmpmeta");
	const parser = new DOMParser();
	while (start !== -1) {
		const end = str.indexOf("x:xmpmeta>", start);
		str.slice(start, end + 10);
		const xmpBlock = str.slice(start, end + 10);
		try {
			const description = parser.parseFromString(xmpBlock, "text/xml").getElementsByTagName("rdf:Description")[0];
			const gainMapMin = getAttribute(description, "hdrgm:GainMapMin", "0");
			const gainMapMax = getAttribute(description, "hdrgm:GainMapMax");
			const gamma = getAttribute(description, "hdrgm:Gamma", "1");
			const offsetSDR = getAttribute(description, "hdrgm:OffsetSDR", "0.015625");
			const offsetHDR = getAttribute(description, "hdrgm:OffsetHDR", "0.015625");
			let hdrCapacityMin = (_a = description.attributes.getNamedItem("hdrgm:HDRCapacityMin")) === null || _a === void 0 ? void 0 : _a.nodeValue;
			if (!hdrCapacityMin) hdrCapacityMin = "0";
			const hdrCapacityMax = (_b = description.attributes.getNamedItem("hdrgm:HDRCapacityMax")) === null || _b === void 0 ? void 0 : _b.nodeValue;
			if (!hdrCapacityMax) throw new Error("Incomplete gainmap metadata");
			return {
				gainMapMin: Array.isArray(gainMapMin) ? gainMapMin.map((v) => parseFloat(v)) : [
					parseFloat(gainMapMin),
					parseFloat(gainMapMin),
					parseFloat(gainMapMin)
				],
				gainMapMax: Array.isArray(gainMapMax) ? gainMapMax.map((v) => parseFloat(v)) : [
					parseFloat(gainMapMax),
					parseFloat(gainMapMax),
					parseFloat(gainMapMax)
				],
				gamma: Array.isArray(gamma) ? gamma.map((v) => parseFloat(v)) : [
					parseFloat(gamma),
					parseFloat(gamma),
					parseFloat(gamma)
				],
				offsetSdr: Array.isArray(offsetSDR) ? offsetSDR.map((v) => parseFloat(v)) : [
					parseFloat(offsetSDR),
					parseFloat(offsetSDR),
					parseFloat(offsetSDR)
				],
				offsetHdr: Array.isArray(offsetHDR) ? offsetHDR.map((v) => parseFloat(v)) : [
					parseFloat(offsetHDR),
					parseFloat(offsetHDR),
					parseFloat(offsetHDR)
				],
				hdrCapacityMin: parseFloat(hdrCapacityMin),
				hdrCapacityMax: parseFloat(hdrCapacityMax)
			};
		} catch (e) {}
		start = str.indexOf("<x:xmpmeta", end);
	}
};
/**
* MPF Extractor (Multi Picture Format Extractor)
* By Henrik S Nilsson 2019
*
* Extracts images stored in images based on the MPF format (found here: https://www.cipa.jp/e/std/std-sec.html
* under "CIPA DC-007-Translation-2021 Multi-Picture Format"
*
* Overly commented, and without intention of being complete or production ready.
* Created to extract depth maps from iPhone images, and to learn about image metadata.
* Kudos to: Phil Harvey (exiftool), Jaume Sanchez (android-lens-blur-depth-extractor)
*/
var MPFExtractor = class {
	constructor(options) {
		this.options = {
			debug: options && options.debug !== void 0 ? options.debug : false,
			extractFII: options && options.extractFII !== void 0 ? options.extractFII : true,
			extractNonFII: options && options.extractNonFII !== void 0 ? options.extractNonFII : true
		};
	}
	extract(imageArrayBuffer) {
		return new Promise((resolve, reject) => {
			const debug = this.options.debug;
			const dataView = new DataView(imageArrayBuffer.buffer);
			if (dataView.getUint16(0) !== 65496) {
				reject(/* @__PURE__ */ new Error("Not a valid jpeg"));
				return;
			}
			const length = dataView.byteLength;
			let offset = 2;
			let loops = 0;
			let marker;
			while (offset < length) {
				if (++loops > 250) {
					reject(/* @__PURE__ */ new Error(`Found no marker after ${loops} loops 😵`));
					return;
				}
				if (dataView.getUint8(offset) !== 255) {
					reject(/* @__PURE__ */ new Error(`Not a valid marker at offset 0x${offset.toString(16)}, found: 0x${dataView.getUint8(offset).toString(16)}`));
					return;
				}
				marker = dataView.getUint8(offset + 1);
				if (debug) console.log(`Marker: ${marker.toString(16)}`);
				if (marker === 226) {
					if (debug) console.log("Found APP2 marker (0xffe2)");
					const formatPt = offset + 4;
					if (dataView.getUint32(formatPt) === 1297106432) {
						const tiffOffset = formatPt + 4;
						let bigEnd;
						if (dataView.getUint16(tiffOffset) === 18761) bigEnd = false;
						else if (dataView.getUint16(tiffOffset) === 19789) bigEnd = true;
						else {
							reject(/* @__PURE__ */ new Error("No valid endianness marker found in TIFF header"));
							return;
						}
						if (dataView.getUint16(tiffOffset + 2, !bigEnd) !== 42) {
							reject(/* @__PURE__ */ new Error("Not valid TIFF data! (no 0x002A marker)"));
							return;
						}
						const firstIFDOffset = dataView.getUint32(tiffOffset + 4, !bigEnd);
						if (firstIFDOffset < 8) {
							reject(/* @__PURE__ */ new Error("Not valid TIFF data! (First offset less than 8)"));
							return;
						}
						const dirStart = tiffOffset + firstIFDOffset;
						const count = dataView.getUint16(dirStart, !bigEnd);
						const entriesStart = dirStart + 2;
						let numberOfImages = 0;
						for (let i = entriesStart; i < entriesStart + 12 * count; i += 12) if (dataView.getUint16(i, !bigEnd) === 45057) numberOfImages = dataView.getUint32(i + 8, !bigEnd);
						const MPImageListValPt = dirStart + 2 + count * 12 + 4;
						const images = [];
						for (let i = MPImageListValPt; i < MPImageListValPt + numberOfImages * 16; i += 16) {
							const image = {
								MPType: dataView.getUint32(i, !bigEnd),
								size: dataView.getUint32(i + 4, !bigEnd),
								dataOffset: dataView.getUint32(i + 8, !bigEnd),
								dependantImages: dataView.getUint32(i + 12, !bigEnd),
								start: -1,
								end: -1,
								isFII: false
							};
							if (!image.dataOffset) {
								image.start = 0;
								image.isFII = true;
							} else {
								image.start = tiffOffset + image.dataOffset;
								image.isFII = false;
							}
							image.end = image.start + image.size;
							images.push(image);
						}
						if (this.options.extractNonFII && images.length) {
							const bufferBlob = new Blob([dataView]);
							const imgs = [];
							for (const image of images) {
								if (image.isFII && !this.options.extractFII) continue;
								const imageBlob = bufferBlob.slice(image.start, image.end + 1, "image/jpeg");
								imgs.push(imageBlob);
							}
							resolve(imgs);
						}
					}
				}
				offset += 2 + dataView.getUint16(offset + 2);
			}
		});
	}
};
/**
* Extracts XMP Metadata and the gain map recovery image
* from a single JPEG file.
*
* @category Decoding Functions
* @group Decoding Functions
* @param jpegFile an `Uint8Array` containing and encoded JPEG file
* @returns an sdr `Uint8Array` compressed in JPEG, a gainMap `Uint8Array` compressed in JPEG and the XMP parsed XMP metadata
* @throws Error if XMP Metadata is not found
* @throws Error if Gain map image is not found
* @example
* import { FileLoader } from 'three'
* import { extractGainmapFromJPEG } from '@monogrid/gainmap-js'
*
* const jpegFile = await new FileLoader()
*  .setResponseType('arraybuffer')
*  .loadAsync('image.jpg')
*
* const { sdr, gainMap, metadata } = extractGainmapFromJPEG(jpegFile)
*/
const extractGainmapFromJPEG = async (jpegFile) => {
	const metadata = extractXMP(jpegFile);
	if (!metadata) throw new XMPMetadataNotFoundError("Gain map XMP metadata not found");
	const images = await new MPFExtractor({
		extractFII: true,
		extractNonFII: true
	}).extract(jpegFile);
	if (images.length !== 2) throw new GainMapNotFoundError("Gain map recovery image not found");
	return {
		sdr: new Uint8Array(await images[0].arrayBuffer()),
		gainMap: new Uint8Array(await images[1].arrayBuffer()),
		metadata
	};
};
/**
* private function, async get image from blob
*
* @param blob
* @returns
*/
const getHTMLImageFromBlob = (blob) => {
	return new Promise((resolve, reject) => {
		const img = document.createElement("img");
		img.onload = () => {
			resolve(img);
		};
		img.onerror = (e) => {
			reject(e);
		};
		img.src = URL.createObjectURL(blob);
	});
};
var LoaderBase = class extends Loader {
	/**
	*
	* @param renderer
	* @param manager
	*/
	constructor(renderer, manager) {
		super(manager);
		if (renderer) this._renderer = renderer;
		this._internalLoadingManager = new LoadingManager();
	}
	/**
	* Specify the renderer to use when rendering the gain map
	*
	* @param renderer
	* @returns
	*/
	setRenderer(renderer) {
		this._renderer = renderer;
		return this;
	}
	/**
	* Specify the renderTarget options to use when rendering the gain map
	*
	* @param options
	* @returns
	*/
	setRenderTargetOptions(options) {
		this._renderTargetOptions = options;
		return this;
	}
	/**
	* @private
	* @returns
	*/
	prepareQuadRenderer() {
		if (!this._renderer) console.warn("WARNING: An existing WebGL Renderer was not passed to this Loader constructor or in setRenderer, the result of this Loader will need to be converted to a Data Texture with toDataTexture() before you can use it in your renderer.");
		return new QuadRenderer({
			width: 16,
			height: 16,
			type: HalfFloatType,
			colorSpace: LinearSRGBColorSpace,
			material: new GainMapDecoderMaterial({
				gainMapMax: [
					1,
					1,
					1
				],
				gainMapMin: [
					0,
					0,
					0
				],
				gamma: [
					1,
					1,
					1
				],
				offsetHdr: [
					1,
					1,
					1
				],
				offsetSdr: [
					1,
					1,
					1
				],
				hdrCapacityMax: 1,
				hdrCapacityMin: 0,
				maxDisplayBoost: 1,
				gainMap: new Texture(),
				sdr: new Texture()
			}),
			renderer: this._renderer,
			renderTargetOptions: this._renderTargetOptions
		});
	}
	/**
	* @private
	* @param quadRenderer
	* @param metadata
	* @param sdrBuffer
	* @param gainMapBuffer
	*/
	async render(quadRenderer, metadata, sdrBuffer, gainMapBuffer) {
		const gainMapBlob = gainMapBuffer ? new Blob([gainMapBuffer], { type: "image/jpeg" }) : void 0;
		const sdrBlob = new Blob([sdrBuffer], { type: "image/jpeg" });
		let sdrImage;
		let gainMapImage;
		let needsFlip = false;
		if (typeof createImageBitmap === "undefined") {
			const res = await Promise.all([gainMapBlob ? getHTMLImageFromBlob(gainMapBlob) : Promise.resolve(void 0), getHTMLImageFromBlob(sdrBlob)]);
			gainMapImage = res[0];
			sdrImage = res[1];
			needsFlip = true;
		} else {
			const res = await Promise.all([gainMapBlob ? createImageBitmap(gainMapBlob, { imageOrientation: "flipY" }) : Promise.resolve(void 0), createImageBitmap(sdrBlob, { imageOrientation: "flipY" })]);
			gainMapImage = res[0];
			sdrImage = res[1];
		}
		const gainMap = new Texture(gainMapImage || new ImageData(2, 2), UVMapping, ClampToEdgeWrapping, ClampToEdgeWrapping, LinearFilter, LinearMipMapLinearFilter, RGBAFormat, UnsignedByteType, 1, LinearSRGBColorSpace);
		gainMap.flipY = needsFlip;
		gainMap.needsUpdate = true;
		const sdr = new Texture(sdrImage, UVMapping, ClampToEdgeWrapping, ClampToEdgeWrapping, LinearFilter, LinearMipMapLinearFilter, RGBAFormat, UnsignedByteType, 1, SRGBColorSpace);
		sdr.flipY = needsFlip;
		sdr.needsUpdate = true;
		quadRenderer.width = sdrImage.width;
		quadRenderer.height = sdrImage.height;
		quadRenderer.material.gainMap = gainMap;
		quadRenderer.material.sdr = sdr;
		quadRenderer.material.gainMapMin = metadata.gainMapMin;
		quadRenderer.material.gainMapMax = metadata.gainMapMax;
		quadRenderer.material.offsetHdr = metadata.offsetHdr;
		quadRenderer.material.offsetSdr = metadata.offsetSdr;
		quadRenderer.material.gamma = metadata.gamma;
		quadRenderer.material.hdrCapacityMin = metadata.hdrCapacityMin;
		quadRenderer.material.hdrCapacityMax = metadata.hdrCapacityMax;
		quadRenderer.material.maxDisplayBoost = Math.pow(2, metadata.hdrCapacityMax);
		quadRenderer.material.needsUpdate = true;
		quadRenderer.render();
	}
};
/**
* A Three.js Loader for the gain map format.
*
* @category Loaders
* @group Loaders
*
* @example
* import { GainMapLoader } from '@monogrid/gainmap-js'
* import {
*   EquirectangularReflectionMapping,
*   LinearFilter,
*   Mesh,
*   MeshBasicMaterial,
*   PerspectiveCamera,
*   PlaneGeometry,
*   Scene,
*   WebGLRenderer
* } from 'three'
*
* const renderer = new WebGLRenderer()
*
* const loader = new GainMapLoader(renderer)
*
* const result = await loader.loadAsync(['sdr.jpeg', 'gainmap.jpeg', 'metadata.json'])
* // `result` can be used to populate a Texture
*
* const scene = new Scene()
* const mesh = new Mesh(
*   new PlaneGeometry(),
*   new MeshBasicMaterial({ map: result.renderTarget.texture })
* )
* scene.add(mesh)
* renderer.render(scene, new PerspectiveCamera())
*
* // Starting from three.js r159
* // `result.renderTarget.texture` can
* // also be used as Equirectangular scene background
* //
* // it was previously needed to convert it
* // to a DataTexture with `result.toDataTexture()`
* scene.background = result.renderTarget.texture
* scene.background.mapping = EquirectangularReflectionMapping
*
* // result must be manually disposed
* // when you are done using it
* result.dispose()
*
*/
var GainMapLoader = class extends LoaderBase {
	/**
	* Loads a gainmap using separate data
	* * sdr image
	* * gain map image
	* * metadata json
	*
	* useful for webp gain maps
	*
	* @param urls An array in the form of [sdr.jpg, gainmap.jpg, metadata.json]
	* @param onLoad Load complete callback, will receive the result
	* @param onProgress Progress callback, will receive a {@link ProgressEvent}
	* @param onError Error callback
	* @returns
	*/
	load([sdrUrl, gainMapUrl, metadataUrl], onLoad, onProgress, onError) {
		const quadRenderer = this.prepareQuadRenderer();
		let sdr;
		let gainMap;
		let metadata;
		const loadCheck = async () => {
			if (sdr && gainMap && metadata) {
				try {
					await this.render(quadRenderer, metadata, sdr, gainMap);
				} catch (error) {
					this.manager.itemError(sdrUrl);
					this.manager.itemError(gainMapUrl);
					this.manager.itemError(metadataUrl);
					if (typeof onError === "function") onError(error);
					quadRenderer.disposeOnDemandRenderer();
					return;
				}
				if (typeof onLoad === "function") onLoad(quadRenderer);
				this.manager.itemEnd(sdrUrl);
				this.manager.itemEnd(gainMapUrl);
				this.manager.itemEnd(metadataUrl);
				quadRenderer.disposeOnDemandRenderer();
			}
		};
		let sdrLengthComputable = true;
		let sdrTotal = 0;
		let sdrLoaded = 0;
		let gainMapLengthComputable = true;
		let gainMapTotal = 0;
		let gainMapLoaded = 0;
		let metadataLengthComputable = true;
		let metadataTotal = 0;
		let metadataLoaded = 0;
		const progressHandler = () => {
			if (typeof onProgress === "function") {
				const total = sdrTotal + gainMapTotal + metadataTotal;
				const loaded = sdrLoaded + gainMapLoaded + metadataLoaded;
				const lengthComputable = sdrLengthComputable && gainMapLengthComputable && metadataLengthComputable;
				onProgress(new ProgressEvent("progress", {
					lengthComputable,
					loaded,
					total
				}));
			}
		};
		this.manager.itemStart(sdrUrl);
		this.manager.itemStart(gainMapUrl);
		this.manager.itemStart(metadataUrl);
		const sdrLoader = new FileLoader(this._internalLoadingManager);
		sdrLoader.setResponseType("arraybuffer");
		sdrLoader.setRequestHeader(this.requestHeader);
		sdrLoader.setPath(this.path);
		sdrLoader.setWithCredentials(this.withCredentials);
		sdrLoader.load(sdrUrl, async (buffer) => {
			/* istanbul ignore if
			this condition exists only because of three.js types + strict mode
			*/
			if (typeof buffer === "string") throw new Error("Invalid sdr buffer");
			sdr = buffer;
			await loadCheck();
		}, (e) => {
			sdrLengthComputable = e.lengthComputable;
			sdrLoaded = e.loaded;
			sdrTotal = e.total;
			progressHandler();
		}, (error) => {
			this.manager.itemError(sdrUrl);
			if (typeof onError === "function") onError(error);
		});
		const gainMapLoader = new FileLoader(this._internalLoadingManager);
		gainMapLoader.setResponseType("arraybuffer");
		gainMapLoader.setRequestHeader(this.requestHeader);
		gainMapLoader.setPath(this.path);
		gainMapLoader.setWithCredentials(this.withCredentials);
		gainMapLoader.load(gainMapUrl, async (buffer) => {
			/* istanbul ignore if
			this condition exists only because of three.js types + strict mode
			*/
			if (typeof buffer === "string") throw new Error("Invalid gainmap buffer");
			gainMap = buffer;
			await loadCheck();
		}, (e) => {
			gainMapLengthComputable = e.lengthComputable;
			gainMapLoaded = e.loaded;
			gainMapTotal = e.total;
			progressHandler();
		}, (error) => {
			this.manager.itemError(gainMapUrl);
			if (typeof onError === "function") onError(error);
		});
		const metadataLoader = new FileLoader(this._internalLoadingManager);
		metadataLoader.setRequestHeader(this.requestHeader);
		metadataLoader.setPath(this.path);
		metadataLoader.setWithCredentials(this.withCredentials);
		metadataLoader.load(metadataUrl, async (json) => {
			/* istanbul ignore if
			this condition exists only because of three.js types + strict mode
			*/
			if (typeof json !== "string") throw new Error("Invalid metadata string");
			metadata = JSON.parse(json);
			await loadCheck();
		}, (e) => {
			metadataLengthComputable = e.lengthComputable;
			metadataLoaded = e.loaded;
			metadataTotal = e.total;
			progressHandler();
		}, (error) => {
			this.manager.itemError(metadataUrl);
			if (typeof onError === "function") onError(error);
		});
		return quadRenderer;
	}
};
/**
* A Three.js Loader for a JPEG with embedded gainmap metadata.
*
* @category Loaders
* @group Loaders
*
* @example
* import { HDRJPGLoader } from '@monogrid/gainmap-js'
* import {
*   EquirectangularReflectionMapping,
*   LinearFilter,
*   Mesh,
*   MeshBasicMaterial,
*   PerspectiveCamera,
*   PlaneGeometry,
*   Scene,
*   WebGLRenderer
* } from 'three'
*
* const renderer = new WebGLRenderer()
*
* const loader = new HDRJPGLoader(renderer)
*
* const result = await loader.loadAsync('gainmap.jpeg')
* // `result` can be used to populate a Texture
*
* const scene = new Scene()
* const mesh = new Mesh(
*   new PlaneGeometry(),
*   new MeshBasicMaterial({ map: result.renderTarget.texture })
* )
* scene.add(mesh)
* renderer.render(scene, new PerspectiveCamera())
*
* // Starting from three.js r159
* // `result.renderTarget.texture` can
* // also be used as Equirectangular scene background
* //
* // it was previously needed to convert it
* // to a DataTexture with `result.toDataTexture()`
* scene.background = result.renderTarget.texture
* scene.background.mapping = EquirectangularReflectionMapping
*
* // result must be manually disposed
* // when you are done using it
* result.dispose()
*
*/
var HDRJPGLoader = class extends LoaderBase {
	/**
	* Loads a JPEG containing gain map metadata
	* Renders a normal SDR image if gainmap data is not found
	*
	* @param url An array in the form of [sdr.jpg, gainmap.jpg, metadata.json]
	* @param onLoad Load complete callback, will receive the result
	* @param onProgress Progress callback, will receive a {@link ProgressEvent}
	* @param onError Error callback
	* @returns
	*/
	load(url, onLoad, onProgress, onError) {
		const quadRenderer = this.prepareQuadRenderer();
		const loader = new FileLoader(this._internalLoadingManager);
		loader.setResponseType("arraybuffer");
		loader.setRequestHeader(this.requestHeader);
		loader.setPath(this.path);
		loader.setWithCredentials(this.withCredentials);
		this.manager.itemStart(url);
		loader.load(url, async (jpeg) => {
			/* istanbul ignore if
			this condition exists only because of three.js types + strict mode
			*/
			if (typeof jpeg === "string") throw new Error("Invalid buffer, received [string], was expecting [ArrayBuffer]");
			const jpegBuffer = new Uint8Array(jpeg);
			let sdrJPEG;
			let gainMapJPEG;
			let metadata;
			try {
				const extractionResult = await extractGainmapFromJPEG(jpegBuffer);
				sdrJPEG = extractionResult.sdr;
				gainMapJPEG = extractionResult.gainMap;
				metadata = extractionResult.metadata;
			} catch (e) {
				if (e instanceof XMPMetadataNotFoundError || e instanceof GainMapNotFoundError) {
					console.warn(`Failure to reconstruct an HDR image from ${url}: Gain map metadata not found in the file, HDRJPGLoader will render the SDR jpeg`);
					metadata = {
						gainMapMin: [
							0,
							0,
							0
						],
						gainMapMax: [
							1,
							1,
							1
						],
						gamma: [
							1,
							1,
							1
						],
						hdrCapacityMin: 0,
						hdrCapacityMax: 1,
						offsetHdr: [
							0,
							0,
							0
						],
						offsetSdr: [
							0,
							0,
							0
						]
					};
					sdrJPEG = jpegBuffer;
				} else throw e;
			}
			try {
				await this.render(quadRenderer, metadata, sdrJPEG, gainMapJPEG);
			} catch (error) {
				this.manager.itemError(url);
				if (typeof onError === "function") onError(error);
				quadRenderer.disposeOnDemandRenderer();
				return;
			}
			if (typeof onLoad === "function") onLoad(quadRenderer);
			this.manager.itemEnd(url);
			quadRenderer.disposeOnDemandRenderer();
		}, onProgress, (error) => {
			this.manager.itemError(url);
			if (typeof onError === "function") onError(error);
		});
		return quadRenderer;
	}
};

//#endregion
export { HDRJPGLoader as n, GainMapLoader as t };