6 lines
6.6 KiB
JavaScript
6 lines
6.6 KiB
JavaScript
/**
|
|
* @monogrid/gainmap-js v3.4.0
|
|
* With ❤️, by MONOGRID <gainmap@monogrid.com>
|
|
*/
|
|
import{SRGBColorSpace as e,LinearSRGBColorSpace as t,HalfFloatType as a,Loader as r,LoadingManager as n,Texture as i,UVMapping as o,ClampToEdgeWrapping as s,LinearFilter as d,LinearMipMapLinearFilter as p,RGBAFormat as c,UnsignedByteType as l}from"three";function g(r){return n=>{const{sdr:i,gainMap:o,renderer:s}=n;i.colorSpace!==e&&(console.warn("SDR Colorspace needs to be *SRGBColorSpace*, setting it automatically"),i.colorSpace=e),i.needsUpdate=!0,o.colorSpace!==t&&(console.warn("Gainmap Colorspace needs to be *LinearSRGBColorSpace*, setting it automatically"),o.colorSpace=t),o.needsUpdate=!0;const d=r.createMaterial({...n,sdr:i,gainMap:o});return r.createQuadRenderer({width:i.image.width,height:i.image.height,type:a,colorSpace:t,material:d,renderer:s,renderTargetOptions:n.renderTargetOptions})}}class m extends Error{}class f extends Error{}const h=(e,t,a)=>{const r=new RegExp(`${t}="([^"]*)"`,"i").exec(e);if(r)return r[1];const n=new RegExp(`<${t}[^>]*>([\\s\\S]*?)</${t}>`,"i").exec(e);if(n){const e=n[1].match(/<rdf:li>([^<]*)<\/rdf:li>/g);return e&&3===e.length?e.map(e=>e.replace(/<\/?rdf:li>/g,"")):n[1].trim()}if(void 0!==a)return a;throw new Error(`Can't find ${t} in gainmap metadata`)},u=e=>{let t;t="undefined"!=typeof TextDecoder?(new TextDecoder).decode(e):e.toString();let a=t.indexOf("<x:xmpmeta");for(;-1!==a;){const e=t.indexOf("x:xmpmeta>",a),r=t.slice(a,e+10);try{const e=h(r,"hdrgm:GainMapMin","0"),t=h(r,"hdrgm:GainMapMax"),a=h(r,"hdrgm:Gamma","1"),n=h(r,"hdrgm:OffsetSDR","0.015625"),i=h(r,"hdrgm:OffsetHDR","0.015625"),o=/hdrgm:HDRCapacityMin="([^"]*)"/.exec(r),s=o?o[1]:"0",d=/hdrgm:HDRCapacityMax="([^"]*)"/.exec(r);if(!d)throw new Error("Incomplete gainmap metadata");const p=d[1];return{gainMapMin:Array.isArray(e)?e.map(e=>parseFloat(e)):[parseFloat(e),parseFloat(e),parseFloat(e)],gainMapMax:Array.isArray(t)?t.map(e=>parseFloat(e)):[parseFloat(t),parseFloat(t),parseFloat(t)],gamma:Array.isArray(a)?a.map(e=>parseFloat(e)):[parseFloat(a),parseFloat(a),parseFloat(a)],offsetSdr:Array.isArray(n)?n.map(e=>parseFloat(e)):[parseFloat(n),parseFloat(n),parseFloat(n)],offsetHdr:Array.isArray(i)?i.map(e=>parseFloat(e)):[parseFloat(i),parseFloat(i),parseFloat(i)],hdrCapacityMin:parseFloat(s),hdrCapacityMax:parseFloat(p)}}catch(e){}a=t.indexOf("<x:xmpmeta",e)}};class x{options;constructor(e){this.options={debug:!(!e||void 0===e.debug)&&e.debug,extractFII:!e||void 0===e.extractFII||e.extractFII,extractNonFII:!e||void 0===e.extractNonFII||e.extractNonFII}}extract(e){return new Promise((t,a)=>{const r=this.options.debug,n=new DataView(e.buffer);if(65496!==n.getUint16(0))return void a(new Error("Not a valid jpeg"));const i=n.byteLength;let o,s=2,d=0;for(;s<i;){if(++d>250)return void a(new Error(`Found no marker after ${d} loops 😵`));if(255!==n.getUint8(s))return void a(new Error(`Not a valid marker at offset 0x${s.toString(16)}, found: 0x${n.getUint8(s).toString(16)}`));if(o=n.getUint8(s+1),r&&console.log(`Marker: ${o.toString(16)}`),226===o){r&&console.log("Found APP2 marker (0xffe2)");const e=s+4;if(1297106432===n.getUint32(e)){const r=e+4;let i;if(18761===n.getUint16(r))i=!1;else{if(19789!==n.getUint16(r))return void a(new Error("No valid endianness marker found in TIFF header"));i=!0}if(42!==n.getUint16(r+2,!i))return void a(new Error("Not valid TIFF data! (no 0x002A marker)"));const o=n.getUint32(r+4,!i);if(o<8)return void a(new Error("Not valid TIFF data! (First offset less than 8)"));const s=r+o,d=n.getUint16(s,!i),p=s+2;let c=0;for(let e=p;e<p+12*d;e+=12)45057===n.getUint16(e,!i)&&(c=n.getUint32(e+8,!i));const l=s+2+12*d+4,g=[];for(let e=l;e<l+16*c;e+=16){const t={MPType:n.getUint32(e,!i),size:n.getUint32(e+4,!i),dataOffset:n.getUint32(e+8,!i),dependantImages:n.getUint32(e+12,!i),start:-1,end:-1,isFII:!1};t.dataOffset?(t.start=r+t.dataOffset,t.isFII=!1):(t.start=0,t.isFII=!0),t.end=t.start+t.size,g.push(t)}if(this.options.extractNonFII&&g.length){const e=new Blob([n]),a=[];for(const t of g){if(t.isFII&&!this.options.extractFII)continue;const r=e.slice(t.start,t.end+1,"image/jpeg");a.push(r)}t(a)}}}s+=2+n.getUint16(s+2)}})}}const w=async e=>{const t=u(e);if(!t)throw new f("Gain map XMP metadata not found");const a=new x({extractFII:!0,extractNonFII:!0}),r=await a.extract(e);if(2!==r.length)throw new m("Gain map recovery image not found");return{sdr:new Uint8Array(await r[0].arrayBuffer()),gainMap:new Uint8Array(await r[1].arrayBuffer()),metadata:t}},M=e=>new Promise((t,a)=>{const r=document.createElement("img");r.onload=()=>{t(r)},r.onerror=e=>{a(e)},r.src=URL.createObjectURL(e)});class F extends r{_renderer;_renderTargetOptions;_internalLoadingManager;_config;constructor(e,t){super(t),this._config=e,e.renderer&&(this._renderer=e.renderer),this._internalLoadingManager=new n}setRenderer(e){return this._renderer=e,this}setRenderTargetOptions(e){return this._renderTargetOptions=e,this}prepareQuadRenderer(){this._renderer||console.warn("WARNING: A 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.");const e=this._config.createMaterial({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 i,sdr:new i});return this._config.createQuadRenderer({width:16,height:16,type:a,colorSpace:t,material:e,renderer:this._renderer,renderTargetOptions:this._renderTargetOptions})}async processImages(e,t,a){const r=t?new Blob([t],{type:"image/jpeg"}):void 0,n=new Blob([e],{type:"image/jpeg"});let i,o,s=!1;if("undefined"==typeof createImageBitmap){const e=await Promise.all([r?M(r):Promise.resolve(void 0),M(n)]);o=e[0],i=e[1],s="flipY"===a}else{const e=await Promise.all([r?createImageBitmap(r,{imageOrientation:a||"flipY"}):Promise.resolve(void 0),createImageBitmap(n,{imageOrientation:a||"flipY"})]);o=e[0],i=e[1]}return{sdrImage:i,gainMapImage:o,needsFlip:s}}createTextures(a,r,n){const g=new i(r||new ImageData(2,2),o,s,s,d,p,c,l,1,t);g.flipY=n,g.needsUpdate=!0;const m=new i(a,o,s,s,d,p,c,l,1,e);return m.flipY=n,m.needsUpdate=!0,{gainMap:g,sdr:m}}updateQuadRenderer(e,t,a,r,n){e.width=t.width,e.height=t.height,e.material.gainMap=a,e.material.sdr=r,e.material.gainMapMin=n.gainMapMin,e.material.gainMapMax=n.gainMapMax,e.material.offsetHdr=n.offsetHdr,e.material.offsetSdr=n.offsetSdr,e.material.gamma=n.gamma,e.material.hdrCapacityMin=n.hdrCapacityMin,e.material.hdrCapacityMax=n.hdrCapacityMax,e.material.maxDisplayBoost=Math.pow(2,n.hdrCapacityMax),e.material.needsUpdate=!0}}export{m as G,F as L,x as M,f as X,u as a,g as c,w as e};
|