diff --git a/bin/Main.re b/bin/Main.re index 138e5d0b..486b8551 100644 --- a/bin/Main.re +++ b/bin/Main.re @@ -1,27 +1,49 @@ open Odiff.ImageIO; open Odiff.Diff; -let getIOModule = filename => +let getTypeFromFilename = filename => Filename.extension(filename) |> ( fun - | ".png" => ((module ODiffIO.Png.IO): (module ImageIO)) + | ".png" => `png | ".jpg" - | ".jpeg" => ((module ODiffIO.Jpg.IO): (module ImageIO)) - | ".bmp" => ((module ODiffIO.Bmp.IO): (module ImageIO)) - | ".tiff" => ((module ODiffIO.Tiff.IO): (module ImageIO)) + | ".jpeg" => `jpg + | ".bmp" => `bmp + | ".tiff" => `tiff | f => failwith("This format is not supported: " ++ f) ); +let getIOModule = + fun + | `png => ((module ODiffIO.Png.IO): (module ImageIO)) + | `jpg => ((module ODiffIO.Jpg.IO): (module ImageIO)) + | `bmp => ((module ODiffIO.Bmp.IO): (module ImageIO)) + | `tiff => ((module ODiffIO.Tiff.IO): (module ImageIO)); + type diffResult('output) = { exitCode: int, diff: option('output), }; +let readFromStdin = () => { + /* We use 65536 because that is the size of OCaml's IO buffers. */ + let chunk_size = 65536; + let buffer = Buffer.create(chunk_size); + let rec loop = () => { + Buffer.add_channel(buffer, stdin, chunk_size); + loop(); + }; + try(loop()) { + | End_of_file => Buffer.contents(buffer) + }; +}; + let main = ( - img1Path, - img2Path, + img1, + img2, + img1Type, + img2Type, diffPath, threshold, outputDiffMask, @@ -31,13 +53,54 @@ let main = antialiasing, ignoreRegions, ) => { - module IO1 = (val getIOModule(img1Path)); - module IO2 = (val getIOModule(img2Path)); + let img1Type = + switch (img1Type) { + | `auto when img1 == "_" => + failwith("--base-type has to be not auto, when using buffer as input") + | `auto => getTypeFromFilename(img1) + | `bmp => `bmp + | `jpg => `jpg + | `png => `png + | `tiff => `tiff + }; + + let img2Type = + switch (img2Type) { + | `auto when img2 == "_" => + failwith( + "--compare-type has to be not auto, when using buffer as input", + ) + | `auto => getTypeFromFilename(img2) + | `bmp => `bmp + | `jpg => `jpg + | `png => `png + | `tiff => `tiff + }; + + module IO1 = (val getIOModule(img1Type)); + module IO2 = (val getIOModule(img2Type)); module Diff = MakeDiff(IO1, IO2); - let img1 = IO1.loadImage(img1Path); - let img2 = IO2.loadImage(img2Path); + let img1 = + switch (img1) { + | "_" => + if (!stdoutParsableString) { + print_endline("Please provide the buffer for the base image:"); + }; + readFromStdin() |> IO1.loadImageFromBuffer; + | path => IO1.loadImageFromPath(path) + }; + + let img2 = + switch (img2) { + | "_" => + if (!stdoutParsableString) { + print_endline("Please provide the buffer for the compare image:"); + }; + readFromStdin() |> IO2.loadImageFromBuffer; + | path => IO2.loadImageFromPath(path) + }; let {diff, exitCode} = Diff.diff( diff --git a/bin/ODiffBin.re b/bin/ODiffBin.re index a3c17aa9..268e33f7 100644 --- a/bin/ODiffBin.re +++ b/bin/ODiffBin.re @@ -1,5 +1,21 @@ open Cmdliner; +let supported_formats = [ + ("jpg", `jpg), + ("png", `png), + ("bmp", `bmp), + ("tiff", `tiff), +]; + +let underscore_or = ((parse, print)) => { + let parse = s => + switch (s) { + | "_" => `Ok(s) + | s => parse(s) + }; + (parse, print); +}; + let diffPath = Arg.( value @@ -10,15 +26,55 @@ let diffPath = let base = Arg.( value - & pos(0, file, "") - & info([], ~docv="BASE", ~doc="Path to base image") + & pos(0, underscore_or(non_dir_file), "_") + & info( + [], + ~docv="BASE", + ~doc= + "Path to base image (or \"_\" when you want to provide the base image from stdin)", + ) + ); + +let baseType = + Arg.( + value + & opt(enum([("auto", `auto), ...supported_formats]), `auto) + & info( + ["base-type"], + ~docv="FORMAT", + ~doc= + Printf.sprintf( + "The type of the base image (required to be not auto when a buffer is used as input).\nSupported values are: auto,%s", + supported_formats |> List.map(fst) |> String.concat(","), + ), + ) ); let comp = Arg.( value - & pos(1, file, "") - & info([], ~docv="COMPARING", ~doc="Path to comparing image") + & pos(1, underscore_or(non_dir_file), "_") + & info( + [], + ~docv="COMPARING", + ~doc= + "Path to comparing image (or \"_\" when you want to provide the comparing image from stdin)", + ) + ); + +let compType = + Arg.( + value + & opt(enum([("auto", `auto), ...supported_formats]), `auto) + & info( + ["compare-type"], + ~docv="FORMAT", + ~doc= + Printf.sprintf( + "The type of the comparing image (required to be not auto when a buffer is used as input).\nSupported values are: auto,%s", + supported_formats |> List.map(fst) |> String.concat(","), + ), + ) ); let threshold = { @@ -113,7 +169,7 @@ let cmd = { let man = [ `S(Manpage.s_description), `P("$(tname) is the fastest pixel-by-pixel image comparison tool."), - `P("Supported image types: .png, .jpg, .jpeg, .bitmap"), + `P("Supported image types: .png, .jpg, .jpeg, .bmp, .tiff"), ]; ( @@ -121,6 +177,8 @@ let cmd = { const(Main.main) $ base $ comp + $ baseType + $ compType $ diffPath $ threshold $ diffMask diff --git a/bin/node-bindings/odiff.d.ts b/bin/node-bindings/odiff.d.ts index cc1b70b3..92ff55e7 100644 --- a/bin/node-bindings/odiff.d.ts +++ b/bin/node-bindings/odiff.d.ts @@ -1,4 +1,8 @@ export type ODiffOptions = Partial<{ + /** The image type of the base image. This has to be set to the corresponding image format when using a buffer as input */ + baseImageType?: 'filepath' | 'jpg' | 'png' | 'bmp' | 'tiff' = 'filepath'; + /** The image type of the compare image. This has to be set to the corresponding image format when using a buffer as input */ + compareImageType?: 'filepath' | 'jpg' | 'png' | 'bmp' | 'tiff' = 'filepath'; /** Color used to highlight different pixels in the output (in hex format e.g. #cd2cc9). */ diffColor: string; /** Output full diff image. */ @@ -21,16 +25,16 @@ export type ODiffOptions = Partial<{ }>; declare function compare( - basePath: string, - comparePath: string, + baseImage: string | Buffer, + compareImage: string | Buffer, diffPath: string, options?: ODiffOptions ): Promise< | { match: true } - | { match: false; reason: "layout-diff" } + | { match: false; reason: 'layout-diff' } | { match: false; - reason: "pixel-diff"; + reason: 'pixel-diff'; /** Amount of different pixels */ diffCount: number; /** Percentage of different pixels in the whole image */ @@ -38,7 +42,7 @@ declare function compare( } | { match: false; - reason: "file-not-exists"; + reason: 'file-not-exists'; /** Errored file path */ file: string; } diff --git a/bin/node-bindings/odiff.js b/bin/node-bindings/odiff.js index 7f75dfbe..53ccab2e 100644 --- a/bin/node-bindings/odiff.js +++ b/bin/node-bindings/odiff.js @@ -1,6 +1,7 @@ // @ts-check const path = require("path"); const { execFile } = require("child_process"); +var stream = require('stream'); function optionsToArgs(options) { let argArray = ["--parsable-stdout"]; @@ -26,6 +27,18 @@ function optionsToArgs(options) { const [option, value] = optionEntry; switch (option) { + case "baseImageType": + if(value !== "filepath") { + setArgWithValue("base-type", value); + } + break; + + case "compareImageType": + if(value !== "filepath") { + setArgWithValue("compare-type", value); + } + break; + case "failOnLayoutDiff": setFlag("fail-on-layout", value); break; @@ -90,7 +103,7 @@ function parsePixelDiffStdout(stdout) { const CMD_BIN_HELPER_MSG = "Usage: odiff [OPTION]... [BASE] [COMPARING] [DIFF]\nTry `odiff --help' for more information.\n"; -async function compare(basePath, comparePath, diffOutput, options = {}) { +async function compare(baseImage, compareImage, diffOutput, options = {}) { return new Promise((resolve, reject) => { let producedStdout, producedStdError; @@ -99,9 +112,23 @@ async function compare(basePath, comparePath, diffOutput, options = {}) { ? options.__binaryPath : path.join(__dirname, "bin", "odiff"); - execFile( + let baseImageArg = baseImage; + let baseImageIsBuffer = false; + if(options.baseImageType && options.baseImageType !== "filepath") { + baseImageArg = "_"; + baseImageIsBuffer = true; + } + + let compareImageArg = compareImage; + let compareImageIsBuffer = false; + if(options.compareImageType && options.compareImageType !== "filepath") { + compareImageArg = "_"; + compareImageIsBuffer = true; + } + + const cp = execFile( binaryPath, - [basePath, comparePath, diffOutput, ...optionsToArgs(options)], + [baseImageArg, compareImageArg, diffOutput, ...optionsToArgs(options)], (_, stdout, stderr) => { producedStdout = stdout; producedStdError = stderr; @@ -128,7 +155,7 @@ async function compare(basePath, comparePath, diffOutput, options = {}) { ).replace(CMD_BIN_HELPER_MSG, ""); const noFileOrDirectoryMatches = originalErrorMessage.match( - /no\n\s*`(.*)'\sfile or\n\s*directory/ + /no\n\s*`(.*)'\sfile/ ); if (options.noFailOnFsErrors && noFileOrDirectoryMatches[1]) { @@ -153,7 +180,24 @@ async function compare(basePath, comparePath, diffOutput, options = {}) { ); break; } - }); + }) + + if(baseImageIsBuffer) { + cp.stdin?.write(baseImage, 'binary'); + cp.stdin?.end(); + } + + if(compareImageIsBuffer) { + cp.stdin?.write(compareImage, 'binary'); + cp.stdin?.end(); + } + + // const stdinStream = new stream.Readable(); + // if(compareImageIsBuffer) { + // stdinStream.push(compareImage, 'binary'); + // stdinStream.push("\n"); + // stdinStream.pipe(cp?.stdin, { end: false }); + // } }); } diff --git a/io/bmp/Bmp.re b/io/bmp/Bmp.re index ff5371da..eee81bbd 100644 --- a/io/bmp/Bmp.re +++ b/io/bmp/Bmp.re @@ -5,12 +5,16 @@ type data = Array1.t(int32, int32_elt, c_layout); module IO: Odiff.ImageIO.ImageIO = { type t = data; - let loadImage = (filename): Odiff.ImageIO.img(t) => { + let loadImageFromPath = (filename): Odiff.ImageIO.img(t) => { let (width, height, data) = ReadBmp.load(filename); {width, height, image: data}; }; + let loadImageFromBuffer = (buffer): Odiff.ImageIO.img(t) => { + failwith("Not implemented"); + }; + [@inline] let readDirectPixel = (~x: int, ~y: int, img: Odiff.ImageIO.img(t)) => { let image: data = img.image; diff --git a/io/jpg/Jpg.re b/io/jpg/Jpg.re index 126f53a8..c8456b7b 100644 --- a/io/jpg/Jpg.re +++ b/io/jpg/Jpg.re @@ -9,7 +9,7 @@ module IO = { buffer, }; - let loadImage = (filename): Odiff.ImageIO.img(t) => { + let loadImageFromPath = (filename): Odiff.ImageIO.img(t) => { let (width, height, data, buffer) = ReadJpg.read_jpeg_image(filename); { @@ -22,6 +22,10 @@ module IO = { }; }; + let loadImageFromBuffer = (buffer): Odiff.ImageIO.img(t) => { + failwith("Not implemented"); + }; + let readDirectPixel = (~x, ~y, img: Odiff.ImageIO.img(t)) => { Array1.unsafe_get(img.image.data, y * img.width + x); }; diff --git a/io/png/Png.re b/io/png/Png.re index 52395b97..6ff735be 100644 --- a/io/png/Png.re +++ b/io/png/Png.re @@ -16,12 +16,19 @@ module IO: Odiff.ImageIO.ImageIO = { Array1.unsafe_set(image, y * img.width + x, color); }; - let loadImage = (filename): Odiff.ImageIO.img(t) => { + let loadImageFromPath = (filename): Odiff.ImageIO.img(t) => { let (width, height, data, _buffer) = ReadPng.read_png_image(filename); {width, height, image: data}; }; + let loadImageFromBuffer = (buffer): Odiff.ImageIO.img(t) => { + let (width, height, data, _buffer) = + ReadPng.read_png_buffer(buffer, String.length(buffer)); + + {width, height, image: data}; + }; + let saveImage = (img: Odiff.ImageIO.img(t), filename) => { WritePng.write_png_bigarray(filename, img.image, img.width, img.height); }; diff --git a/io/png/ReadPng.c b/io/png/ReadPng.c index fc509338..504ad385 100644 --- a/io/png/ReadPng.c +++ b/io/png/ReadPng.c @@ -90,3 +90,78 @@ read_png_file(value file) CAMLreturn(res); } + +CAMLprim value +read_png_buffer(value buffer, value length) +{ + CAMLparam2(buffer, length); + CAMLlocal2(res, ba); + + int result = 0; + spng_ctx *ctx = NULL; + unsigned char *out = NULL; + uint8_t *buf = (uint8_t *)String_val(buffer); + size_t buf_len = (size_t)Int_val(length); + + ctx = spng_ctx_new(0); + + if (ctx == NULL) + { + caml_failwith("spng_ctx_new() failed"); + spng_ctx_free(ctx); + } + + /* Ignore and don't calculate chunk CRC's */ + spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE); + + /* Set memory usage limits for storing standard and unknown chunks, + this is important when reading untrusted files! */ + size_t limit = 1024 * 1024 * 64; + spng_set_chunk_limits(ctx, limit, limit); + + /* Set source PNG Buffer */ + spng_set_png_buffer(ctx, buf, buf_len); + + struct spng_ihdr ihdr; + result = spng_get_ihdr(ctx, &ihdr); + + if (result) + { + caml_failwith("spng_get_ihdr() error!"); + spng_ctx_free(ctx); + } + + size_t out_size; + result = spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &out_size); + if (result) + { + spng_ctx_free(ctx); + }; + + out = malloc(out_size); + if (out == NULL) + { + spng_ctx_free(ctx); + free(out); + }; + + result = spng_decode_image(ctx, out, out_size, SPNG_FMT_RGBA8, 0); + if (result) + { + spng_ctx_free(ctx); + free(out); + caml_failwith(spng_strerror(result)); + } + + res = caml_alloc(4, 0); + ba = caml_ba_alloc_dims(CAML_BA_INT32 | CAML_BA_C_LAYOUT, 1, out, out_size); + + Store_field(res, 0, Val_int(ihdr.width)); + Store_field(res, 1, Val_int(ihdr.height)); + Store_field(res, 2, ba); + Store_field(res, 3, Val_bp(out)); + + spng_ctx_free(ctx); + + CAMLreturn(res); +} diff --git a/io/png/ReadPng.re b/io/png/ReadPng.re index 43a979a7..af89574e 100644 --- a/io/png/ReadPng.re +++ b/io/png/ReadPng.re @@ -7,3 +7,13 @@ external read_png_image: 'a, ) = "read_png_file"; + +external read_png_buffer: + (string, int) => + ( + int, + int, + Bigarray.Array1.t(int32, Bigarray.int32_elt, Bigarray.c_layout), + 'a, + ) = + "read_png_buffer"; diff --git a/io/tiff/Tiff.re b/io/tiff/Tiff.re index d4f29dbe..bdea0827 100644 --- a/io/tiff/Tiff.re +++ b/io/tiff/Tiff.re @@ -9,7 +9,7 @@ module IO: Odiff.ImageIO.ImageIO = { buffer, }; - let loadImage = (filename): Odiff.ImageIO.img(t) => { + let loadImageFromPath = (filename): Odiff.ImageIO.img(t) => { let (width, height, data, buffer) = ReadTiff.load(filename); { @@ -22,6 +22,10 @@ module IO: Odiff.ImageIO.ImageIO = { }; }; + let loadImageFromBuffer = (buffer): Odiff.ImageIO.img(t) => { + failwith("Not implemented"); + }; + let readDirectPixel = (~x: int, ~y: int, img: Odiff.ImageIO.img(t)) => { Array1.unsafe_get(img.image.data, y * img.width + x); }; diff --git a/package.json b/package.json index aa8a6298..13e3a8da 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "scripts": { "run": "esy x ODiffBin", "test": "esy x RunTests.exe", - "test-js": "esy ava", + "test-js": "esy ava --verbose", "test-ts": "esy tsc --noEmit test/node-bindings.test.ts", "format": "esy dune build @fmt --auto-promote", "doc": "esy dune build @doc", diff --git a/src/ImageIO.re b/src/ImageIO.re index 62ae1aa6..930323d9 100644 --- a/src/ImageIO.re +++ b/src/ImageIO.re @@ -9,7 +9,8 @@ exception ImageNotLoaded; module type ImageIO = { type t; - let loadImage: string => img(t); + let loadImageFromPath: string => img(t); + let loadImageFromBuffer: string => img(t); let makeSameAsLayout: img(t) => img(t); let readDirectPixel: (~x: int, ~y: int, img(t)) => Int32.t; let setImgColor: (~x: int, ~y: int, Int32.t, img(t)) => unit; diff --git a/test/TestUtils.re b/test/TestUtils.re new file mode 100644 index 00000000..11cbfa07 --- /dev/null +++ b/test/TestUtils.re @@ -0,0 +1,7 @@ +let getFileContents = filename => { + let ic = open_in_bin(filename); + let file_length = in_channel_length(ic); + let data = really_input_string(ic, file_length); + close_in(ic); + data; +}; diff --git a/test/Test_Core.re b/test/Test_Core.re index d59699de..20df12ac 100644 --- a/test/Test_Core.re +++ b/test/Test_Core.re @@ -7,8 +7,8 @@ describe("CORE: Antialiasing", ({test, _}) => { open Png.IO; test("does not count anti-aliased pixels as different", ({expect, _}) => { - let img1 = loadImage("test/test-images/aa/antialiasing-on.png"); - let img2 = loadImage("test/test-images/aa/antialiasing-off.png"); + let img1 = loadImageFromPath("test/test-images/aa/antialiasing-on.png"); + let img2 = loadImageFromPath("test/test-images/aa/antialiasing-off.png"); let (_, diffPixels, diffPercentage) = PNG_Diff.compare( @@ -24,8 +24,9 @@ describe("CORE: Antialiasing", ({test, _}) => { }); test("tests diffrent sized AA images", ({expect, _}) => { - let img1 = loadImage("test/test-images/aa/antialiasing-on.png"); - let img2 = loadImage("test/test-images/aa/antialiasing-off-small.png"); + let img1 = loadImageFromPath("test/test-images/aa/antialiasing-on.png"); + let img2 = + loadImageFromPath("test/test-images/aa/antialiasing-off-small.png"); let (_, diffPixels, diffPercentage) = PNG_Diff.compare( @@ -43,8 +44,9 @@ describe("CORE: Antialiasing", ({test, _}) => { describe("CORE: Threshold", ({test, _}) => { test("uses provided threshold", ({expect, _}) => { - let img1 = Png.IO.loadImage("test/test-images/png/orange.png"); - let img2 = Png.IO.loadImage("test/test-images/png/orange_changed.png"); + let img1 = Png.IO.loadImageFromPath("test/test-images/png/orange.png"); + let img2 = + Png.IO.loadImageFromPath("test/test-images/png/orange_changed.png"); let (_, diffPixels, diffPercentage) = PNG_Diff.compare(img1, img2, ~threshold=0.5, ()); @@ -55,8 +57,9 @@ describe("CORE: Threshold", ({test, _}) => { describe("CORE: Ignore Regions", ({test, _}) => { test("uses provided irgnore regions", ({expect, _}) => { - let img1 = Png.IO.loadImage("test/test-images/png/orange.png"); - let img2 = Png.IO.loadImage("test/test-images/png/orange_changed.png"); + let img1 = Png.IO.loadImageFromPath("test/test-images/png/orange.png"); + let img2 = + Png.IO.loadImageFromPath("test/test-images/png/orange_changed.png"); let (_diffOutput, diffPixels, diffPercentage) = PNG_Diff.compare( @@ -76,14 +79,15 @@ describe("CORE: Ignore Regions", ({test, _}) => { describe("CORE: Diff Color", ({test, _}) => { test("creates diff output image with custom diff color", ({expect, _}) => { - let img1 = Png.IO.loadImage("test/test-images/png/orange.png"); - let img2 = Png.IO.loadImage("test/test-images/png/orange_changed.png"); + let img1 = Png.IO.loadImageFromPath("test/test-images/png/orange.png"); + let img2 = + Png.IO.loadImageFromPath("test/test-images/png/orange_changed.png"); let (diffOutput, _, _) = PNG_Diff.compare(img1, img2, ~diffPixel=(0, 255, 0), ()); let originalDiff = - Png.IO.loadImage("test/test-images/png/orange_diff_green.png"); + Png.IO.loadImageFromPath("test/test-images/png/orange_diff_green.png"); let (diffMaskOfDiff, diffOfDiffPixels, diffOfDiffPercentage) = PNG_Diff.compare(originalDiff, diffOutput, ()); diff --git a/test/Test_IO_BMP.re b/test/Test_IO_BMP.re index 84eaf8a8..d580824c 100644 --- a/test/Test_IO_BMP.re +++ b/test/Test_IO_BMP.re @@ -6,8 +6,8 @@ module Output_Diff = Odiff.Diff.MakeDiff(Png.IO, Bmp.IO); describe("IO: BMP", ({test, _}) => { test("finds difference between 2 images", ({expect, _}) => { - let img1 = Bmp.IO.loadImage("test/test-images/bmp/clouds.bmp"); - let img2 = Bmp.IO.loadImage("test/test-images/bmp/clouds-2.bmp"); + let img1 = Bmp.IO.loadImageFromPath("test/test-images/bmp/clouds.bmp"); + let img2 = Bmp.IO.loadImageFromPath("test/test-images/bmp/clouds-2.bmp"); let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ()); @@ -16,14 +16,14 @@ describe("IO: BMP", ({test, _}) => { }); test("Diff of mask and no mask are equal", ({expect, _}) => { - let img1 = Bmp.IO.loadImage("test/test-images/bmp/clouds.bmp"); - let img2 = Bmp.IO.loadImage("test/test-images/bmp/clouds-2.bmp"); + let img1 = Bmp.IO.loadImageFromPath("test/test-images/bmp/clouds.bmp"); + let img2 = Bmp.IO.loadImageFromPath("test/test-images/bmp/clouds-2.bmp"); let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ~outputDiffMask=false, ()); - let img1 = Bmp.IO.loadImage("test/test-images/bmp/clouds.bmp"); - let img2 = Bmp.IO.loadImage("test/test-images/bmp/clouds-2.bmp"); + let img1 = Bmp.IO.loadImageFromPath("test/test-images/bmp/clouds.bmp"); + let img2 = Bmp.IO.loadImageFromPath("test/test-images/bmp/clouds-2.bmp"); let (_, diffPixelsMask, diffPercentageMask) = Diff.compare(img1, img2, ~outputDiffMask=true, ()); @@ -33,13 +33,13 @@ describe("IO: BMP", ({test, _}) => { }); test("Creates correct diff output image", ({expect, _}) => { - let img1 = Bmp.IO.loadImage("test/test-images/bmp/clouds.bmp"); - let img2 = Bmp.IO.loadImage("test/test-images/bmp/clouds-2.bmp"); + let img1 = Bmp.IO.loadImageFromPath("test/test-images/bmp/clouds.bmp"); + let img2 = Bmp.IO.loadImageFromPath("test/test-images/bmp/clouds-2.bmp"); let (diffOutput, _, _) = Diff.compare(img1, img2, ()); let originalDiff = - Png.IO.loadImage("test/test-images/bmp/clouds-diff.png"); + Png.IO.loadImageFromPath("test/test-images/bmp/clouds-diff.png"); let (diffMaskOfDiff, diffOfDiffPixels, diffOfDiffPercentage) = Output_Diff.compare(originalDiff, diffOutput, ()); @@ -54,4 +54,18 @@ describe("IO: BMP", ({test, _}) => { expect.int(diffOfDiffPixels).toBe(0); expect.float(diffOfDiffPercentage).toBeCloseTo(0.0); }); + + test("Can load images with a provided buffer", ({expect, _}) => { + let img1 = + TestUtils.getFileContents("test/test-images/bmp/clouds.bmp") + |> Bmp.IO.loadImageFromBuffer; + let img2 = + TestUtils.getFileContents("test/test-images/bmp/clouds-2.bmp") + |> Bmp.IO.loadImageFromBuffer; + + let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ()); + + expect.int(diffPixels).toBe(191); + expect.float(diffPercentage).toBeCloseTo(0.076); + }); }); diff --git a/test/Test_IO_JPG.re b/test/Test_IO_JPG.re index c6b7897c..b475f5b7 100644 --- a/test/Test_IO_JPG.re +++ b/test/Test_IO_JPG.re @@ -6,8 +6,8 @@ module Output_Diff = Odiff.Diff.MakeDiff(Png.IO, Jpg.IO); describe("IO: JPG / JPEG", ({test, _}) => { test("finds difference between 2 images", ({expect, _}) => { - let img1 = Jpg.IO.loadImage("test/test-images/jpg/tiger.jpg"); - let img2 = Jpg.IO.loadImage("test/test-images/jpg/tiger-2.jpg"); + let img1 = Jpg.IO.loadImageFromPath("test/test-images/jpg/tiger.jpg"); + let img2 = Jpg.IO.loadImageFromPath("test/test-images/jpg/tiger-2.jpg"); let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ()); @@ -16,14 +16,14 @@ describe("IO: JPG / JPEG", ({test, _}) => { }); test("Diff of mask and no mask are equal", ({expect, _}) => { - let img1 = Jpg.IO.loadImage("test/test-images/jpg/tiger.jpg"); - let img2 = Jpg.IO.loadImage("test/test-images/jpg/tiger-2.jpg"); + let img1 = Jpg.IO.loadImageFromPath("test/test-images/jpg/tiger.jpg"); + let img2 = Jpg.IO.loadImageFromPath("test/test-images/jpg/tiger-2.jpg"); let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ~outputDiffMask=false, ()); - let img1 = Jpg.IO.loadImage("test/test-images/jpg/tiger.jpg"); - let img2 = Jpg.IO.loadImage("test/test-images/jpg/tiger-2.jpg"); + let img1 = Jpg.IO.loadImageFromBuffer("test/test-images/jpg/tiger.jpg"); + let img2 = Jpg.IO.loadImageFromBuffer("test/test-images/jpg/tiger-2.jpg"); let (_, diffPixelsMask, diffPercentageMask) = Diff.compare(img1, img2, ~outputDiffMask=true, ()); @@ -33,13 +33,13 @@ describe("IO: JPG / JPEG", ({test, _}) => { }); test("Creates correct diff output image", ({expect, _}) => { - let img1 = Jpg.IO.loadImage("test/test-images/jpg/tiger.jpg"); - let img2 = Jpg.IO.loadImage("test/test-images/jpg/tiger-2.jpg"); + let img1 = Jpg.IO.loadImageFromPath("test/test-images/jpg/tiger.jpg"); + let img2 = Jpg.IO.loadImageFromPath("test/test-images/jpg/tiger-2.jpg"); let (diffOutput, _, _) = Diff.compare(img1, img2, ()); let originalDiff = - Png.IO.loadImage("test/test-images/jpg/tiger-diff.png"); + Png.IO.loadImageFromPath("test/test-images/jpg/tiger-diff.png"); let (diffMaskOfDiff, diffOfDiffPixels, diffOfDiffPercentage) = Output_Diff.compare(originalDiff, diffOutput, ()); @@ -54,4 +54,18 @@ describe("IO: JPG / JPEG", ({test, _}) => { expect.int(diffOfDiffPixels).toBe(0); expect.float(diffOfDiffPercentage).toBeCloseTo(0.0); }); + + test("Can load images with a provided buffer", ({expect, _}) => { + let img1 = + TestUtils.getFileContents("test/test-images/jpg/tiger.jpg") + |> Jpg.IO.loadImageFromBuffer; + let img2 = + TestUtils.getFileContents("test/test-images/jpg/tiger-2.jpg") + |> Jpg.IO.loadImageFromBuffer; + + let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ()); + + expect.int(diffPixels).toBe(7586); + expect.float(diffPercentage).toBeCloseTo(1.14); + }); }); diff --git a/test/Test_IO_PNG.re b/test/Test_IO_PNG.re index a6e2290e..a5036979 100644 --- a/test/Test_IO_PNG.re +++ b/test/Test_IO_PNG.re @@ -7,8 +7,8 @@ describe("IO: PNG", ({test, _}) => { open Png.IO; test("finds difference between 2 images", ({expect, _}) => { - let img1 = loadImage("test/test-images/png/orange.png"); - let img2 = loadImage("test/test-images/png/orange_changed.png"); + let img1 = loadImageFromPath("test/test-images/png/orange.png"); + let img2 = loadImageFromPath("test/test-images/png/orange_changed.png"); let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ()); @@ -17,14 +17,14 @@ describe("IO: PNG", ({test, _}) => { }); test("Diff of mask and no mask are equal", ({expect, _}) => { - let img1 = loadImage("test/test-images/png/orange.png"); - let img2 = loadImage("test/test-images/png/orange_changed.png"); + let img1 = loadImageFromPath("test/test-images/png/orange.png"); + let img2 = loadImageFromPath("test/test-images/png/orange_changed.png"); let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ~outputDiffMask=false, ()); - let img1 = loadImage("test/test-images/png/orange.png"); - let img2 = loadImage("test/test-images/png/orange_changed.png"); + let img1 = loadImageFromPath("test/test-images/png/orange.png"); + let img2 = loadImageFromPath("test/test-images/png/orange_changed.png"); let (_, diffPixelsMask, diffPercentageMask) = Diff.compare(img1, img2, ~outputDiffMask=true, ()); @@ -34,12 +34,13 @@ describe("IO: PNG", ({test, _}) => { }); test("Creates correct diff output image", ({expect, _}) => { - let img1 = loadImage("test/test-images/png/orange.png"); - let img2 = loadImage("test/test-images/png/orange_changed.png"); + let img1 = loadImageFromPath("test/test-images/png/orange.png"); + let img2 = loadImageFromPath("test/test-images/png/orange_changed.png"); let (diffOutput, _, _) = Diff.compare(img1, img2, ()); - let originalDiff = loadImage("test/test-images/png/orange_diff.png"); + let originalDiff = + loadImageFromPath("test/test-images/png/orange_diff.png"); let (diffMaskOfDiff, diffOfDiffPixels, diffOfDiffPercentage) = Diff.compare(originalDiff, diffOutput, ()); @@ -51,4 +52,18 @@ describe("IO: PNG", ({test, _}) => { expect.int(diffOfDiffPixels).toBe(0); expect.float(diffOfDiffPercentage).toBeCloseTo(0.0); }); + + test("Can load images with a provided buffer", ({expect, _}) => { + let img1 = + TestUtils.getFileContents("test/test-images/png/orange.png") + |> loadImageFromBuffer; + let img2 = + TestUtils.getFileContents("test/test-images/png/orange_changed.png") + |> loadImageFromBuffer; + + let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ()); + + expect.int(diffPixels).toBe(1430); + expect.float(diffPercentage).toBeCloseTo(1.20); + }); }); diff --git a/test/Test_IO_TIFF.re b/test/Test_IO_TIFF.re index 8308d343..d79766d0 100644 --- a/test/Test_IO_TIFF.re +++ b/test/Test_IO_TIFF.re @@ -6,8 +6,10 @@ module Output_Diff = Odiff.Diff.MakeDiff(Png.IO, Tiff.IO); describe("IO: TIFF", ({test, _}) => { test("finds difference between 2 images", ({expect, _}) => { - let img1 = Tiff.IO.loadImage("test/test-images/tiff/laptops.tiff"); - let img2 = Tiff.IO.loadImage("test/test-images/tiff/laptops-2.tiff"); + let img1 = + Tiff.IO.loadImageFromPath("test/test-images/tiff/laptops.tiff"); + let img2 = + Tiff.IO.loadImageFromPath("test/test-images/tiff/laptops-2.tiff"); let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ()); @@ -16,14 +18,18 @@ describe("IO: TIFF", ({test, _}) => { }); test("Diff of mask and no mask are equal", ({expect, _}) => { - let img1 = Tiff.IO.loadImage("test/test-images/tiff/laptops.tiff"); - let img2 = Tiff.IO.loadImage("test/test-images/tiff/laptops-2.tiff"); + let img1 = + Tiff.IO.loadImageFromPath("test/test-images/tiff/laptops.tiff"); + let img2 = + Tiff.IO.loadImageFromPath("test/test-images/tiff/laptops-2.tiff"); let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ~outputDiffMask=false, ()); - let img1 = Tiff.IO.loadImage("test/test-images/tiff/laptops.tiff"); - let img2 = Tiff.IO.loadImage("test/test-images/tiff/laptops-2.tiff"); + let img1 = + Tiff.IO.loadImageFromPath("test/test-images/tiff/laptops.tiff"); + let img2 = + Tiff.IO.loadImageFromPath("test/test-images/tiff/laptops-2.tiff"); let (_, diffPixelsMask, diffPercentageMask) = Diff.compare(img1, img2, ~outputDiffMask=true, ()); @@ -33,13 +39,15 @@ describe("IO: TIFF", ({test, _}) => { }); test("Creates correct diff output image", ({expect, _}) => { - let img1 = Tiff.IO.loadImage("test/test-images/tiff/laptops.tiff"); - let img2 = Tiff.IO.loadImage("test/test-images/tiff/laptops-2.tiff"); + let img1 = + Tiff.IO.loadImageFromPath("test/test-images/tiff/laptops.tiff"); + let img2 = + Tiff.IO.loadImageFromPath("test/test-images/tiff/laptops-2.tiff"); let (diffOutput, _, _) = Diff.compare(img1, img2, ()); let originalDiff = - Png.IO.loadImage("test/test-images/tiff/laptops-diff.png"); + Png.IO.loadImageFromPath("test/test-images/tiff/laptops-diff.png"); let (diffMaskOfDiff, diffOfDiffPixels, diffOfDiffPercentage) = Output_Diff.compare(originalDiff, diffOutput, ()); @@ -54,4 +62,18 @@ describe("IO: TIFF", ({test, _}) => { expect.int(diffOfDiffPixels).toBe(0); expect.float(diffOfDiffPercentage).toBeCloseTo(0.0); }); + + test("Can load images with a provided buffer", ({expect, _}) => { + let img1 = + TestUtils.getFileContents("test/test-images/tiff/laptops.tiff") + |> Tiff.IO.loadImageFromBuffer; + let img2 = + TestUtils.getFileContents("test/test-images/tiff/laptops-2.tiff") + |> Tiff.IO.loadImageFromBuffer; + + let (_, diffPixels, diffPercentage) = Diff.compare(img1, img2, ()); + + expect.int(diffPixels).toBe(8569); + expect.float(diffPercentage).toBeCloseTo(3.79); + }); }); diff --git a/test/node-binding.test.cjs b/test/node-binding.test.cjs index 5fec027c..57d749da 100644 --- a/test/node-binding.test.cjs +++ b/test/node-binding.test.cjs @@ -1,6 +1,7 @@ const path = require("path"); const test = require("ava"); const { compare } = require("../bin/node-bindings/odiff"); +const { readFile } = require("fs/promises"); const IMAGES_PATH = path.resolve(__dirname, "..", "images"); const BINARY_PATH = path.resolve( @@ -133,3 +134,39 @@ test("Returns meaningful error if file does not exist and noFailOnFsErrors", asy t.is(reason, "file-not-exists"); t.is(file, path.join(IMAGES_PATH, "not-existing.png")); }); + +test("Accepts buffer as input for compare image", async (t) => { + const buffer = await readFile(path.join(IMAGES_PATH, "donkey-2.png"), { encoding: "binary" }); + const { reason, diffCount, diffPercentage } = await compare( + path.join(IMAGES_PATH, "donkey.png"), + buffer, + path.join(IMAGES_PATH, "diff.png"), + { + __binaryPath: BINARY_PATH, + compareImageType: "png" + } + ); + + t.is(reason, "pixel-diff"); + t.is(diffCount, 109861); + t.is(diffPercentage, 2.85952484323); +}); + +test.skip("Accepts buffer as input for base and compare image at the same time", async (t) => { + const buffer1 = await readFile(path.join(IMAGES_PATH, "donkey.png"), { encoding: "binary" }); + const buffer2 = await readFile(path.join(IMAGES_PATH, "donkey-2.png"), { encoding: "binary" }); + const { reason, diffCount, diffPercentage } = await compare( + buffer1, + buffer2, + path.join(IMAGES_PATH, "diff.png"), + { + baseImageType: "png", + compareImageType: "png", + __binaryPath: BINARY_PATH, + } + ); + + t.is(reason, "pixel-diff"); + t.is(diffCount, 109861); + t.is(diffPercentage, 2.85952484323); +});