Browse Source

Fix a few more cases in which bKGD wasn't properly applied.

Jeroen van Rijn 4 years ago
parent
commit
db1ef078ff
1 changed files with 71 additions and 12 deletions
  1. 71 12
      core/image/png/png.odin

+ 71 - 12
core/image/png/png.odin

@@ -886,9 +886,25 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 			}
 			}
 		case 2:
 		case 2:
 			// Gray with alpha, we shouldn't have a tRNS chunk.
 			// Gray with alpha, we shouldn't have a tRNS chunk.
+			bg := f32(0.0);
+			if seen_bkgd {
+				bg = f32(img.background.([3]u16)[0]);
+			}
+
 			for len(p16) > 0 {
 			for len(p16) > 0 {
 				r := p16[0];
 				r := p16[0];
-				if premultiply {
+				if seen_bkgd {
+					alpha := f32(p16[1]) / f32(65535);
+					c := u16(f32(r) * alpha + (1.0 - alpha) * bg);
+					o16[0] = c;
+					o16[1] = c;
+					o16[2] = c;
+					/*
+						After BG blending, the pixel is now fully opaque.
+						Update the value we'll write to the output alpha.
+					*/
+					p16[1] = 65535;
+				} else if premultiply {
 					alpha := p16[1];
 					alpha := p16[1];
 					c := u16(f32(r) * f32(alpha) / f32(65535));
 					c := u16(f32(r) * f32(alpha) / f32(65535));
 					o16[0] = c;
 					o16[0] = c;
@@ -900,7 +916,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 					o16[2] = r;
 					o16[2] = r;
 				}
 				}
 
 
-				if .alpha_drop_if_present not_in options {
+				if out_image_channels == 4 {
 					o16[3] = p16[1];
 					o16[3] = p16[1];
 				}
 				}
 
 
@@ -963,7 +979,22 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 				b     := p16[2];
 				b     := p16[2];
 				a     := p16[3];
 				a     := p16[3];
 
 
-				if premultiply {
+				if seen_bkgd {
+					alpha := f32(a) / 65535.0;
+					c  := img.background.([3]u16);
+					rb := f32(c[0]) * (1.0 - alpha);
+					gb := f32(c[1]) * (1.0 - alpha);
+					bb := f32(c[2]) * (1.0 - alpha);
+
+					o16[0] = u16(f32(r) * alpha + rb);
+					o16[1] = u16(f32(g) * alpha + gb);
+					o16[2] = u16(f32(b) * alpha + bb);
+					/*
+						After BG blending, the pixel is now fully opaque.
+						Update the value we'll write to the output alpha.
+					*/
+					a = 65535;
+				} else if premultiply {
 					alpha := f32(a) / 65535.0;
 					alpha := f32(a) / 65535.0;
 					o16[0] = u16(f32(r) * alpha);
 					o16[0] = u16(f32(r) * alpha);
 					o16[1] = u16(f32(g) * alpha);
 					o16[1] = u16(f32(g) * alpha);
@@ -974,7 +1005,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 					o16[2] = b;
 					o16[2] = b;
 				}
 				}
 
 
-				if .alpha_drop_if_present not_in options {
+				if out_image_channels == 4 {
 					o16[3] = a;
 					o16[3] = a;
 				}
 				}
 
 
@@ -992,7 +1023,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 		// Check if we need to do something.
 		// Check if we need to do something.
 		if raw_image_channels == out_image_channels {
 		if raw_image_channels == out_image_channels {
 			// If we have 3 in and 3 out, or 4 in and 4 out without premultiplication...
 			// If we have 3 in and 3 out, or 4 in and 4 out without premultiplication...
-			if raw_image_channels == 4 && .alpha_premultiply not_in options {
+			if !premultiply {
 				// Then we're done.
 				// Then we're done.
 				return img, E_General.OK;
 				return img, E_General.OK;
 			}
 			}
@@ -1050,9 +1081,25 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 			}
 			}
 		case 2:
 		case 2:
 			// Gray with alpha, we shouldn't have a tRNS chunk.
 			// Gray with alpha, we shouldn't have a tRNS chunk.
+			bg := f32(0.0);
+			if seen_bkgd {
+				bg = f32(img.background.([3]u16)[0]);
+			}
+
 			for len(p) > 0 {
 			for len(p) > 0 {
 				r := p[0];
 				r := p[0];
-				if .alpha_premultiply in options {
+				if seen_bkgd {
+					alpha := f32(p[1]) / f32(255);
+					c := u8(f32(r) * alpha + (1.0 - alpha) * bg);
+					o[0] = c;
+					o[1] = c;
+					o[2] = c;
+					/*
+						After BG blending, the pixel is now fully opaque.
+						Update the value we'll write to the output alpha.
+					*/
+					p[1] = 255;
+				} else if .alpha_premultiply in options {
 					alpha := p[1];
 					alpha := p[1];
 					c := u8(f32(r) * f32(alpha) / f32(255));
 					c := u8(f32(r) * f32(alpha) / f32(255));
 					o[0] = c;
 					o[0] = c;
@@ -1064,7 +1111,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 					o[2] = r;
 					o[2] = r;
 				}
 				}
 
 
-				if .alpha_drop_if_present not_in options {
+				if out_image_channels == 4 {
 					o[3] = p[1];
 					o[3] = p[1];
 				}
 				}
 
 
@@ -1088,7 +1135,6 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 
 
 				alpha := u8(1); // Default to full opaque
 				alpha := u8(1); // Default to full opaque
 
 
-				// TODO: Combine the seen_trns cases.
 				if seen_trns {
 				if seen_trns {
 					if r == key[0] && g == key[1] && b == key[2] {
 					if r == key[0] && g == key[1] && b == key[2] {
 						if seen_bkgd {
 						if seen_bkgd {
@@ -1126,8 +1172,22 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 				g     := p[1];
 				g     := p[1];
 				b     := p[2];
 				b     := p[2];
 				a     := p[3];
 				a     := p[3];
-
-				if .alpha_premultiply in options {
+				if seen_bkgd {
+					alpha := f32(a) / 255.0;
+					c  := img.background.([3]u16);
+					rb := f32(c[0]) * (1.0 - alpha);
+					gb := f32(c[1]) * (1.0 - alpha);
+					bb := f32(c[2]) * (1.0 - alpha);
+
+					o[0] = u8(f32(r) * alpha + rb);
+					o[1] = u8(f32(g) * alpha + gb);
+					o[2] = u8(f32(b) * alpha + bb);
+					/*
+						After BG blending, the pixel is now fully opaque.
+						Update the value we'll write to the output alpha.
+					*/
+					a = 255;
+				} else if premultiply {
 					alpha := f32(a) / 255.0;
 					alpha := f32(a) / 255.0;
 					o[0] = u8(f32(r) * alpha);
 					o[0] = u8(f32(r) * alpha);
 					o[1] = u8(f32(g) * alpha);
 					o[1] = u8(f32(g) * alpha);
@@ -1138,7 +1198,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
 					o[2] = b;
 					o[2] = b;
 				}
 				}
 
 
-				if .alpha_drop_if_present not_in options {
+				if out_image_channels == 4 {
 					o[3] = a;
 					o[3] = a;
 				}
 				}
 
 
@@ -1207,7 +1267,6 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
 		nk := row_stride - channels;
 		nk := row_stride - channels;
 
 
 		filter := Row_Filter(src[0]); src = src[1:];
 		filter := Row_Filter(src[0]); src = src[1:];
-		// fmt.printf("Row: %v | Filter: %v\n", y, filter);
 		switch(filter) {
 		switch(filter) {
 		case .None:
 		case .None:
 			copy(dest, src[:row_stride]);
 			copy(dest, src[:row_stride]);