@ludicon/spark.js 0.0.6 → 0.0.7

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
@@ -62,7 +62,7 @@ To run local examples:
62
62
  npm run dev
63
63
  ```
64
64
 
65
- And visit `http://localhost:5174/examples/basic.html`.
65
+ This will open `http://localhost:5174/examples/index.thml` where you can browse the examples.
66
66
 
67
67
  > Note: Browsers treat http://localhost as a secure context, so HTTPS is not required when testing locally on the same machine. However, to access the dev server from another device you must enable HTTPS for WebGPU features to work.
68
68
  >
@@ -81,8 +81,8 @@ Load an image and encode it to a compressed GPU texture.
81
81
 
82
82
  #### Parameters
83
83
 
84
- - **`source`** (`GPUtexture | string | HTMLImageElement | HTMLCanvasElement | Blob | ArrayBuffer`)
85
- The image to encode. Can be a GPUtexture, URL, DOM image/canvas, binary buffer, or blob.
84
+ - **`source`** (`string | HTMLImageElement | ImageBitmap | GPUtexture`)
85
+ The image to encode. Can be a GPUTexture, URL, DOM image or ImageBitmap.
86
86
 
87
87
  - **`options`** *(optional object)*
88
88
  Configuration options for encoding:
@@ -118,6 +118,38 @@ Load an image and encode it to a compressed GPU texture.
118
118
  - `Promise<GPUTexture>` — the compressed GPU texture, ready for use in WebGPU.
119
119
 
120
120
 
