Skip to content

Commit

Permalink
fix: Semi transparent colors normalzation (#93)
Browse files Browse the repository at this point in the history
* Normalize alpha before blending

* Add unit tests for blendSemiTransparentColor

* Clamp rgb values and distance instead of range assertion

* Update tests

* Correctly fix alpha decoding

* Optimize color blending

* Unstage test artifacts

---------

Co-authored-by: Dmitriy Kovalenko <[email protected]>
  • Loading branch information
ryo33 and dmtrKovalenko authored Apr 29, 2024
1 parent a6f8e3c commit e527f35
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 12 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ _release
*.byte
*.native
*.install
images/diff.png
images/diff.png
test/test-images/_*.png

Binary file added images/extreme-alpha-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/extreme-alpha.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/www.cypress-diff.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion io/png/ReadPng.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
24 changes: 20 additions & 4 deletions src/ColorDelta.ml
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
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
let a = (pixel lsr 24) land 255 in
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) =
Expand All @@ -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
Expand Down
51 changes: 49 additions & 2 deletions test/Test_Core.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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; _ } ->
Expand Down Expand Up @@ -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))
21 changes: 17 additions & 4 deletions test/Test_IO_PNG.ml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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))
Binary file added test/test-images/png/diff-output-green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/test-images/png/extreme-alpha-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/test-images/png/extreme-alpha.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/test-images/png/orange_diff.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/test-images/png/orange_diff_green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e527f35

Please sign in to comment.