// image-probe: what modules/Image_Decode makes of image and video 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 { // A video: what video_poster finds, and its poster frame. head: [256] u8; head_count := 0; file, opened := file_open(path, log_errors = false); if opened { _, head_count = file_read(file, head.data, head.count); file_close(*file); } head_view := array_view(head, 0, head_count); if image_kind(head_view) == .VIDEO || array_find(string.["mts", "m2ts", "ts"], to_lower_copy(path_extension(path),, temp)) { start := current_time_monotonic(); image, info, decoded, error := video_poster(path); print("%\n video, %\n", path, video_format_name(head_view, path)); if !decoded { print(" cannot decode: %\n", error); failures += 1; continue; } print(" % x % as shown, % s, % fps, %, rotation %, audio %; poster at % s, in % ms\n", info.width, info.height, FF(info.duration, 2), FF(info.frame_rate, 2), info.codec, info.rotation, info.has_audio, FF(info.poster_time, 2), FF(to_float64_seconds(current_time_monotonic() - start) * 1000, 1)); print(" creationdate \"%\", creation_time \"%\", make \"%\", model \"%\", software \"%\", location \"%\"\n", info.creation_date, info.creation_time, info.make, info.model, info.software, info.location); if out_folder { name := path_strip_extension(path_filename(path)); stbi_write_png(temp_c_string(tprint("%/%.poster.png", out_folder, name)), image.width, image.height, 4, image.pixels, image.width * 4); print(" wrote %/%.poster.png\n", out_folder, name); } image_free(*image); reset_temporary_storage(); continue; } 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 kind == .GIF { frames, duration_ms := gif_info(bytes); print(" GIF: % frames, % ms\n", frames, duration_ms); } 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, ImageIO 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";