diff --git a/.gitignore b/.gitignore index 32dc11f9..8b76fc71 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ _release *.byte *.native *.install -images/diff.png \ No newline at end of file +images/diff.png +test/test-images/_*.png + diff --git a/images/extreme-alpha-1.png b/images/extreme-alpha-1.png new file mode 100644 index 00000000..7994e894 Binary files /dev/null and b/images/extreme-alpha-1.png differ diff --git a/images/extreme-alpha.png b/images/extreme-alpha.png new file mode 100644 index 00000000..ec12bddb Binary files /dev/null and b/images/extreme-alpha.png differ diff --git a/images/www.cypress-diff.png b/images/www.cypress-diff.png index 5ec21163..0c8ebe21 100644 Binary files a/images/www.cypress-diff.png and b/images/www.cypress-diff.png differ diff --git a/io/png/ReadPng.c b/io/png/ReadPng.c index 661e2922..2b25f812 100644 --- a/io/png/ReadPng.c +++ b/io/png/ReadPng.c @@ -63,7 +63,8 @@ CAMLprim value read_png_file(value file) { free(out); }; - result = spng_decode_image(ctx, out, out_size, SPNG_FMT_RGBA8, 0); + result = + spng_decode_image(ctx, out, out_size, SPNG_FMT_RGBA8, SPNG_DECODE_TRNS); if (result) { spng_ctx_free(ctx); free(out); diff --git a/src/ColorDelta.ml b/src/ColorDelta.ml index 3482982c..3e544c50 100644 --- a/src/ColorDelta.ml +++ b/src/ColorDelta.ml @@ -1,9 +1,21 @@ -let blend color alpha = 255. +. ((color -. 255.) *. alpha) +let blend_channel_white color alpha = 255. +. ((color -. 255.) *. alpha) +let white_pixel = (255., 255., 255., 0.) let blendSemiTransparentColor = function + | r, g, b, 0. -> white_pixel + | r, g, b, 255. -> (r, g, b, 1.) | r, g, b, alpha when alpha < 255. -> - (blend r alpha, blend g alpha, blend b alpha, alpha /. 255.) - | colors -> colors + let normalizedAlpha = alpha /. 255. in + let r, g, b, a = + ( blend_channel_white r normalizedAlpha, + blend_channel_white g normalizedAlpha, + blend_channel_white b normalizedAlpha, + normalizedAlpha ) + in + (r, g, b, a) + | _ -> + failwith + "Found pixel with alpha value greater than uint8 max value. Aborting." let convertPixelToFloat pixel = let pixel = pixel |> Int32.to_int in @@ -11,6 +23,7 @@ let convertPixelToFloat pixel = let b = (pixel lsr 16) land 255 in let g = (pixel lsr 8) land 255 in let r = pixel land 255 in + (Float.of_int r, Float.of_int g, Float.of_int b, Float.of_int a) let rgb2y (r, g, b, a) = @@ -25,10 +38,13 @@ let rgb2q (r, g, b, a) = let calculatePixelColorDelta _pixelA _pixelB = let pixelA = _pixelA |> convertPixelToFloat |> blendSemiTransparentColor in let pixelB = _pixelB |> convertPixelToFloat |> blendSemiTransparentColor in + let y = rgb2y pixelA -. rgb2y pixelB in let i = rgb2i pixelA -. rgb2i pixelB in let q = rgb2q pixelA -. rgb2q pixelB in - (0.5053 *. y *. y) +. (0.299 *. i *. i) +. (0.1957 *. q *. q) + + let delta = (0.5053 *. y *. y) +. (0.299 *. i *. i) +. (0.1957 *. q *. q) in + delta let calculatePixelBrightnessDelta pixelA pixelB = let pixelA = pixelA |> convertPixelToFloat |> blendSemiTransparentColor in diff --git a/test/Test_Core.ml b/test/Test_Core.ml index f58fca21..bfcd24a4 100644 --- a/test/Test_Core.ml +++ b/test/Test_Core.ml @@ -37,8 +37,8 @@ let _ = let _, diffPixels, diffPercentage, _ = PNG_Diff.compare img1 img2 ~threshold:0.5 () in - (expect.int diffPixels).toBe 222; - (expect.float diffPercentage).toBeCloseTo 0.19)) + (expect.int diffPixels).toBe 25; + (expect.float diffPercentage).toBeCloseTo 0.02)) let _ = describe "CORE: Ignore Regions" (fun { test; _ } -> @@ -80,3 +80,50 @@ let _ = "test/test-images/png/diff-of-diff-green.png"); (expect.int diffOfDiffPixels).toBe 0; (expect.float diffOfDiffPercentage).toBeCloseTo 0.0)) + +let _ = + describe "CORE: blendSemiTransparentColor" (fun { test; _ } -> + test "blend 255. alpha" (fun { expect; _ } -> + let r, g, b, a = + Odiff.ColorDelta.blendSemiTransparentColor (0., 128., 255., 255.) + in + (expect.float r).toBeCloseTo 0.; + (expect.float g).toBeCloseTo 128.; + (expect.float b).toBeCloseTo 255.; + (expect.float a).toBeCloseTo 1.); + + test "blend 0. alpha" (fun { expect; _ } -> + let r, g, b, a = + Odiff.ColorDelta.blendSemiTransparentColor (0., 128., 255., 0.) + in + (expect.float r).toBeCloseTo 255.; + (expect.float g).toBeCloseTo 255.; + (expect.float b).toBeCloseTo 255.; + (expect.float a).toBeCloseTo 0.); + + test "blend 5. alpha" (fun { expect; _ } -> + let r, g, b, a = + Odiff.ColorDelta.blendSemiTransparentColor (0., 128., 255., 5.) + in + (expect.float r).toBeCloseTo 250.; + (expect.float g).toBeCloseTo 252.51; + (expect.float b).toBeCloseTo 255.; + (expect.float a).toBeCloseTo 0.02); + + test "blend 51. alpha" (fun { expect; _ } -> + let r, g, b, a = + Odiff.ColorDelta.blendSemiTransparentColor (0., 128., 255., 51.) + in + (expect.float r).toBeCloseTo 204.; + (expect.float g).toBeCloseTo 229.6; + (expect.float b).toBeCloseTo 255.; + (expect.float a).toBeCloseTo 0.2); + + test "blend 128. alpha" (fun { expect; _ } -> + let r, g, b, a = + Odiff.ColorDelta.blendSemiTransparentColor (0., 128., 255., 128.) + in + (expect.float r).toBeCloseTo 127.; + (expect.float g).toBeCloseTo 191.25; + (expect.float b).toBeCloseTo 255.; + (expect.float a).toBeCloseTo 0.5)) diff --git a/test/Test_IO_PNG.ml b/test/Test_IO_PNG.ml index 4c8f83e8..075ade37 100644 --- a/test/Test_IO_PNG.ml +++ b/test/Test_IO_PNG.ml @@ -1,16 +1,17 @@ open TestFramework +open Png.IO open ODiffIO module Diff = Odiff.Diff.MakeDiff (Png.IO) (Png.IO) let _ = describe "IO: PNG" (fun { test; _ } -> - let open Png.IO in test "finds difference between 2 images" (fun { expect; _ } -> let img1 = loadImage "test/test-images/png/orange.png" in let img2 = loadImage "test/test-images/png/orange_changed.png" in let _, diffPixels, diffPercentage, _ = Diff.compare img1 img2 () in - (expect.int diffPixels).toBe 1430; - (expect.float diffPercentage).toBeCloseTo 1.20); + (expect.int diffPixels).toBe 1366; + (expect.float diffPercentage).toBeCloseTo 1.14); + test "Diff of mask and no mask are equal" (fun { expect; _ } -> let img1 = loadImage "test/test-images/png/orange.png" in let img2 = loadImage "test/test-images/png/orange_changed.png" in @@ -24,6 +25,7 @@ let _ = in (expect.int diffPixels).toBe diffPixelsMask; (expect.float diffPercentage).toBeCloseTo diffPercentageMask); + test "Creates correct diff output image" (fun { expect; _ } -> let img1 = loadImage "test/test-images/png/orange.png" in let img2 = loadImage "test/test-images/png/orange_changed.png" in @@ -36,4 +38,15 @@ let _ = saveImage diffOutput "test/test-images/png/diff-output.png"; saveImage diffMaskOfDiff "test/test-images/png/diff-of-diff.png"); (expect.int diffOfDiffPixels).toBe 0; - (expect.float diffOfDiffPercentage).toBeCloseTo 0.0)) + (expect.float diffOfDiffPercentage).toBeCloseTo 0.0); + + test "Correctly handles different encodings of transparency" + (fun { expect; _ } -> + let img1 = + loadImage "test/test-images/png/extreme-alpha.png" + in + let img2 = + loadImage "test/test-images/png/extreme-alpha-1.png" + in + let _, diffPixels, _, _ = Diff.compare img1 img2 () in + (expect.int diffPixels).toBe 0)) diff --git a/test/test-images/png/diff-output-green.png b/test/test-images/png/diff-output-green.png new file mode 100644 index 00000000..99800d2e Binary files /dev/null and b/test/test-images/png/diff-output-green.png differ diff --git a/test/test-images/png/extreme-alpha-1.png b/test/test-images/png/extreme-alpha-1.png new file mode 100644 index 00000000..7994e894 Binary files /dev/null and b/test/test-images/png/extreme-alpha-1.png differ diff --git a/test/test-images/png/extreme-alpha.png b/test/test-images/png/extreme-alpha.png new file mode 100644 index 00000000..ec12bddb Binary files /dev/null and b/test/test-images/png/extreme-alpha.png differ diff --git a/test/test-images/png/orange_diff.png b/test/test-images/png/orange_diff.png index ebe5e7e2..636d46e4 100644 Binary files a/test/test-images/png/orange_diff.png and b/test/test-images/png/orange_diff.png differ diff --git a/test/test-images/png/orange_diff_green.png b/test/test-images/png/orange_diff_green.png index c125e7b5..fefba2b8 100644 Binary files a/test/test-images/png/orange_diff_green.png and b/test/test-images/png/orange_diff_green.png differ