mcap/tools/image_probe.jai
Musa Mahmood 811c096dd0 [0.0.8] Import: any folder of screenshots and photos into the library, a folder for each source
- Import window (and -import SOURCE DEST): every image under a folder (PNG, JPEG, HEIC, AVIF, WebP,
  GIF, BMP, TIFF, camera RAW) becomes an .mcap holding the file as it was, a preview turned by its
  EXIF orientation, and metadata.json and import.json Capture Text (date from EXIF, the file name or
  its time; file name as title; camera as app). .snagx files become the same .mcap SnagitConverter
  makes. Duplicates across the whole library are skipped by a hash index (.import-index); videos
  and sidecars are skipped; source files are only read.
- modules/Image_Decode: stb_image, then WIC for everything else; EXIF; orientation; previews.
  tools/image_probe.jai shows what it reads.
- modules/Snagx (and modules/jaison), copied from SnagitConverter.
- The library is a folder of folders: recursive scan, a folder filter, and a folder: search prefix.
- OCR, the viewer and ocr-bench decode any image format, turned the right way up.
- Rename D:\SnagitIndex to D:\McapIndex.
2026-10-09 08:15:12 +01:00

59 lines
2.8 KiB
Plaintext

// image-probe: what modules/Image_Decode makes of image files. For each FILE, its kind, EXIF and the
// decoded size; with -out FOLDER, the decoded image (turned the right way up) and its .mcap preview
// written there as PNGs, to look at.
//
// Build: jai first.jai - image_probe Run: bin/image-probe FILE... [-out FOLDER]
main :: () {
args := get_command_line_arguments();
files: [..] string;
out_folder: string;
i := 1;
while i < args.count {
if args[i] == "-out" && i + 1 < args.count { out_folder = args[i + 1]; i += 2; continue; }
array_add(*files, args[i]);
i += 1;
}
if !files.count { print("Usage: image-probe FILE... [-out FOLDER]\n"); exit(2); }
if out_folder make_directory_if_it_does_not_exist(out_folder, recursive = true);
failures := 0;
for path: files {
data, ok := read_entire_file(path, log_errors = false);
if !ok { print("%: cannot be read\n", path); failures += 1; continue; }
bytes := cast([] u8) data;
kind := image_kind_from_name(image_kind(bytes), path);
exif := exif_read(bytes, kind);
print("%\n kind %, % bytes\n", path, image_kind_name(kind), bytes.count);
if exif.found {
print(" EXIF: orientation %, date \"%\" %, make \"%\", model \"%\", software \"%\"\n", exif.orientation, exif.date_original, exif.offset_original, exif.make, exif.model, exif.software);
if exif.has_gps print(" GPS: %, %, % m\n", exif.latitude, exif.longitude, exif.altitude);
}
if kind == .HEIF || kind == .AVIF {
heif := heif_info(bytes);
print(" container: % x %, irot %, imir %, Exif item % bytes\n", heif.width, heif.height, heif.rotation, heif.mirror, heif.exif.count);
}
start := current_time_monotonic();
image, decoded, error := image_decode(bytes, kind);
if !decoded { print(" cannot decode: %\n", error); failures += 1; continue; }
print(" decoded: % x % in % ms (%)\n", image.width, image.height, FF(to_float64_seconds(current_time_monotonic() - start) * 1000, 1), ifx image.from_stb then "stb_image" else "WIC or turned");
if out_folder {
name := path_strip_extension(path_filename(path));
full := stbi_write_png(temp_c_string(tprint("%/%.png", out_folder, name)), image.width, image.height, 4, image.pixels, image.width * 4);
png, w, h, preview_ok, preview_error := image_preview_png(image, 320);
if preview_ok write_entire_file(tprint("%/%.preview.png", out_folder, name), png.data, png.count);
print(" wrote %/%.png%\n", out_folder, name, ifx preview_ok then tprint(" and a % x % preview", w, h) else tprint("; no preview: %", preview_error));
}
image_free(*image);
reset_temporary_storage();
}
exit(xx ifx failures then 1 else 0);
}
#import "Basic";
#import "String";
#import "File";
#import "Image_Decode";
#import "stb_image_write";