raw-webgpu 0.1.0-alpha.1
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/LICENSE +21 -0
- package/NOTICE +21 -0
- package/README.md +115 -0
- package/THIRD_PARTY_LICENSES.txt +2842 -0
- package/dist/decode/instantiate.d.ts +3 -0
- package/dist/decode/module.d.ts +2 -0
- package/dist/decode/session.d.ts +7 -0
- package/dist/decode/tiff-worker.d.ts +1 -0
- package/dist/decode/worker.d.ts +1 -0
- package/dist/develop/gpu.d.ts +27 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +710 -0
- package/dist/libraw.js +2 -0
- package/dist/libraw.wasm +0 -0
- package/dist/math.d.ts +4 -0
- package/dist/tiff/color.d.ts +17 -0
- package/dist/tiff/index.d.ts +7 -0
- package/dist/tiff/upload.d.ts +21 -0
- package/dist/tiff-worker.js +181 -0
- package/dist/types.d.ts +51 -0
- package/dist/worker.js +116 -0
- package/docs/conversion.md +52 -0
- package/native/bridge.cpp +119 -0
- package/native/build.py +229 -0
- package/native/calibration.cpp +136 -0
- package/native/direct.h +29 -0
- package/native/dng-no-xmp.patch +34 -0
- package/native/dng.cpp +42 -0
- package/native/pixels.cpp +213 -0
- package/native/raw.h +47 -0
- package/native/tiff.cpp +164 -0
- package/package.json +68 -0
package/dist/libraw.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
async function Module(moduleArg={}){var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var runtimeInitialized=false;function getMemoryBuffer(){return wasmMemory.buffer}function updateMemoryViews(){if(HEAP8?.buffer?.resizable)return;var b=getMemoryBuffer();HEAP8=new Int8Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}function preRun(){}function initRuntime(){runtimeInitialized=true;wasmExports["s"]()}function postRun(){}function abort(what){what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";if(runtimeInitialized){___trap()}var e=new WebAssembly.RuntimeError(what);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("libraw.wasm")}return new URL("libraw.wasm",import.meta.url).href}function getBinarySync(file){if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();var instantiateWasm=Module["instantiateWasm"];if(instantiateWasm){return new Promise(resolve=>{instantiateWasm(info,inst=>resolve(receiveInstance(inst)))})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP8;var UTF8Decoder=new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var HEAPU8;var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>{if(!ptr)return"";var end=findStringEnd(HEAPU8,ptr,maxBytesToRead,ignoreNul);return UTF8Decoder.decode(HEAPU8.subarray(ptr,end))};var SYSCALLS={varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;return 0}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;return 0}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs}var __abort_js=()=>abort("");var runtimeKeepaliveCounter=0;var __emscripten_runtime_keepalive_clear=()=>{runtimeKeepaliveCounter=0};var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>num<INT53_MIN||num>INT53_MAX?NaN:Number(num);var HEAP32;function __localtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);if(isNaN(date.getTime())){return 1}HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst;return 0}var __mktime_js=function(tmPtr){var ret=(()=>{var date=new Date(HEAP32[tmPtr+20>>2]+1900,HEAP32[tmPtr+16>>2],HEAP32[tmPtr+12>>2],HEAP32[tmPtr+8>>2],HEAP32[tmPtr+4>>2],HEAP32[tmPtr>>2],0);if(isNaN(date.getTime())){return-1}var dst=HEAP32[tmPtr+32>>2];var guessedOffset=date.getTimezoneOffset();var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dstOffset=Math.min(winterOffset,summerOffset);if(dst<0){dst=Number(summerOffset!=winterOffset&&dstOffset==guessedOffset)}else if(dst>0!=(dstOffset==guessedOffset)){var nonDstOffset=Math.max(winterOffset,summerOffset);var trueOffset=dst>0?dstOffset:nonDstOffset;date.setTime(date.getTime()+(trueOffset-guessedOffset)*6e4);if(isNaN(date.getTime())){return-1}}HEAP32[tmPtr+32>>2]=dst;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getYear();return date.getTime()/1e3})();return BigInt(ret)};var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var keepRuntimeAlive=()=>true;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{return func()}catch(e){handleException(e)}finally{maybeExit()}};var _emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var HEAPU32;var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffset<winterOffset){stringToUTF8(winterName,std_name,17);stringToUTF8(summerName,dst_name,17)}else{stringToUTF8(winterName,dst_name,17);stringToUTF8(summerName,std_name,17)}};var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram;var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var _fd_close=fd=>52;var _fd_read=(fd,iov,iovcnt,pnum)=>52;function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);return 70}var printCharBuffers=[null,[],[]];var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);return UTF8Decoder.decode(heapOrArray.buffer?heapOrArray.subarray(idx,endPtr):new Uint8Array(heapOrArray.slice(idx,endPtr)))};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(!curr||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};var HEAPU16;var HEAPF64;{}Module["UTF8ToString"]=UTF8ToString;var _raw_error,_raw_open,_raw_calibration,_raw_original_offset,_raw_width,_raw_height,_raw_flip,_raw_cpu_demosaic,_raw_float,_raw_daylight_balance,_raw_mosaic,_raw_cfa_size,_raw_cfa,_raw_black,_raw_vignette,_raw_white,_raw_pixels,_raw_free_pixels,_raw_temperature,_raw_tint,_raw_close,_tiff_open,_tiff_error,_tiff_metadata,_tiff_original_offset,_tiff_big_endian,_tiff_pixels,_tiff_profile,_tiff_profile_size,_tiff_close,_malloc,_free,__emscripten_timeout,___trap,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){_raw_error=Module["_raw_error"]=wasmExports["t"];_raw_open=Module["_raw_open"]=wasmExports["u"];_raw_calibration=Module["_raw_calibration"]=wasmExports["v"];_raw_original_offset=Module["_raw_original_offset"]=wasmExports["w"];_raw_width=Module["_raw_width"]=wasmExports["x"];_raw_height=Module["_raw_height"]=wasmExports["y"];_raw_flip=Module["_raw_flip"]=wasmExports["z"];_raw_cpu_demosaic=Module["_raw_cpu_demosaic"]=wasmExports["A"];_raw_float=Module["_raw_float"]=wasmExports["B"];_raw_daylight_balance=Module["_raw_daylight_balance"]=wasmExports["C"];_raw_mosaic=Module["_raw_mosaic"]=wasmExports["D"];_raw_cfa_size=Module["_raw_cfa_size"]=wasmExports["E"];_raw_cfa=Module["_raw_cfa"]=wasmExports["F"];_raw_black=Module["_raw_black"]=wasmExports["G"];_raw_vignette=Module["_raw_vignette"]=wasmExports["H"];_raw_white=Module["_raw_white"]=wasmExports["I"];_raw_pixels=Module["_raw_pixels"]=wasmExports["J"];_raw_free_pixels=Module["_raw_free_pixels"]=wasmExports["K"];_raw_temperature=Module["_raw_temperature"]=wasmExports["L"];_raw_tint=Module["_raw_tint"]=wasmExports["M"];_raw_close=Module["_raw_close"]=wasmExports["N"];_tiff_open=Module["_tiff_open"]=wasmExports["O"];_tiff_error=Module["_tiff_error"]=wasmExports["P"];_tiff_metadata=Module["_tiff_metadata"]=wasmExports["Q"];_tiff_original_offset=Module["_tiff_original_offset"]=wasmExports["R"];_tiff_big_endian=Module["_tiff_big_endian"]=wasmExports["S"];_tiff_pixels=Module["_tiff_pixels"]=wasmExports["T"];_tiff_profile=Module["_tiff_profile"]=wasmExports["U"];_tiff_profile_size=Module["_tiff_profile_size"]=wasmExports["V"];_tiff_close=Module["_tiff_close"]=wasmExports["W"];_malloc=Module["_malloc"]=wasmExports["X"];_free=Module["_free"]=wasmExports["Y"];__emscripten_timeout=wasmExports["Z"];___trap=wasmExports["_"];memory=wasmMemory=wasmExports["r"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={b:___syscall_fcntl64,h:___syscall_ioctl,p:___syscall_openat,q:__abort_js,l:__emscripten_runtime_keepalive_clear,m:__localtime_js,n:__mktime_js,i:__setitimer_js,o:__tzset_js,j:_emscripten_resize_heap,d:_environ_get,e:_environ_sizes_get,a:_fd_close,g:_fd_read,c:_fd_seek,f:_fd_write,k:_proc_exit};async function run(){preRun();if(ABORT)return;initRuntime();postRun()}var wasmExports;wasmExports=await createWasm();await run();
|
|
2
|
+
;return Module}export default Module;
|
package/dist/libraw.wasm
ADDED
|
Binary file
|
package/dist/math.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Mat3 } from "../math";
|
|
2
|
+
type Curve = (encoded: number) => number;
|
|
3
|
+
type Profile = {
|
|
4
|
+
colorants: Mat3;
|
|
5
|
+
curves: [Curve, Curve, Curve];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Reads a matrix/TRC profile: RGB colorants and a curve per channel, or a single curve for gray.
|
|
9
|
+
* Unsupported lookup-table profiles and malformed profiles fall back to sRGB.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readProfile(bytes: Uint8Array): Profile | undefined;
|
|
12
|
+
/** ICC curves and camera-independent conversion into linear Rec.2020. */
|
|
13
|
+
export declare function tiffColor(icc: Uint8Array | undefined, linear: boolean): {
|
|
14
|
+
table: Float32Array<ArrayBuffer>;
|
|
15
|
+
matrix: number[];
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LoadOptions } from "../types";
|
|
2
|
+
/** Decode TIFF into an owned linear Rec.2020 RGBA16F texture, with straight alpha. */
|
|
3
|
+
export declare function decodeTiff(device: GPUDevice, file: Blob, { signal }?: LoadOptions): Promise<{
|
|
4
|
+
texture: GPUTexture;
|
|
5
|
+
size: [number, number];
|
|
6
|
+
dispose: () => undefined;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type TiffPixels = {
|
|
2
|
+
/** Interleaved samples, one row after another, exactly as the file or the SDK laid them out. */
|
|
3
|
+
data: Uint8Array;
|
|
4
|
+
/**
|
|
5
|
+
* Nine values from native/tiff.cpp: width, height, channels, bytes per sample,
|
|
6
|
+
* bits per sample (zero for float), orientation, photometric interpretation,
|
|
7
|
+
* extra-sample kind (1 for associated alpha), row bytes.
|
|
8
|
+
*/
|
|
9
|
+
metadata: Uint32Array;
|
|
10
|
+
/** Whether the samples still carry the file's big-endian byte order. */
|
|
11
|
+
bigEndian: boolean;
|
|
12
|
+
color: {
|
|
13
|
+
table: Float32Array;
|
|
14
|
+
matrix: number[];
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare function uploadTiff(device: GPUDevice, pixels: TiffPixels): Promise<{
|
|
18
|
+
texture: GPUTexture;
|
|
19
|
+
size: [number, number];
|
|
20
|
+
dispose: () => undefined;
|
|
21
|
+
}>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// src/math.ts
|
|
2
|
+
function multiplyMat3(a, b) {
|
|
3
|
+
const result = [];
|
|
4
|
+
for (let column = 0;column < 3; column++) {
|
|
5
|
+
const [x, y, z] = b.slice(column * 3, column * 3 + 3);
|
|
6
|
+
for (let row = 0;row < 3; row++) {
|
|
7
|
+
result.push(a[row] * x + a[3 + row] * y + a[6 + row] * z);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/tiff/color.ts
|
|
14
|
+
var tableSize = 1024;
|
|
15
|
+
var d50 = [0.9642, 1, 0.8249];
|
|
16
|
+
var identity = (v) => v;
|
|
17
|
+
var xyzToRec2020 = [
|
|
18
|
+
1.6472945,
|
|
19
|
+
-0.6826024,
|
|
20
|
+
0.0296711,
|
|
21
|
+
-0.3935777,
|
|
22
|
+
1.6475829,
|
|
23
|
+
-0.0629319,
|
|
24
|
+
-0.2359823,
|
|
25
|
+
0.0128128,
|
|
26
|
+
1.253617
|
|
27
|
+
];
|
|
28
|
+
var srgbCurve = (v) => v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
|
|
29
|
+
var srgb = {
|
|
30
|
+
colorants: [
|
|
31
|
+
0.4361,
|
|
32
|
+
0.2225,
|
|
33
|
+
0.0139,
|
|
34
|
+
0.3851,
|
|
35
|
+
0.7169,
|
|
36
|
+
0.0971,
|
|
37
|
+
0.1431,
|
|
38
|
+
0.0606,
|
|
39
|
+
0.7141
|
|
40
|
+
],
|
|
41
|
+
curves: [srgbCurve, srgbCurve, srgbCurve]
|
|
42
|
+
};
|
|
43
|
+
function readProfile(bytes) {
|
|
44
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
45
|
+
const text = (at) => String.fromCharCode(...bytes.subarray(at, at + 4));
|
|
46
|
+
const fixed = (at) => view.getInt32(at) / 65536;
|
|
47
|
+
if (bytes.length < 132 || text(36) !== "acsp") {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const tags = new Map;
|
|
51
|
+
for (let i = 0, count = view.getUint32(128);i < count; i++) {
|
|
52
|
+
tags.set(text(132 + i * 12), view.getUint32(136 + i * 12));
|
|
53
|
+
}
|
|
54
|
+
const xyz = (name) => {
|
|
55
|
+
const at = tags.get(name);
|
|
56
|
+
return at === undefined ? undefined : [fixed(at + 8), fixed(at + 12), fixed(at + 16)];
|
|
57
|
+
};
|
|
58
|
+
const curve = (name) => {
|
|
59
|
+
const at = tags.get(name);
|
|
60
|
+
if (at === undefined) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (text(at) === "curv") {
|
|
64
|
+
const count = view.getUint32(at + 8);
|
|
65
|
+
const entry = (i) => view.getUint16(at + 12 + 2 * Math.min(i, count - 1)) / 65535;
|
|
66
|
+
if (count === 0) {
|
|
67
|
+
return identity;
|
|
68
|
+
}
|
|
69
|
+
if (count === 1) {
|
|
70
|
+
const gamma = view.getUint16(at + 12) / 256;
|
|
71
|
+
return (x) => x ** gamma;
|
|
72
|
+
}
|
|
73
|
+
return (x) => {
|
|
74
|
+
const position = x * (count - 1);
|
|
75
|
+
const low = Math.floor(position);
|
|
76
|
+
return entry(low) + (entry(low + 1) - entry(low)) * (position - low);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (text(at) === "para") {
|
|
80
|
+
const kind = view.getUint16(at + 8);
|
|
81
|
+
const [g, a = 1, b = 0, c = 0, d = 0, e = 0, f = 0] = Array.from({ length: [1, 3, 4, 5, 7][kind] ?? 0 }, (_, i) => fixed(at + 12 + i * 4));
|
|
82
|
+
if (kind === 0) {
|
|
83
|
+
return (x) => x ** g;
|
|
84
|
+
}
|
|
85
|
+
if (kind <= 2) {
|
|
86
|
+
return (x) => x >= -b / a ? (a * x + b) ** g + c : c;
|
|
87
|
+
}
|
|
88
|
+
return (x) => x >= d ? (a * x + b) ** g + e : c * x + f;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
try {
|
|
92
|
+
if (text(16) === "GRAY") {
|
|
93
|
+
const k = curve("kTRC");
|
|
94
|
+
return k && {
|
|
95
|
+
colorants: [...d50, 0, 0, 0, 0, 0, 0],
|
|
96
|
+
curves: [k, k, k]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const [r, g, b] = [xyz("rXYZ"), xyz("gXYZ"), xyz("bXYZ")];
|
|
100
|
+
const [rc, gc, bc] = [curve("rTRC"), curve("gTRC"), curve("bTRC")];
|
|
101
|
+
if (text(16) !== "RGB " || !r || !g || !b || !rc || !gc || !bc) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
colorants: [...r, ...g, ...b],
|
|
106
|
+
curves: [rc, gc, bc]
|
|
107
|
+
};
|
|
108
|
+
} catch {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
var sampleCurves = (curves) => Float32Array.from({ length: 3 * tableSize }, (_, i) => curves[Math.floor(i / tableSize)](i % tableSize / (tableSize - 1)));
|
|
113
|
+
function tiffColor(icc, linear) {
|
|
114
|
+
let profile = (icc && readProfile(icc)) ?? srgb;
|
|
115
|
+
let table = sampleCurves(linear ? [identity, identity, identity] : profile.curves);
|
|
116
|
+
if (!table.every(Number.isFinite)) {
|
|
117
|
+
profile = srgb;
|
|
118
|
+
table = sampleCurves(profile.curves);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
table,
|
|
122
|
+
matrix: multiplyMat3(xyzToRec2020, profile.colorants)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/decode/instantiate.ts
|
|
127
|
+
import createLibRaw from "./libraw.js";
|
|
128
|
+
function instantiateDecoder(module) {
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
createLibRaw({
|
|
131
|
+
instantiateWasm(imports, ready) {
|
|
132
|
+
WebAssembly.instantiate(module, imports).then((instance) => ready(instance, module), reject);
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
135
|
+
}).then(resolve, reject);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/decode/tiff-worker.ts
|
|
140
|
+
self.onmessage = async ({
|
|
141
|
+
data: { file, module }
|
|
142
|
+
}) => {
|
|
143
|
+
try {
|
|
144
|
+
const raw = await instantiateDecoder(module);
|
|
145
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
146
|
+
const pointer = raw._malloc(bytes.length);
|
|
147
|
+
if (!pointer) {
|
|
148
|
+
throw Error("Not enough memory to open TIFF.");
|
|
149
|
+
}
|
|
150
|
+
let source = 0;
|
|
151
|
+
try {
|
|
152
|
+
raw.HEAPU8.set(bytes, pointer);
|
|
153
|
+
source = raw._tiff_open(pointer, bytes.length);
|
|
154
|
+
if (!source) {
|
|
155
|
+
throw Error(raw.UTF8ToString(raw._tiff_error()));
|
|
156
|
+
}
|
|
157
|
+
const metadataIndex = raw._tiff_metadata(source) / 4;
|
|
158
|
+
const metadata = raw.HEAPU32.slice(metadataIndex, metadataIndex + 9);
|
|
159
|
+
const [, height, , , bitsPerSample, , , , rowBytes] = metadata;
|
|
160
|
+
const byteLength = height * rowBytes;
|
|
161
|
+
const originalOffset = raw._tiff_original_offset(source);
|
|
162
|
+
const pixelPointer = raw._tiff_pixels(source);
|
|
163
|
+
const data = originalOffset ? bytes.subarray(originalOffset, originalOffset + byteLength) : raw.HEAPU8.slice(pixelPointer, pixelPointer + byteLength);
|
|
164
|
+
const bigEndian = Boolean(raw._tiff_big_endian(source));
|
|
165
|
+
const profilePointer = raw._tiff_profile(source);
|
|
166
|
+
const profileSize = raw._tiff_profile_size(source);
|
|
167
|
+
const profile = profileSize ? raw.HEAPU8.slice(profilePointer, profilePointer + profileSize) : undefined;
|
|
168
|
+
const color = tiffColor(profile, bitsPerSample === 0);
|
|
169
|
+
self.postMessage({ data, metadata, color, bigEndian }, { transfer: [data.buffer, metadata.buffer, color.table.buffer] });
|
|
170
|
+
} finally {
|
|
171
|
+
if (source) {
|
|
172
|
+
raw._tiff_close(source);
|
|
173
|
+
}
|
|
174
|
+
raw._free(pointer);
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
self.postMessage({
|
|
178
|
+
error: error instanceof Error ? error.message : String(error)
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type WhiteBalance = {
|
|
2
|
+
temperature: number;
|
|
3
|
+
tint: number;
|
|
4
|
+
};
|
|
5
|
+
/** Column-major camera RGB to linear Rec.2020/D65, after applying gains. */
|
|
6
|
+
export type Calibration = {
|
|
7
|
+
gains: number[];
|
|
8
|
+
matrix: number[];
|
|
9
|
+
};
|
|
10
|
+
export type RawMetadata = {
|
|
11
|
+
/** Sensor dimensions before applying the LibRaw orientation bitmask. */
|
|
12
|
+
size: [number, number];
|
|
13
|
+
/** LibRaw orientation bitmask: 4 transposes, 2 mirrors vertically, 1 mirrors horizontally. */
|
|
14
|
+
flip: number;
|
|
15
|
+
/** Row-major repeating CFA: R=0, G=1 or 3, B=2; null means camera RGB. */
|
|
16
|
+
cfa: number[] | null;
|
|
17
|
+
/** Square CFA side length; defaults to 2 for Bayer. */
|
|
18
|
+
cfaSize?: number;
|
|
19
|
+
/** Black level per CFA color index, in sensor units. */
|
|
20
|
+
black: number[];
|
|
21
|
+
/** Saturation level, in sensor units. */
|
|
22
|
+
white: number;
|
|
23
|
+
/** Five radial coefficients, two origin coordinates, two coordinate scales. */
|
|
24
|
+
vignette: number[];
|
|
25
|
+
demosaic: "gpu" | "cpu" | "none";
|
|
26
|
+
/** Float samples are already normalized by the DNG SDK. */
|
|
27
|
+
sampleFormat?: "uint16" | "float32";
|
|
28
|
+
whiteBalanceOrigin?: "as-shot" | "daylight";
|
|
29
|
+
};
|
|
30
|
+
/** One uint16 sample per mosaic pixel; four uint16 or float32 samples for camera RGB. */
|
|
31
|
+
export type RawPixels = RawMetadata & {
|
|
32
|
+
data: Uint16Array<ArrayBuffer> | Float32Array<ArrayBuffer>;
|
|
33
|
+
};
|
|
34
|
+
export type RawReply = {
|
|
35
|
+
/** Present only when the request opened a file. */
|
|
36
|
+
image?: RawPixels;
|
|
37
|
+
calibration: Calibration;
|
|
38
|
+
asShot: WhiteBalance;
|
|
39
|
+
};
|
|
40
|
+
export type LoadOptions = {
|
|
41
|
+
/** Aborting rejects a pending load with the signal's reason; a loaded source is unaffected. */
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
};
|
|
44
|
+
export type DevelopOptions = {
|
|
45
|
+
destination: GPUTexture;
|
|
46
|
+
calibration: Calibration;
|
|
47
|
+
/** Exposure in stops, default zero. */
|
|
48
|
+
exposure?: number;
|
|
49
|
+
/** If omitted, submit immediately on the consumer's device. */
|
|
50
|
+
encoder?: GPUCommandEncoder;
|
|
51
|
+
};
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// src/decode/instantiate.ts
|
|
2
|
+
import createLibRaw from "./libraw.js";
|
|
3
|
+
function instantiateDecoder(module) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
createLibRaw({
|
|
6
|
+
instantiateWasm(imports, ready) {
|
|
7
|
+
WebAssembly.instantiate(module, imports).then((instance) => ready(instance, module), reject);
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
}).then(resolve, reject);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/decode/worker.ts
|
|
15
|
+
var module = Promise.withResolvers();
|
|
16
|
+
var decoder = module.promise.then(instantiateDecoder);
|
|
17
|
+
var source = 0;
|
|
18
|
+
var asShot;
|
|
19
|
+
self.onmessage = async ({
|
|
20
|
+
data
|
|
21
|
+
}) => {
|
|
22
|
+
if ("module" in data) {
|
|
23
|
+
module.resolve(data.module);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const { id, value } = data;
|
|
27
|
+
try {
|
|
28
|
+
const raw = await decoder;
|
|
29
|
+
let image;
|
|
30
|
+
let temperature = 0;
|
|
31
|
+
let tint = 0;
|
|
32
|
+
if (value instanceof Blob) {
|
|
33
|
+
const bytes = new Uint8Array(await value.arrayBuffer());
|
|
34
|
+
const pointer2 = raw._malloc(bytes.length);
|
|
35
|
+
if (!pointer2) {
|
|
36
|
+
throw Error("Not enough memory to open this RAW image.");
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
raw.HEAPU8.set(bytes, pointer2);
|
|
40
|
+
if (source) {
|
|
41
|
+
raw._raw_close(source);
|
|
42
|
+
source = 0;
|
|
43
|
+
}
|
|
44
|
+
source = raw._raw_open(pointer2, bytes.length);
|
|
45
|
+
if (!source) {
|
|
46
|
+
throw Error(raw.UTF8ToString(raw._raw_error()));
|
|
47
|
+
}
|
|
48
|
+
const width = raw._raw_width(source);
|
|
49
|
+
const height = raw._raw_height(source);
|
|
50
|
+
const mosaic = Boolean(raw._raw_mosaic(source));
|
|
51
|
+
const demosaic = raw._raw_cpu_demosaic(source) ? "cpu" : "none";
|
|
52
|
+
const originalOffset = raw._raw_original_offset(source);
|
|
53
|
+
const floating = Boolean(raw._raw_float(source));
|
|
54
|
+
const pixelPointer = raw._raw_pixels(source);
|
|
55
|
+
const Samples = floating ? Float32Array : Uint16Array;
|
|
56
|
+
const sampleCount = width * height * (mosaic ? 1 : 4);
|
|
57
|
+
const blackIndex = raw._raw_black(source) / 8;
|
|
58
|
+
const cfaIndex = raw._raw_cfa(source) / 4;
|
|
59
|
+
const cfaSize = raw._raw_cfa_size(source);
|
|
60
|
+
const vignetteIndex = raw._raw_vignette(source) / 8;
|
|
61
|
+
image = {
|
|
62
|
+
size: [width, height],
|
|
63
|
+
sampleFormat: floating ? "float32" : "uint16",
|
|
64
|
+
whiteBalanceOrigin: raw._raw_daylight_balance(source) ? "daylight" : "as-shot",
|
|
65
|
+
demosaic: mosaic ? "gpu" : demosaic,
|
|
66
|
+
data: originalOffset ? new Uint16Array(bytes.buffer, originalOffset, width * height) : new Samples(raw.HEAPU8.slice(pixelPointer, pixelPointer + sampleCount * Samples.BYTES_PER_ELEMENT).buffer),
|
|
67
|
+
black: Array.from(raw.HEAPF64.subarray(blackIndex, blackIndex + 4)),
|
|
68
|
+
white: raw._raw_white(source),
|
|
69
|
+
flip: raw._raw_flip(source),
|
|
70
|
+
vignette: Array.from(raw.HEAPF64.subarray(vignetteIndex, vignetteIndex + 9)),
|
|
71
|
+
cfaSize,
|
|
72
|
+
cfa: mosaic ? Array.from(raw.HEAPU32.subarray(cfaIndex, cfaIndex + cfaSize * cfaSize)) : null
|
|
73
|
+
};
|
|
74
|
+
asShot = {
|
|
75
|
+
temperature: raw._raw_temperature(source),
|
|
76
|
+
tint: raw._raw_tint(source)
|
|
77
|
+
};
|
|
78
|
+
raw._raw_free_pixels(source);
|
|
79
|
+
} finally {
|
|
80
|
+
raw._free(pointer2);
|
|
81
|
+
}
|
|
82
|
+
} else if (value.temperature !== asShot.temperature || value.tint !== asShot.tint) {
|
|
83
|
+
temperature = value.temperature;
|
|
84
|
+
tint = value.tint;
|
|
85
|
+
}
|
|
86
|
+
const pointer = raw._raw_calibration(source, temperature, tint);
|
|
87
|
+
if (!pointer) {
|
|
88
|
+
throw Error(raw.UTF8ToString(raw._raw_error()));
|
|
89
|
+
}
|
|
90
|
+
const values = raw.HEAPF64.subarray(pointer / 8, pointer / 8 + 12);
|
|
91
|
+
const reply = {
|
|
92
|
+
image,
|
|
93
|
+
asShot,
|
|
94
|
+
calibration: {
|
|
95
|
+
gains: Array.from(values.subarray(0, 3)),
|
|
96
|
+
matrix: [
|
|
97
|
+
values[3],
|
|
98
|
+
values[6],
|
|
99
|
+
values[9],
|
|
100
|
+
values[4],
|
|
101
|
+
values[7],
|
|
102
|
+
values[10],
|
|
103
|
+
values[5],
|
|
104
|
+
values[8],
|
|
105
|
+
values[11]
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
self.postMessage({ id, ...reply }, { transfer: image ? [image.data.buffer] : [] });
|
|
110
|
+
} catch (error) {
|
|
111
|
+
self.postMessage({
|
|
112
|
+
id,
|
|
113
|
+
error: error instanceof Error ? error.message : String(error)
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Convert a RAW file to PNG with Bun
|
|
2
|
+
|
|
3
|
+
This example uses `vgpu/node` for a real GPU device and `pngjs` for encoding. Install those in your script's project with `bun add vgpu pngjs`, alongside the built `raw-webgpu` package. Save the snippet as `convert.ts` and run `bun convert.ts`.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { PNG } from "pngjs";
|
|
7
|
+
import { init, target } from "vgpu/node";
|
|
8
|
+
import { createRawDecoder } from "raw-webgpu";
|
|
9
|
+
|
|
10
|
+
function srgb(value: number) {
|
|
11
|
+
const x = Math.max(0, Math.min(1, value));
|
|
12
|
+
return Math.round(255 * (
|
|
13
|
+
x <= 0.0031308 ? 12.92 * x : 1.055 * x ** (1 / 2.4) - 0.055
|
|
14
|
+
));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const gpu = await init();
|
|
18
|
+
const decoder = createRawDecoder(gpu.gpu);
|
|
19
|
+
try {
|
|
20
|
+
const source = await decoder.load(Bun.file("photo.dng"));
|
|
21
|
+
const output = target(gpu, { size: source.size, format: "rgba16float" });
|
|
22
|
+
try {
|
|
23
|
+
source.createDevelopPass().render({
|
|
24
|
+
destination: output.color.gpu,
|
|
25
|
+
calibration: source.calibration,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const pixels = await output.readFloats();
|
|
29
|
+
const image = new PNG({ width: source.size[0], height: source.size[1] });
|
|
30
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
31
|
+
const [r, g, b] = pixels.subarray(i, i + 3);
|
|
32
|
+
// Linear Rec.2020 → linear sRGB → encoded sRGB.
|
|
33
|
+
image.data[i] = srgb(1.660491 * r - 0.587641 * g - 0.072850 * b);
|
|
34
|
+
image.data[i + 1] = srgb(-0.124550 * r + 1.132900 * g - 0.008349 * b);
|
|
35
|
+
image.data[i + 2] = srgb(-0.018151 * r - 0.100579 * g + 1.118730 * b);
|
|
36
|
+
image.data[i + 3] = 255;
|
|
37
|
+
}
|
|
38
|
+
await Bun.write("photo.png", PNG.sync.write(image));
|
|
39
|
+
} finally {
|
|
40
|
+
output.color.dispose();
|
|
41
|
+
}
|
|
42
|
+
} finally {
|
|
43
|
+
decoder.dispose();
|
|
44
|
+
gpu.dispose();
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
For JPEG, install `jpeg-js`, import its `encode` function and replace the final write with `await Bun.write("photo.jpg", encode(image, 95).data)`. For BMP, use `bmp-js` instead and call `image.data.swap32()` before encoding because it expects ABGR bytes rather than RGBA.
|
|
49
|
+
|
|
50
|
+
Bun supplies the worker and local file-fetch APIs; Chromium is not required. Plain Node.js is not supported by this snippet. A working GPU backend is required; `vgpu/mock` cannot execute the development shader.
|
|
51
|
+
|
|
52
|
+
The example uses the initial white balance, develops on GPU, then reads back and converts color on CPU. Output is 8-bit SDR/sRGB with clipping and no photographic tone curve. File encoding belongs to the consuming application; the library does not include image encoders or a conversion helper.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// C entry points called from src/decode/worker.ts; see src/decode/libraw.d.ts for the signatures.
|
|
2
|
+
|
|
3
|
+
#include "raw.h"
|
|
4
|
+
#include <emscripten/emscripten.h>
|
|
5
|
+
#include <stdexcept>
|
|
6
|
+
#include <string>
|
|
7
|
+
|
|
8
|
+
static std::string error;
|
|
9
|
+
|
|
10
|
+
extern "C" {
|
|
11
|
+
|
|
12
|
+
EMSCRIPTEN_KEEPALIVE const char *raw_error() {
|
|
13
|
+
return error.c_str();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
EMSCRIPTEN_KEEPALIVE Raw *raw_open(void *bytes, unsigned length) {
|
|
17
|
+
try {
|
|
18
|
+
auto source = std::make_unique<Raw>();
|
|
19
|
+
prepare_pixels(source.get(), bytes, length);
|
|
20
|
+
prepare_calibration(source.get(), bytes, length);
|
|
21
|
+
pack_pixels(source.get());
|
|
22
|
+
return source.release();
|
|
23
|
+
} catch (const std::exception &exception) {
|
|
24
|
+
error = exception.what();
|
|
25
|
+
} catch (...) {
|
|
26
|
+
error = "RAW calibration or decoding failed";
|
|
27
|
+
}
|
|
28
|
+
return nullptr;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
EMSCRIPTEN_KEEPALIVE double *raw_calibration(Raw *source, double temperature, double tint) {
|
|
32
|
+
try {
|
|
33
|
+
return calibrate(source, temperature, tint);
|
|
34
|
+
} catch (...) {
|
|
35
|
+
error = "RAW white balance calibration failed";
|
|
36
|
+
return nullptr;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
EMSCRIPTEN_KEEPALIVE unsigned raw_original_offset(Raw *source) {
|
|
41
|
+
return source->originalOffset;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
EMSCRIPTEN_KEEPALIVE unsigned raw_width(Raw *source) {
|
|
45
|
+
return source->width;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
EMSCRIPTEN_KEEPALIVE unsigned raw_height(Raw *source) {
|
|
49
|
+
return source->height;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
EMSCRIPTEN_KEEPALIVE unsigned raw_flip(Raw *source) {
|
|
53
|
+
return source->flip;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
EMSCRIPTEN_KEEPALIVE unsigned raw_cpu_demosaic(Raw *source) {
|
|
57
|
+
return source->cpuPrepared && source->sensorMosaic;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
EMSCRIPTEN_KEEPALIVE unsigned raw_float(Raw *source) {
|
|
61
|
+
return !source->prepared.empty();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
EMSCRIPTEN_KEEPALIVE unsigned raw_daylight_balance(Raw *source) {
|
|
65
|
+
return source->daylightBalance;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
EMSCRIPTEN_KEEPALIVE unsigned raw_mosaic(Raw *source) {
|
|
69
|
+
return source->mosaic;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
EMSCRIPTEN_KEEPALIVE unsigned raw_cfa_size(Raw *source) {
|
|
73
|
+
return source->cfaSize;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
EMSCRIPTEN_KEEPALIVE unsigned *raw_cfa(Raw *source) {
|
|
77
|
+
return source->cfa;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
EMSCRIPTEN_KEEPALIVE double *raw_black(Raw *source) {
|
|
81
|
+
return source->black;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
EMSCRIPTEN_KEEPALIVE double *raw_vignette(Raw *source) {
|
|
85
|
+
return source->vignette;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
EMSCRIPTEN_KEEPALIVE double raw_white(Raw *source) {
|
|
89
|
+
return source->white;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Packed samples: LibRaw's processed image when one was made, else its raw buffer.
|
|
93
|
+
EMSCRIPTEN_KEEPALIVE void *raw_pixels(Raw *source) {
|
|
94
|
+
if (!source->prepared.empty()) {
|
|
95
|
+
return source->prepared.data();
|
|
96
|
+
}
|
|
97
|
+
return source->decoder.imgdata.image
|
|
98
|
+
? static_cast<void *>(source->decoder.imgdata.image)
|
|
99
|
+
: static_cast<void *>(source->decoder.imgdata.rawdata.raw_image);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Releases the decoded samples once copied out; calibration stays available.
|
|
103
|
+
EMSCRIPTEN_KEEPALIVE void raw_free_pixels(Raw *source) {
|
|
104
|
+
source->decoder.recycle();
|
|
105
|
+
std::vector<float>().swap(source->prepared);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
EMSCRIPTEN_KEEPALIVE double raw_temperature(Raw *source) {
|
|
109
|
+
return source->asShot.Temperature();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
EMSCRIPTEN_KEEPALIVE double raw_tint(Raw *source) {
|
|
113
|
+
return source->asShot.Tint();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
EMSCRIPTEN_KEEPALIVE void raw_close(Raw *source) {
|
|
117
|
+
delete source;
|
|
118
|
+
}
|
|
119
|
+
}
|