121
+ ## Integration with three.js
122
+
123
+ Using spark.js with [three.js](https://threejs.org/) is straightforward. You can encode textures with Spark and expose them to three.js as external textures:
124
+
125
+ ```js
126
+ // Load and encode texture using spark:
127
+ const gpuTexture = await spark.encodeTexture(textureUrl, { srgb: true, flipY: true });
128
+
129
+ // Wrap the GPUTexture for three.js
130
+ const externalTex = new THREE.ExternalTexture(gpuTexture);
131
+
132
+ // Then use as any other texture:
133
+ const material = new THREE.MeshBasicMaterial({ map: externalTex });
134
+
135
+ ```
136
+
137
+ To facilitate the use of Spark when loading GLTF assets, import the provided helper:
138
+
139
+ ```jsd
140
+ import { registerSparkLoader } from "@ludicon/spark.js/three-gltf";
141
+ ```
142
+
143
+ Then register Spark with an existing GLTFLoader instance:
144
+
145
+ ```js
146
+ const loader = new GLTFLoader()
147
+ registerSparkLoader(loader, spark)
148
+ ```
149
+
150
+ After registration, the loader will automatically encode textures with Spark whenever applicable.
151
+
152
+
121
153
  ## License
122
154
 
123
155
  *spark.js* is free for non-commercial use.
package/dist/spark.esm.js CHANGED
@@ -412,17 +412,35 @@ function imageToByteArray(image) {
412
412
  const imageData = ctx.getImageData(0, 0, image.width, image.height);
413
413
  return new Uint8Array(imageData.data.buffer);
414
414
  }
415
- function loadImage(url) {
416
- return new Promise(function(resolve, reject) {
417
- const image = new Image();
418
- image.crossOrigin = "anonymous";
419
- image.onload = function() {
420
- resolve(image);
421
- };
422
- image.onerror = reject;
423
- image.src = url;
415
+ function isSvgUrl(url) {
416
+ return /\.svg(?:$|\?)/i.test(url) || /^data:image\/svg\+xml[,;]/i.test(url);
417
+ }
418
+ function loadImageElement(url) {
419
+ return new Promise((resolve, reject) => {
420
+ const img = new Image();
421
+ img.crossOrigin = "anonymous";
422
+ img.decoding = "async";
423
+ img.onload = () => resolve(img);
424
+ img.onerror = reject;
425
+ img.src = url;
426
+ });
427
+ }
428
+ async function loadImageBitmap(url, opts = {}) {
429
+ const res = await fetch(url, { mode: "cors" });
430
+ if (!res.ok) throw new Error(`HTTP ${res.status} for ${url}`);
431
+ const blob = await res.blob();
432
+ return createImageBitmap(blob, {
433
+ imageOrientation: opts.flipY ? "flipY" : "none",
434
+ colorSpaceConversion: opts.colorSpaceConversion ?? "none"
424
435
  });
425
436
  }
437
+ function loadImage(url) {
438
+ if (isSvgUrl(url)) {
439
+ return loadImageElement(url);
440
+ } else {
441
+ return loadImageBitmap(url);
442
+ }
443
+ }
426
444
  const BYTES_PER_ROW_ALIGNMENT = 256;
427
445
  const MIN_MIP_SIZE = 4;
428
446
  function computeMipmapLayout(w, h, blockSize, mipmaps) {
@@ -457,6 +475,7 @@ class Spark {
457
475
  #querySet;
458
476
  #queryBuffer;
459
477
  #queryReadbackBuffer;
478
+ #encodeCounter = 0;
460
479
  /**
461
480
  * Initialize the encoder by detecting available compression formats.
462
481
  * @param {GPUDevice} device - WebGPU device.
@@ -550,13 +569,13 @@ class Spark {
550
569
  * Try to determine the best compression options automatically. Do not use this in production, this is
551
570
  * for the convenience of the spark.js image viewer only.
552
571
  *
553
- * @param {string | HTMLImageElement | HTMLCanvasElement | Blob | ArrayBuffer | GPUTexture} source - Image input.
572
+ * @param {string | HTMLImageElement | ImageBitmap | GPUTexture} source - Image input.
554
573
  * @param {Object} options - Encoding options.
555
574
  * @returns {Object} - Recommended encoding options with an explicit encoding format.
556
575
  */
557
576
  async selectPreferredOptions(source, options = {}) {
558
577
  if (options.format == void 0 || options.format == "auto") {
559
- const image = source instanceof Image || source instanceof GPUTexture ? source : await loadImage(source);
578
+ const image = source instanceof Image || source instanceof ImageBitmap || source instanceof GPUTexture ? source : await loadImage(source);
560
579
  options.format = "auto";
561
580
  const format = await this.#getBestMatchingFormat(options, image);
562
581
  options.format = SparkFormatName[format];
@@ -572,8 +591,8 @@ class Spark {
572
591
  /**
573
592
  * Load an image and encode it to a compressed GPU texture.
574
593
  *
575
- * @param {GPUTexture | string | HTMLImageElement | HTMLCanvasElement | Blob | ArrayBuffer} source
576
- * The image to encode. Can be a GPUTexture, URL, DOM image/canvas, binary buffer, or Blob.
594
+ * @param {string | HTMLImageElement | ImageBitmap | GPUTexture} source
595
+ * The image to encode. Can be a GPUTexture, URL, DOM image or ImageBitmap.
577
596
  *
578
597
  * @param {Object} [options] - Optional configuration for encoding.
579
598
  *
@@ -610,7 +629,7 @@ class Spark {
610
629
  */
611
630
  async encodeTexture(source, options = {}) {
612
631
  assert(this.#device, "Spark is not initialized");
613
- const image = source instanceof Image || source instanceof GPUTexture ? source : await loadImage(source);
632
+ const image = source instanceof Image || source instanceof ImageBitmap || source instanceof GPUTexture ? source : await loadImage(source);
614
633
  console.log("Loaded image", image);
615
634
  const format = await this.#getBestMatchingFormat(options, image);
616
635
  const pipelinePromise = this.#loadPipeline(format);
@@ -622,7 +641,8 @@ class Spark {
622
641
  const srgb = (options.srgb || options.format?.endsWith("srgb")) && SparkFormatIsRGB[format];
623
642
  const webgpuFormat = SparkWebGPUFormats[format] + (srgb ? "-srgb" : "");
624
643
  const viewFormats = srgb ? ["rgba8unorm", "rgba8unorm-srgb"] : ["rgba8unorm"];
625
- console.time("create input texture");
644
+ const counter = this.#encodeCounter++;
645
+ console.time("create input texture #" + counter);
626
646
  let inputUsage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.STORAGE_BINDING;
627
647
  const needsProcessing = options.flipY || width != image.width || height != image.height;
628
648
  if (!needsProcessing && !(image instanceof GPUTexture)) {
@@ -678,7 +698,7 @@ class Spark {
678
698
  this.#generateMipmaps(commandEncoder, inputTexture, mipmapCount, width, height, srgb);
679
699
  }
680
700
  commandEncoder.popDebugGroup?.();
681
- console.timeEnd("create input texture");
701
+ console.timeEnd("create input texture #" + counter);
682
702
  const outputTexture = this.#device.createTexture({
683
703
  size: [width, height, 1],
684
704
  mipLevelCount: mipmapCount,
@@ -689,7 +709,7 @@ class Spark {
689
709
  size: outputSize,
690
710
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
691
711
  });
692
- console.time("dispatch compute shader");
712
+ console.time("dispatch compute shader #" + counter);
693
713
  commandEncoder.pushDebugGroup?.("spark encode texture");
694
714
  let args = {};
695
715
  if (this.#querySet && typeof commandEncoder.writeTimestamp !== "function") {
@@ -757,7 +777,7 @@ class Spark {
757
777
  }
758
778
  commandEncoder.popDebugGroup?.();
759
779
  this.#device.queue.submit([commandEncoder.finish()]);
760
- console.timeEnd("dispatch compute shader");
780
+ console.timeEnd("dispatch compute shader #" + counter);
761
781
  tmpTexture?.destroy();
762
782
  if (inputTexture != image) {
763
783
  inputTexture?.destroy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ludicon/spark.js",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Real-Time GPU Texture Codecs for the Web",
5
5
  "main": "dist/spark.esm.js",
6
6
  "module": "dist/spark.esm.js",
@@ -8,7 +8,8 @@
8
8
  "exports": {
9
9
  ".": {
10
10
  "import": "./dist/spark.esm.js"
11
- }
11
+ },
12
+ "./three-gltf": "./src/three-gltf.js"
12
13
  },
13
14
  "files": [
14
15
  "dist",
@@ -61,8 +62,17 @@
61
62
  "npm-run-all": "^4.1.5",
62
63
  "prettier": "^3.0.0",
63
64
  "rimraf": "^5.0.0",
65
+ "three": "^0.180.0",
64
66
  "vite": "^7.0.0"
65
67
  },
68
+ "peerDependencies": {
69
+ "three": "^0.180.0"
70
+ },
71
+ "peerDependenciesMeta": {
72
+ "three": {
73
+ "optional": true
74
+ }
75
+ },
66
76
  "publishConfig": {
67
77
  "access": "public"
68
78
  },