diff --git a/src/pixie/images.nim b/src/pixie/images.nim index a5c2e6f..c2fc65d 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -329,6 +329,46 @@ proc applyOpacity*(target: Image | Mask, opacity: float32) = for j in i ..< target.data.len: target.data[j] = ((target.data[j] * opacity) div 255).uint8 +proc invert*(target: Image | Mask) = + ## Inverts all of the colors and alpha. + var i: int + when defined(amd64) and not defined(pixieNoSimd): + let v255 = mm_set1_epi8(cast[int8](255)) + + when type(target) is Image: + let byteLen = target.data.len * 4 + else: + let byteLen = target.data.len + + for _ in countup(0, byteLen - 16, 16): + when type(target) is Image: + let index = i div 4 + else: + let index = i + + var values = mm_loadu_si128(target.data[index].addr) + values = mm_sub_epi8(v255, values) + mm_storeu_si128(target.data[index].addr, values) + + i += 16 + + when type(target) is Image: + for j in i div 4 ..< target.data.len: + var rgba = target.data[j] + rgba.r = 255 - rgba.r + rgba.g = 255 - rgba.g + rgba.b = 255 - rgba.b + rgba.a = 255 - rgba.a + target.data[j] = rgba + + # Inverting rgba(50, 100, 150, 200) becomes rgba(205, 155, 105, 55). This + # is not a valid premultiplied alpha color. + # We need to convert back to premultiplied alpha after inverting. + target.toPremultipliedAlpha() + else: + for j in i ..< target.data.len: + target.data[j] = (255 - target.data[j]).uint8 + proc getRgbaSmooth*(image: Image, x, y: float32): ColorRGBA = let minX = floor(x) @@ -439,24 +479,6 @@ proc draw*( when defined(release): {.pop.} -proc invert*(image: Image) = - ## Inverts all of the colors and alpha. - var i: int - when defined(amd64) and not defined(pixieNoSimd): - let vec255 = mm_set1_epi8(cast[int8](255)) - while i < image.data.len - 4: - var m = mm_loadu_si128(image.data[i].addr) - m = mm_sub_epi8(vec255, m) - mm_storeu_si128(image.data[i].addr, m) - i += 4 - for j in i ..< image.data.len: - var rgba = image.data[j] - rgba.r = 255 - rgba.r - rgba.g = 255 - rgba.g - rgba.b = 255 - rgba.b - rgba.a = 255 - rgba.a - image.data[j] = rgba - proc gaussianLookup(radius: int): seq[float32] = ## Compute lookup table for 1d Gaussian kernel. result.setLen(radius * 2 + 1) diff --git a/tests/benchmark_masks.nim b/tests/benchmark_masks.nim index 5289fdf..3693d7c 100644 --- a/tests/benchmark_masks.nim +++ b/tests/benchmark_masks.nim @@ -13,5 +13,10 @@ timeIt "minifyBy2": reset() +timeIt "invert": + mask.invert() + +reset() + timeIt "applyOpacity": mask.applyOpacity(0.5) diff --git a/tests/test_images.nim b/tests/test_images.nim index cccff3b..0a56c7b 100644 --- a/tests/test_images.nim +++ b/tests/test_images.nim @@ -98,3 +98,9 @@ block: a = readImage("tests/images/flipped1.png") b = a.minifyBy2(2) b.writeFile("tests/images/minifiedBy4.png") + +block: + let a = newImage(100, 100) + a.fill(rgba(50, 100, 150, 200)) + a.invert() + doAssert a[0, 0] == rgba(44, 33, 22, 55) diff --git a/tests/test_masks.nim b/tests/test_masks.nim index 02b5aaa..43ffd2e 100644 --- a/tests/test_masks.nim +++ b/tests/test_masks.nim @@ -7,6 +7,12 @@ block: doAssert mask[0, 0] == 100 doAssert mask[88, 88] == 100 +block: + let mask = newMask(100, 100) + mask.fill(200) + mask.invert() + doAssert mask[0, 0] == 55 + block: let mask = newMask(100, 100)