@bb-studio/ocio 0.0.2 → 0.0.4

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 CHANGED
@@ -12,15 +12,44 @@ The native module links against OpenColorIO C++ and runs the OCIO CPU processor
12
12
  npm install @bb-studio/ocio
13
13
  ```
14
14
 
15
- The npm package exposes the JavaScript API from `@bb-studio/ocio` and a direct wasm loader from `@bb-studio/ocio/wasm`.
15
+ The npm package exposes the JavaScript API from `@bb-studio/ocio`. `createOCIO()` selects the correct WebAssembly loader for the current runtime and resolves the bundled wasm file automatically.
16
16
 
17
- It ships three prebuilt Emscripten artifacts:
17
+ The package ships three internal Emscripten artifacts:
18
18
 
19
19
  - `dist/ocio-wasm.js`
20
20
  - `dist/ocio-wasm.node.js`
21
21
  - `dist/ocio-wasm.wasm`
22
22
 
23
- Node.js resolves `@bb-studio/ocio/wasm` to `dist/ocio-wasm.node.js`. Browsers, workers, and other runtimes resolve it to `dist/ocio-wasm.js`. Both wrappers load the same `dist/ocio-wasm.wasm` binary. The browser wrapper is built without Node.js runtime branches, so browser bundlers do not see Node.js built-in imports such as `node:module`.
23
+ Node.js uses `dist/ocio-wasm.node.js`. Browsers, workers, and other runtimes use `dist/ocio-wasm.js`. Both wrappers load the same `dist/ocio-wasm.wasm` binary. The browser wrapper is built without Node.js runtime branches, so browser bundlers do not see Node.js built-in imports such as `node:module`.
24
+
25
+ ## WASM URL Resolution
26
+
27
+ By default, `createOCIO()` resolves the bundled wasm file with the standard `new URL('../dist/ocio-wasm.wasm', import.meta.url)` pattern.
28
+
29
+ Vite apps can keep the same import and opt into the built-in Vite asset URL adapter with a resolver condition:
30
+
31
+ ```js
32
+ // vite.config.js
33
+ export default {
34
+ resolve: {
35
+ conditions: ['vite', 'module', 'browser', 'development|production']
36
+ }
37
+ };
38
+ ```
39
+
40
+ Then `createOCIO()` automatically uses the Vite-compatible wasm URL in dev and production builds.
41
+
42
+ If your app serves static assets from a CDN or public assets directory, pass the wasm URL directly:
43
+
44
+ ```js
45
+ import { createOCIO } from '@bb-studio/ocio';
46
+
47
+ const ocio = await createOCIO({
48
+ wasmUrl: '/assets/ocio-wasm.wasm'
49
+ });
50
+ ```
51
+
52
+ Advanced Emscripten users can pass `locateFile` for full control over every auxiliary file lookup.
24
53
 
25
54
  ## Usage
26
55
 
@@ -9,6 +9,10 @@ This package includes WebAssembly builds of OpenColorIO.
9
9
  - Website: https://opencolorio.org/
10
10
  - Source: https://github.com/AcademySoftwareFoundation/OpenColorIO
11
11
 
12
+ The bundled WebAssembly build applies a local WebGL GLSL ES compatibility
13
+ patch to OpenColorIO's fixed-function GPU shader generation. See
14
+ `patches/opencolorio/0001-webgl-glsl-es-float-literals.patch`.
15
+
12
16
  Copyright Contributors to the OpenColorIO Project.
13
17
 
14
18
  Redistribution and use in source and binary forms, with or without
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bb-studio/ocio",
3
- "version": "0.0.2",
4
- "description": "Real OpenColorIO 2.5 WebAssembly bindings for browser and Node.js.",
3
+ "version": "0.0.4",
4
+ "description": "OpenColorIO 2.5 WebAssembly bindings for browser and Node.js.",
5
5
  "type": "module",
6
6
  "types": "./src/ocio-js.d.ts",
7
7
  "imports": {
@@ -13,13 +13,9 @@
13
13
  "exports": {
14
14
  ".": {
15
15
  "types": "./src/ocio-js.d.ts",
16
+ "vite": "./src/index.vite.js",
16
17
  "default": "./src/index.js"
17
18
  },
18
- "./wasm": {
19
- "node": "./dist/ocio-wasm.node.js",
20
- "default": "./dist/ocio-wasm.js"
21
- },
22
- "./wasm.wasm": "./dist/ocio-wasm.wasm",
23
19
  "./package.json": "./package.json"
24
20
  },
25
21
  "scripts": {
@@ -53,10 +49,15 @@
53
49
  "dist/ocio-wasm.node.js",
54
50
  "dist/ocio-wasm.wasm",
55
51
  "src/index.js",
52
+ "src/index.vite.js",
53
+ "src/wasm-url.js",
54
+ "src/wasm-url.vite.js",
56
55
  "src/ocio-js.d.ts",
57
56
  "README.md",
58
57
  "LICENSE",
59
- "THIRD_PARTY_NOTICES.md"
58
+ "THIRD_PARTY_NOTICES.md",
59
+ "patches/opencolorio/0001-webgl-glsl-es-float-literals.patch",
60
+ "patches/opencolorio/README.md"
60
61
  ],
61
62
  "engines": {
62
63
  "node": ">=20"
@@ -0,0 +1,47 @@
1
+ diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp
2
+ index 0ead67f..cfcccd7 100644
3
+ --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp
4
+ +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp
5
+ @@ -559,18 +559,18 @@ std::string _Add_Reach_table(
6
+ ss.indent();
7
+
8
+ ss.newLine() << ss.floatDecl("i_base") << " = floor(h);";
9
+ - ss.newLine() << ss.floatDecl("i_lo") << " = i_base + " << table.base_index << ";";
10
+ - ss.newLine() << ss.floatDecl("i_hi") << " = i_lo + 1;";
11
+ + ss.newLine() << ss.floatDecl("i_lo") << " = i_base + float(" << table.base_index << ");";
12
+ + ss.newLine() << ss.floatDecl("i_hi") << " = i_lo + 1.0;";
13
+
14
+ if (dimensions == GpuShaderDesc::TEXTURE_1D)
15
+ {
16
+ - ss.newLine() << ss.floatDecl("lo") << " = " << ss.sampleTex1D(name, "(i_lo + 0.5) / " + std::to_string(table.total_size)) << ".r;";
17
+ - ss.newLine() << ss.floatDecl("hi") << " = " << ss.sampleTex1D(name, "(i_hi + 0.5) / " + std::to_string(table.total_size)) << ".r;";
18
+ + ss.newLine() << ss.floatDecl("lo") << " = " << ss.sampleTex1D(name, "(i_lo + 0.5) / " + std::to_string(table.total_size) + ".0") << ".r;";
19
+ + ss.newLine() << ss.floatDecl("hi") << " = " << ss.sampleTex1D(name, "(i_hi + 0.5) / " + std::to_string(table.total_size) + ".0") << ".r;";
20
+ }
21
+ else
22
+ {
23
+ - ss.newLine() << ss.floatDecl("lo") << " = " << ss.sampleTex2D(name, ss.float2Const("(i_lo + 0.5) / " + std::to_string(table.total_size), "0.0")) << ".r;";
24
+ - ss.newLine() << ss.floatDecl("hi") << " = " << ss.sampleTex2D(name, ss.float2Const("(i_hi + 0.5) / " + std::to_string(table.total_size), "0.5")) << ".r;";
25
+ + ss.newLine() << ss.floatDecl("lo") << " = " << ss.sampleTex2D(name, ss.float2Const("(i_lo + 0.5) / " + std::to_string(table.total_size) + ".0", "0.0")) << ".r;";
26
+ + ss.newLine() << ss.floatDecl("hi") << " = " << ss.sampleTex2D(name, ss.float2Const("(i_hi + 0.5) / " + std::to_string(table.total_size) + ".0", "0.5")) << ".r;";
27
+ }
28
+
29
+ ss.newLine() << ss.floatDecl("t") << " = h - i_base;"; // Hardcoded single degree spacing
30
+ @@ -892,13 +892,13 @@ std::string _Add_Cusp_table(
31
+
32
+ if (dimensions == GpuShaderDesc::TEXTURE_1D)
33
+ {
34
+ - ss.newLine() << ss.float3Decl("lo") << " = " << ss.sampleTex1D(name, std::string("(i_hi - 1 + 0.5) / ") + std::to_string(g.gamut_cusp_table.total_size)) << ".rgb;";
35
+ - ss.newLine() << ss.float3Decl("hi") << " = " << ss.sampleTex1D(name, std::string("(i_hi + 0.5) / ") + std::to_string(g.gamut_cusp_table.total_size)) << ".rgb;";
36
+ + ss.newLine() << ss.float3Decl("lo") << " = " << ss.sampleTex1D(name, std::string("(float(i_hi) - 1.0 + 0.5) / ") + std::to_string(g.gamut_cusp_table.total_size) + ".0") << ".rgb;";
37
+ + ss.newLine() << ss.float3Decl("hi") << " = " << ss.sampleTex1D(name, std::string("(float(i_hi) + 0.5) / ") + std::to_string(g.gamut_cusp_table.total_size) + ".0") << ".rgb;";
38
+ }
39
+ else
40
+ {
41
+ - ss.newLine() << ss.float3Decl("lo") << " = " << ss.sampleTex2D(name, ss.float2Const(std::string("(i_hi - 1 + 0.5) / ") + std::to_string(g.gamut_cusp_table.total_size), "0.5")) << ".rgb;";
42
+ - ss.newLine() << ss.float3Decl("hi") << " = " << ss.sampleTex2D(name, ss.float2Const(std::string("(i_hi + 0.5) / ") + std::to_string(g.gamut_cusp_table.total_size), "0.5")) << ".rgb;";
43
+ + ss.newLine() << ss.float3Decl("lo") << " = " << ss.sampleTex2D(name, ss.float2Const(std::string("(float(i_hi) - 1.0 + 0.5) / ") + std::to_string(g.gamut_cusp_table.total_size) + ".0", "0.5")) << ".rgb;";
44
+ + ss.newLine() << ss.float3Decl("hi") << " = " << ss.sampleTex2D(name, ss.float2Const(std::string("(float(i_hi) + 0.5) / ") + std::to_string(g.gamut_cusp_table.total_size) + ".0", "0.5")) << ".rgb;";
45
+ }
46
+
47
+ ss.newLine() << ss.floatDecl("t") << " = (h - " << hues_array_name << "[i_hi - 1]) / "
@@ -0,0 +1,11 @@
1
+ # OpenColorIO Patches
2
+
3
+ These patches are applied to `OCIO_SOURCE_DIR` by `scripts/build-wasm.sh` before building the WebAssembly OpenColorIO library.
4
+
5
+ ## `0001-webgl-glsl-es-float-literals.patch`
6
+
7
+ OpenColorIO 2.5 generates GLSL for ACES fixed-function gamut tables in `FixedFunctionOpGPU.cpp`. Some generated expressions mix integer and floating-point values, such as `i_base + 12` or `(i_hi + 0.5) / 360`.
8
+
9
+ WebGL GLSL ES compilers are stricter than desktop GLSL and may reject those implicit int/float conversions. This patch makes the generated shader code use explicit float casts and float literals, such as `i_base + float(12)` and `/ 360.0`.
10
+
11
+ Keep this patch small and remove it once the equivalent fix is available in the OpenColorIO version used by this package.
package/src/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ import OcioWasmModule from '#ocio-wasm';
2
+ import DEFAULT_WASM_URL from './wasm-url.js';
3
+
1
4
  export const ACES_CG_V2_CONFIG = 'ocio://cg-config-v2.2.0_aces-v1.3_ocio-v2.4';
2
5
  export const ACES_STUDIO_V2_CONFIG = 'ocio://studio-config-v2.2.0_aces-v1.3_ocio-v2.4';
3
6
  export const ACES_CG_V4_CONFIG = 'ocio://cg-config-v4.0.0_aces-v2.0_ocio-v2.5';
@@ -17,7 +20,6 @@ export const OptimizationFlags = Object.freeze({
17
20
  DRAFT: -1
18
21
  });
19
22
 
20
- const DEFAULT_MODULE_PATH = '#ocio-wasm';
21
23
  const DEFAULT_GPU_SHADER_FUNCTION = 'OCIODisplay';
22
24
  const DEFAULT_GPU_RESOURCE_PREFIX = 'ocio';
23
25
  const GPU_UNIFORM_TYPES = Object.freeze([
@@ -82,16 +84,17 @@ function toPositiveInteger(value, name) {
82
84
  }
83
85
 
84
86
  export async function createOCIO(options = {}) {
85
- const moduleFactory = options.moduleFactory ?? (await import(options.modulePath ?? DEFAULT_MODULE_PATH)).default;
87
+ const moduleFactory = options.moduleFactory ?? OcioWasmModule;
86
88
  const userLocateFile = options.locateFile;
89
+ const wasmUrl = options.wasmUrl == null ? DEFAULT_WASM_URL : String(options.wasmUrl);
87
90
  const moduleOptions = {
88
91
  ...options.moduleOptions,
89
92
  locateFile(path, prefix) {
90
93
  if (userLocateFile) {
91
94
  return userLocateFile(path, prefix);
92
95
  }
93
- if (path.endsWith('.wasm')) {
94
- return new URL(`../dist/${path}`, import.meta.url).href;
96
+ if (path === 'ocio-wasm.wasm') {
97
+ return wasmUrl;
95
98
  }
96
99
  return prefix + path;
97
100
  }
@@ -0,0 +1,11 @@
1
+ import { createOCIO as createDefaultOCIO } from './index.js';
2
+ import wasmUrl from './wasm-url.vite.js';
3
+
4
+ export * from './index.js';
5
+
6
+ export function createOCIO(options = {}) {
7
+ if (options.locateFile || options.wasmUrl != null) {
8
+ return createDefaultOCIO(options);
9
+ }
10
+ return createDefaultOCIO({ ...options, wasmUrl });
11
+ }
package/src/ocio-js.d.ts CHANGED
@@ -19,8 +19,8 @@ export const OptimizationFlags: Readonly<{
19
19
 
20
20
  export interface CreateOCIOOptions {
21
21
  moduleFactory?: (options?: unknown) => Promise<unknown>;
22
- modulePath?: string;
23
22
  moduleOptions?: Record<string, unknown>;
23
+ wasmUrl?: string;
24
24
  locateFile?: (path: string, prefix: string) => string;
25
25
  }
26
26
 
@@ -0,0 +1 @@
1
+ export default new URL('../dist/ocio-wasm.wasm', import.meta.url).href;
@@ -0,0 +1,3 @@
1
+ import wasmUrl from '../dist/ocio-wasm.wasm?url';
2
+
3
+ export default wasmUrl;