From 7e56800de7ed6d35d2b619bfc324e61ca88679e9 Mon Sep 17 00:00:00 2001
From: Ryan Oldenburg <ryan@guzba.com>
Date: Tue, 17 Aug 2021 16:14:31 -0500
Subject: [PATCH] image isTransparent isOneColor

---
 src/pixie/images.nim       | 35 +++++++++++++++++++++++++++++++++++
 tests/benchmark_images.nim |  8 ++++++++
 tests/test_images.nim      | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 76 insertions(+)

diff --git a/src/pixie/images.nim b/src/pixie/images.nim
index 0eb4a69..07cfdb5 100644
--- a/src/pixie/images.nim
+++ b/src/pixie/images.nim
@@ -126,6 +126,41 @@ proc fill*(image: Image, color: SomeColor) {.inline.} =
   ## Fills the image with the parameter color.
   fillUnsafe(image.data, color, 0, image.data.len)
 
+proc isOneColor(image: Image, color: ColorRGBX): bool =
+  ## Checks if the entire image is color.
+  result = true
+
+  let color = image.getRgbaUnsafe(0, 0)
+
+  var i: int
+  when defined(amd64) and not defined(pixieNoSimd):
+    let colorVec = mm_set1_epi32(cast[int32](color))
+    for j in countup(0, image.data.len - 16, 16):
+      let
+        values0 = mm_loadu_si128(image.data[j].addr)
+        values1 = mm_loadu_si128(image.data[j + 4].addr)
+        values2 = mm_loadu_si128(image.data[j + 8].addr)
+        values3 = mm_loadu_si128(image.data[j + 12].addr)
+        values01 = mm_or_si128(values0, values1)
+        values23 = mm_or_si128(values2, values3)
+        values = mm_or_si128(values01, values23)
+        mask = mm_movemask_epi8(mm_cmpeq_epi8(values, colorVec))
+      if mask != uint16.high.int:
+        return false
+      i += 16
+
+  for j in i ..< image.data.len:
+    if image.data[j] != color:
+      return false
+
+proc isOneColor*(image: Image): bool =
+  ## Checks if the entire image is the same color.
+  image.isOneColor(image.getRgbaUnsafe(0, 0))
+
+proc isTransparent*(image: Image): bool =
+  ## Checks if this image is fully transparent or not.
+  image.isOneColor(rgbx(0, 0, 0, 0))
+
 proc flipHorizontal*(image: Image) =
   ## Flips the image around the Y axis.
   let w = image.width div 2
diff --git a/tests/benchmark_images.nim b/tests/benchmark_images.nim
index 6f761d7..428fbd0 100644
--- a/tests/benchmark_images.nim
+++ b/tests/benchmark_images.nim
@@ -17,6 +17,14 @@ timeIt "fill_rgba":
   image.fill(rgba(63, 127, 191, 191))
   doAssert image[0, 0] == rgba(63, 127, 191, 191)
 
+image.fill(rgba(100, 0, 100, 100))
+timeIt "isOneColor":
+  doAssert image.isOneColor()
+
+image.fill(rgba(0, 0, 0, 0))
+timeIt "isTransparent":
+  doAssert image.isTransparent()
+
 reset()
 
 timeIt "subImage":
diff --git a/tests/test_images.nim b/tests/test_images.nim
index 13b3da0..1b052a0 100644
--- a/tests/test_images.nim
+++ b/tests/test_images.nim
@@ -164,3 +164,36 @@ block:
   image.fillPath(p, rgba(255, 0, 0, 255))
 
   newImage(newMask(image)).writeFile("tests/images/mask2image.png")
+
+block:
+  let image = newImage(100, 100)
+  doAssert image.isOneColor()
+
+block:
+  let image = newImage(100, 100)
+  image.fill(rgba(255, 255, 255, 255))
+  doAssert image.isOneColor()
+
+block:
+  let image = newImage(100, 100)
+  image.fill(rgba(1, 2, 3, 4))
+  doAssert image.isOneColor()
+
+block:
+  let image = newImage(100, 100)
+  image[99, 99] = rgba(255, 255, 255, 255)
+  doAssert not image.isOneColor()
+
+block:
+  let image = newImage(100, 100)
+  doAssert image.isTransparent()
+
+block:
+  let image = newImage(100, 100)
+  image.fill(rgba(255, 255, 255, 0))
+  doAssert image.isTransparent()
+
+block:
+  let image = newImage(100, 100)
+  image[99, 99] = rgba(255, 255, 255, 255)
+  doAssert not image.isTransparent()