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/native/raw.h ADDED
@@ -0,0 +1,47 @@
1
+ #pragma once
2
+
3
+ #include "dng_color_spec.h"
4
+ #include "dng_host.h"
5
+ #include "dng_info.h"
6
+ #include "dng_negative.h"
7
+ #include "dng_temperature.h"
8
+ #include <libraw/libraw.h>
9
+ #include <memory>
10
+ #include <vector>
11
+
12
+ // One open RAW file: LibRaw decodes the samples, the DNG SDK owns the color calibration.
13
+ struct Raw {
14
+ dng_host host;
15
+ LibRaw decoder;
16
+ std::unique_ptr<dng_info> info; // Parsed once for DNG files, null otherwise.
17
+ AutoPtr<dng_negative> negative;
18
+ std::unique_ptr<dng_color_spec> color;
19
+ dng_temperature asShot;
20
+ dng_xy_coord asShotXY;
21
+
22
+ // Sensor layout, mirrored by RawMetadata in src/types.ts.
23
+ unsigned width, height, flip, mosaic;
24
+ bool cpuPrepared = false;
25
+ bool sensorMosaic = false;
26
+ bool daylightBalance = false;
27
+ std::vector<float> prepared; // SDK-normalized camera RGB, with HDR headroom.
28
+ unsigned originalOffset = 0; // Byte offset of a usable mosaic inside the file, or zero.
29
+ unsigned cfa[36] = {};
30
+ unsigned cfaSize = 2;
31
+ double black[4] = {};
32
+ double white;
33
+ double vignette[9] = {};
34
+
35
+ double calibration[12] = {}; // Three gains, then a row-major 3x3 matrix.
36
+ unsigned opcodes[3] = {}; // DNG opcode counts per list.
37
+
38
+ Raw() {
39
+ decoder.set_dng_host(&host);
40
+ }
41
+ };
42
+
43
+ void prepare_pixels(Raw *source, void *bytes, unsigned length);
44
+ void pack_pixels(Raw *source);
45
+ void prepare_dng_pixels(Raw *source, void *bytes, unsigned length);
46
+ void prepare_calibration(Raw *source, void *bytes, unsigned length);
47
+ double *calibrate(Raw *source, double temperature, double tint);
@@ -0,0 +1,164 @@
1
+ // C entry points called from src/decode/tiff-worker.ts; see src/decode/libraw.d.ts.
2
+
3
+ #include "direct.h"
4
+ #include "dng_host.h"
5
+ #include "dng_ifd.h"
6
+ #include "dng_info.h"
7
+ #include "dng_simple_image.h"
8
+ #include "dng_stream.h"
9
+ #include "dng_tag_codes.h"
10
+ #include "dng_tag_types.h"
11
+ #include <emscripten/emscripten.h>
12
+ #include <memory>
13
+ #include <stdexcept>
14
+ #include <string>
15
+ #include <vector>
16
+
17
+ // Plain TIFF parsing with two additions: reject samples the SDK would silently clamp, and keep
18
+ // the embedded ICC profile.
19
+ class TiffInfo : public dng_info {
20
+ public:
21
+ std::vector<uint8> profile;
22
+
23
+ protected:
24
+ void ParseTag(dng_host &host, dng_stream &stream, dng_exif *exif, dng_shared *shared,
25
+ dng_ifd *ifd, uint32 parent, uint32 tag, uint32 type, uint32 count, uint64 offset,
26
+ int64 delta) override {
27
+ if (parent == 0 && tag == tcBitsPerSample) {
28
+ const auto position = stream.Position();
29
+ for (uint32 i = 0; i < count; ++i) {
30
+ if (stream.TagValue_uint32(type) > 32) {
31
+ throw std::runtime_error("TIFF samples wider than 32 bits are not supported");
32
+ }
33
+ }
34
+ stream.SetReadPosition(position);
35
+ }
36
+ if (parent == 0 && tag == tcICCProfile) {
37
+ if (count > stream.Length() || offset > stream.Length() - count) {
38
+ throw std::runtime_error("Invalid TIFF color profile");
39
+ }
40
+ profile.resize(count);
41
+ stream.SetReadPosition(offset);
42
+ stream.Get(profile.data(), count);
43
+ return;
44
+ }
45
+ dng_info::ParseTag(host, stream, exif, shared, ifd, parent, tag, type, count, offset,
46
+ delta);
47
+ }
48
+ };
49
+
50
+ struct Tiff {
51
+ std::unique_ptr<dng_simple_image> image;
52
+ dng_pixel_buffer pixels; // Points into `image`, or into the file bytes for the direct path.
53
+ std::vector<uint8> profile;
54
+ // Mirrored by TiffPixels.metadata in src/tiff/upload.ts: width, height, channels, bytes per
55
+ // sample, bits per sample (zero for float), orientation, photometric, extra-sample kind,
56
+ // row bytes.
57
+ uint32 metadata[9];
58
+ uint32 originalOffset = 0;
59
+ bool bigEndian = false;
60
+ };
61
+
62
+ static std::string tiffError;
63
+
64
+ extern "C" {
65
+
66
+ EMSCRIPTEN_KEEPALIVE Tiff *tiff_open(void *bytes, unsigned length) {
67
+ try {
68
+ dng_host host;
69
+ dng_stream stream(bytes, length);
70
+ TiffInfo info;
71
+ info.Parse(host, stream);
72
+ if (info.fIFD.empty()) {
73
+ throw std::runtime_error("TIFF contains no image");
74
+ }
75
+ auto &ifd = *info.fIFD[0];
76
+ ifd.PostParse();
77
+
78
+ const unsigned colors = ifd.fPhotometricInterpretation == 2 ? 3 : 1;
79
+ if (ifd.fPhotometricInterpretation > 2 || ifd.fSamplesPerPixel < colors ||
80
+ ifd.fSamplesPerPixel > colors + 1 || !ifd.CanRead()) {
81
+ throw std::runtime_error("Unsupported TIFF pixel layout or compression");
82
+ }
83
+ const auto type = ifd.PixelType();
84
+ if (type != ttByte && type != ttShort && type != ttLong && type != ttFloat) {
85
+ throw std::runtime_error("Unsupported TIFF sample type");
86
+ }
87
+ if (ifd.fSampleFormat[0] != 1 && ifd.fSampleFormat[0] != 3) {
88
+ throw std::runtime_error("Unsupported TIFF sample format");
89
+ }
90
+
91
+ // Samples that already fill their storage type can be read from the file bytes directly;
92
+ // anything else, such as packed 12-bit or compressed data, goes through the SDK.
93
+ auto result = std::make_unique<Tiff>();
94
+ result->originalOffset =
95
+ ifd.fBitsPerSample[0] == TagTypeSize(type) * 8 ? uncompressed_offset(ifd, stream) : 0;
96
+ result->bigEndian = info.fBigEndian;
97
+ if (result->originalOffset) {
98
+ result->pixels.fData = static_cast<uint8 *>(bytes) + result->originalOffset;
99
+ result->pixels.fRowStep = ifd.fImageWidth * ifd.fSamplesPerPixel;
100
+ } else {
101
+ result->image = std::make_unique<dng_simple_image>(
102
+ dng_rect(ifd.fImageLength, ifd.fImageWidth), ifd.fSamplesPerPixel, type);
103
+ ifd.ReadImage(host, stream, *result->image);
104
+ result->image->GetPixelBuffer(result->pixels);
105
+ }
106
+ result->profile = std::move(info.profile);
107
+
108
+ const unsigned sampleBytes = TagTypeSize(type);
109
+ const uint32 metadata[] = {ifd.fImageWidth,
110
+ ifd.fImageLength,
111
+ ifd.fSamplesPerPixel,
112
+ sampleBytes,
113
+ ifd.fBitsPerSample[0],
114
+ ifd.fOrientation,
115
+ ifd.fPhotometricInterpretation,
116
+ ifd.fExtraSamplesCount ? ifd.fExtraSamples[0] : 0,
117
+ uint32(result->pixels.fRowStep) * sampleBytes};
118
+ std::copy(std::begin(metadata), std::end(metadata), result->metadata);
119
+ // The sample type is encoded separately from integer bit depth.
120
+ if (type == ttFloat) {
121
+ result->metadata[4] = 0;
122
+ }
123
+ return result.release();
124
+ } catch (const std::exception &exception) {
125
+ tiffError = exception.what();
126
+ } catch (...) {
127
+ tiffError = "TIFF decoding failed";
128
+ }
129
+ return nullptr;
130
+ }
131
+
132
+ EMSCRIPTEN_KEEPALIVE const char *tiff_error() {
133
+ return tiffError.c_str();
134
+ }
135
+
136
+ EMSCRIPTEN_KEEPALIVE uint32 *tiff_metadata(Tiff *source) {
137
+ return source->metadata;
138
+ }
139
+
140
+ EMSCRIPTEN_KEEPALIVE unsigned tiff_original_offset(Tiff *source) {
141
+ return source->originalOffset;
142
+ }
143
+
144
+ // SDK-decoded samples are already native-endian; only direct file bytes keep the file's order.
145
+ EMSCRIPTEN_KEEPALIVE unsigned tiff_big_endian(Tiff *source) {
146
+ return source->originalOffset && source->bigEndian;
147
+ }
148
+
149
+ EMSCRIPTEN_KEEPALIVE void *tiff_pixels(Tiff *source) {
150
+ return source->pixels.fData;
151
+ }
152
+
153
+ EMSCRIPTEN_KEEPALIVE void *tiff_profile(Tiff *source) {
154
+ return source->profile.data();
155
+ }
156
+
157
+ EMSCRIPTEN_KEEPALIVE unsigned tiff_profile_size(Tiff *source) {
158
+ return source->profile.size();
159
+ }
160
+
161
+ EMSCRIPTEN_KEEPALIVE void tiff_close(Tiff *source) {
162
+ delete source;
163
+ }
164
+ }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "raw-webgpu",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Camera RAW, DNG and TIFF decoding with WebGPU development and interactive white balance.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "native",
18
+ "README.md",
19
+ "NOTICE",
20
+ "LICENSE",
21
+ "THIRD_PARTY_LICENSES.txt",
22
+ "docs"
23
+ ],
24
+ "scripts": {
25
+ "build": "bun run build:native && bun run build:sdk",
26
+ "build:native": "python3 native/build.py",
27
+ "check": "biome check --write src build.ts tests",
28
+ "test": "bun test",
29
+ "test:gpu": "GPU=1 bun test",
30
+ "test:browser": "bun tests/browser.ts",
31
+ "build:sdk": "bun build.ts && tsc --emitDeclarationOnly",
32
+ "test:tiff": "bun tests/tiff.browser.ts",
33
+ "benchmark:tiff": "bun tests/tiff.browser.ts --benchmark",
34
+ "prepack": "bun run build",
35
+ "test:package": "bun tests/package.ts",
36
+ "prepublishOnly": "bunx --no-install biome ci src build.ts tests && bun run test:gpu && bun run test:package"
37
+ },
38
+ "devDependencies": {
39
+ "@biomejs/biome": "2.5.11",
40
+ "@playwright/test": "1.62.1",
41
+ "@types/bun": "^1.4.1",
42
+ "typescript": "^7.0.2",
43
+ "vgpu": "^0.3.1",
44
+ "@webgpu/types": "^0.1.72"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public",
48
+ "tag": "alpha",
49
+ "registry": "https://registry.npmjs.org/"
50
+ },
51
+ "keywords": [
52
+ "raw",
53
+ "dng",
54
+ "tiff",
55
+ "webgpu",
56
+ "libraw",
57
+ "wasm",
58
+ "image"
59
+ ],
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "git+https://github.com/roprgm/raw-webgpu.git"
63
+ },
64
+ "homepage": "https://github.com/roprgm/raw-webgpu#readme",
65
+ "bugs": {
66
+ "url": "https://github.com/roprgm/raw-webgpu/issues"
67
+ }
68
+ }