@bcts/lifehash-cli 1.0.0-alpha.17

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.
@@ -0,0 +1,101 @@
1
+ /**
2
+ * PNG writer for LifeHash images.
3
+ *
4
+ * Ported from bc-lifehash-cli C++ implementation (png-writer.hpp).
5
+ * Uses pngjs instead of libpng.
6
+ *
7
+ * @module
8
+ */
9
+
10
+ import { writeFileSync } from "fs";
11
+ import { PNG } from "pngjs";
12
+ import type { Image } from "@bcts/lifehash";
13
+
14
+ /**
15
+ * Writes a LifeHash image to a PNG file.
16
+ *
17
+ * Port of `write_image()` from lifehash.cpp lines 174-186 and
18
+ * PNGWriter class from png-writer.hpp.
19
+ *
20
+ * @param image - The LifeHash image to write
21
+ * @param filename - The output filename
22
+ * @category PNG Encoding
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { makeFromUtf8 } from "@bcts/lifehash";
27
+ *
28
+ * const image = makeFromUtf8("Hello");
29
+ * writeImage(image, "Hello.png");
30
+ * ```
31
+ */
32
+ export function writeImage(image: Image, filename: string): void {
33
+ const png = new PNG({
34
+ width: image.width,
35
+ height: image.height,
36
+ colorType: 2, // RGB
37
+ bitDepth: 8,
38
+ inputColorType: 2,
39
+ inputHasAlpha: false,
40
+ });
41
+
42
+ // Copy RGB data from image to PNG
43
+ // PNG expects RGBA, so we need to add alpha channel
44
+ for (let y = 0; y < image.height; y++) {
45
+ for (let x = 0; x < image.width; x++) {
46
+ const srcOffset = (y * image.width + x) * 3;
47
+ const dstOffset = (y * image.width + x) * 4;
48
+
49
+ // Copy RGB from source
50
+ png.data[dstOffset] = image.colors[srcOffset]; // R
51
+ png.data[dstOffset + 1] = image.colors[srcOffset + 1]; // G
52
+ png.data[dstOffset + 2] = image.colors[srcOffset + 2]; // B
53
+ png.data[dstOffset + 3] = 255; // A (fully opaque)
54
+ }
55
+ }
56
+
57
+ // Write to file
58
+ const buffer = PNG.sync.write(png);
59
+ writeFileSync(filename, buffer);
60
+ }
61
+
62
+ /**
63
+ * Generates a PNG buffer from a LifeHash image without writing to disk.
64
+ *
65
+ * @param image - The LifeHash image to encode
66
+ * @returns A Buffer containing the PNG data
67
+ * @category PNG Encoding
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * import { makeFromUtf8 } from "@bcts/lifehash";
72
+ *
73
+ * const image = makeFromUtf8("Hello");
74
+ * const pngBuffer = generatePNG(image);
75
+ * ```
76
+ */
77
+ export function generatePNG(image: Image): Buffer {
78
+ const png = new PNG({
79
+ width: image.width,
80
+ height: image.height,
81
+ colorType: 2,
82
+ bitDepth: 8,
83
+ inputColorType: 2,
84
+ inputHasAlpha: false,
85
+ });
86
+
87
+ // Copy RGB data from image to PNG
88
+ for (let y = 0; y < image.height; y++) {
89
+ for (let x = 0; x < image.width; x++) {
90
+ const srcOffset = (y * image.width + x) * 3;
91
+ const dstOffset = (y * image.width + x) * 4;
92
+
93
+ png.data[dstOffset] = image.colors[srcOffset];
94
+ png.data[dstOffset + 1] = image.colors[srcOffset + 1];
95
+ png.data[dstOffset + 2] = image.colors[srcOffset + 2];
96
+ png.data[dstOffset + 3] = 255;
97
+ }
98
+ }
99
+
100
+ return PNG.sync.write(png);
101
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Utility functions for the LifeHash CLI.
3
+ *
4
+ * Ported from bc-lifehash-cli C++ implementation.
5
+ *
6
+ * @module
7
+ */
8
+
9
+ /**
10
+ * Appends a path component to a path, handling trailing slashes correctly.
11
+ *
12
+ * Port of `appending_path_component()` from lifehash.cpp lines 18-24.
13
+ *
14
+ * @param path - The base path
15
+ * @param component - The component to append
16
+ * @returns The combined path
17
+ * @category Utilities
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * appendingPathComponent("", "file.png") // => "file.png"
22
+ * appendingPathComponent("/tmp/", "file.png") // => "/tmp/file.png"
23
+ * appendingPathComponent("/tmp", "file.png") // => "/tmp/file.png"
24
+ * ```
25
+ */
26
+ export function appendingPathComponent(path: string, component: string): string {
27
+ if (path === "") {
28
+ return component;
29
+ }
30
+ if (path.endsWith("/")) {
31
+ return `${path}${component}`;
32
+ }
33
+ return `${path}/${component}`;
34
+ }
35
+
36
+ /**
37
+ * Selects a random element from an array.
38
+ *
39
+ * Port of `random_element()` template from lifehash.cpp lines 38-50.
40
+ *
41
+ * @param array - The array to select from
42
+ * @returns A random element from the array
43
+ * @category Utilities
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * randomElement(["A", "B", "C"]) // => "A" or "B" or "C"
48
+ * ```
49
+ */
50
+ export function randomElement<T>(array: T[]): T {
51
+ const index = Math.floor(Math.random() * array.length);
52
+ return array[index];
53
+ }
54
+
55
+ /**
56
+ * Generates a random input string in "XXX-XXX" format where X is a random uppercase letter.
57
+ *
58
+ * Port of `make_random_input()` from lifehash.cpp lines 52-57.
59
+ *
60
+ * @returns A random string in "XXX-XXX" format
61
+ * @category Utilities
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * makeRandomInput() // => "ABC-DEF" (random letters)
66
+ * ```
67
+ */
68
+ export function makeRandomInput(): string {
69
+ const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
70
+
71
+ const letter = (): string => randomElement(letters);
72
+ const cluster = (): string => `${letter()}${letter()}${letter()}`;
73
+
74
+ return `${cluster()}-${cluster()}`;
75
+ }