commit
4fc124b92f
1 changed files with 36 additions and 32 deletions
|
@ -4,15 +4,15 @@ const h = 0.5.float32
|
||||||
|
|
||||||
type
|
type
|
||||||
Image* = ref object
|
Image* = ref object
|
||||||
## Main image object that holds the bitmap data in RGBA format.
|
## Image object that holds bitmap data in RGBA format.
|
||||||
width*, height*: int
|
width*, height*: int
|
||||||
data*: seq[ColorRGBA]
|
data*: seq[ColorRGBA]
|
||||||
|
|
||||||
proc draw*(a, b: Image, mat: Mat3, blendMode = bmNormal)
|
when defined(release):
|
||||||
proc draw*(a, b: Image, pos = vec2(0, 0), blendMode = bmNormal) {.inline.}
|
{.push checks: off.}
|
||||||
|
|
||||||
proc newImage*(width, height: int): Image =
|
proc newImage*(width, height: int): Image =
|
||||||
## Creates a new image with appropriate dimensions.
|
## Creates a new image with the parameter dimensions.
|
||||||
result = Image()
|
result = Image()
|
||||||
result.width = width
|
result.width = width
|
||||||
result.height = height
|
result.height = height
|
||||||
|
@ -23,23 +23,20 @@ proc wh*(image: Image): Vec2 {.inline.} =
|
||||||
vec2(image.width.float32, image.height.float32)
|
vec2(image.width.float32, image.height.float32)
|
||||||
|
|
||||||
proc copy*(image: Image): Image =
|
proc copy*(image: Image): Image =
|
||||||
## Copies an image creating a new image.
|
## Copies the image data into a new image.
|
||||||
result = newImage(image.width, image.height)
|
result = newImage(image.width, image.height)
|
||||||
result.data = image.data
|
result.data = image.data
|
||||||
|
|
||||||
proc `$`*(image: Image): string =
|
proc `$`*(image: Image): string =
|
||||||
## Display the image size and channels.
|
## Prints the image size.
|
||||||
"<Image " & $image.width & "x" & $image.height & ">"
|
"<Image " & $image.width & "x" & $image.height & ">"
|
||||||
|
|
||||||
proc inside*(image: Image, x, y: int): bool {.inline.} =
|
proc inside*(image: Image, x, y: int): bool {.inline.} =
|
||||||
## Returns true if (x, y) is inside the image.
|
## Returns true if (x, y) is inside the image.
|
||||||
x >= 0 and x < image.width and y >= 0 and y < image.height
|
x >= 0 and x < image.width and y >= 0 and y < image.height
|
||||||
|
|
||||||
proc inside1px*(image: Image, x, y: float): bool {.inline.} =
|
proc dataIndex*(image: Image, x, y: int): int {.inline.} =
|
||||||
## Returns true if (x, y) is inside the image.
|
image.width * y + x
|
||||||
const px = 1
|
|
||||||
x >= -px and x < (image.width.float32 + px) and
|
|
||||||
y >= -px and y < (image.height.float32 + px)
|
|
||||||
|
|
||||||
proc getRgbaUnsafe*(image: Image, x, y: int): ColorRGBA {.inline.} =
|
proc getRgbaUnsafe*(image: Image, x, y: int): ColorRGBA {.inline.} =
|
||||||
## Gets a color from (x, y) coordinates.
|
## Gets a color from (x, y) coordinates.
|
||||||
|
@ -48,11 +45,6 @@ proc getRgbaUnsafe*(image: Image, x, y: int): ColorRGBA {.inline.} =
|
||||||
## Failure in the assumptions will case unsafe memory reads.
|
## Failure in the assumptions will case unsafe memory reads.
|
||||||
result = image.data[image.width * y + x]
|
result = image.data[image.width * y + x]
|
||||||
|
|
||||||
proc getAddr*(image: Image, x, y: int): pointer {.inline.} =
|
|
||||||
## Gets a address of the color from (x, y) coordinates.
|
|
||||||
## Unsafe make sure x, y are in bounds.
|
|
||||||
image.data[image.width * y + x].addr
|
|
||||||
|
|
||||||
proc `[]`*(image: Image, x, y: int): ColorRGBA {.inline.} =
|
proc `[]`*(image: Image, x, y: int): ColorRGBA {.inline.} =
|
||||||
## Gets a pixel at (x, y) or returns transparent black if outside of bounds.
|
## Gets a pixel at (x, y) or returns transparent black if outside of bounds.
|
||||||
if image.inside(x, y):
|
if image.inside(x, y):
|
||||||
|
@ -63,39 +55,51 @@ proc setRgbaUnsafe*(image: Image, x, y: int, rgba: ColorRGBA) {.inline.} =
|
||||||
## * No bounds checking *
|
## * No bounds checking *
|
||||||
## Make sure that x, y are in bounds.
|
## Make sure that x, y are in bounds.
|
||||||
## Failure in the assumptions will case unsafe memory writes.
|
## Failure in the assumptions will case unsafe memory writes.
|
||||||
image.data[image.width * y + x] = rgba
|
image.data[image.dataIndex(x, y)] = rgba
|
||||||
|
|
||||||
proc `[]=`*(image: Image, x, y: int, rgba: ColorRGBA) {.inline.} =
|
proc `[]=`*(image: Image, x, y: int, rgba: ColorRGBA) {.inline.} =
|
||||||
## Sets a pixel at (x, y) or does nothing if outside of bounds.
|
## Sets a pixel at (x, y) or does nothing if outside of bounds.
|
||||||
if image.inside(x, y):
|
if image.inside(x, y):
|
||||||
image.setRgbaUnsafe(x, y, rgba)
|
image.setRgbaUnsafe(x, y, rgba)
|
||||||
|
|
||||||
proc fill*(image: Image, rgba: ColorRgba) =
|
proc fillUnsafe(data: var seq[ColorRGBA], rgba: ColorRGBA, start, len: int) =
|
||||||
## Fills the image with a solid color.
|
## Fills the image data with a solid color starting at index start and
|
||||||
|
## continuing for len indices.
|
||||||
|
|
||||||
# Use memset whene very byte has the same value
|
# Use memset when every byte has the same value
|
||||||
if rgba.r == rgba.g and rgba.r == rgba.b and rgba.r == rgba.a:
|
if rgba.r == rgba.g and rgba.r == rgba.b and rgba.r == rgba.a:
|
||||||
nimSetMem(image.data[0].addr, rgba.r.cint, image.data.len * 4)
|
nimSetMem(data[start].addr, rgba.r.cint, len * 4)
|
||||||
else:
|
else:
|
||||||
var i: int
|
var i = start
|
||||||
when defined(amd64):
|
when defined(amd64):
|
||||||
# When supported, SIMD fill until we run out of room.
|
# When supported, SIMD fill until we run out of room
|
||||||
let m = mm_set1_epi32(cast[int32](rgba))
|
let m = mm_set1_epi32(cast[int32](rgba))
|
||||||
while i < image.data.len - 4:
|
for j in countup(i, start + len - 8, 8):
|
||||||
mm_storeu_si128(image.data[i].addr, m)
|
mm_storeu_si128(data[j].addr, m)
|
||||||
i += 4
|
mm_storeu_si128(data[j + 4].addr, m)
|
||||||
|
i += 8
|
||||||
else:
|
else:
|
||||||
when sizeof(int) == 8:
|
when sizeof(int) == 8:
|
||||||
# Fill 8 bytes at a time when possible
|
# Fill 8 bytes at a time when possible
|
||||||
let
|
let
|
||||||
u32 = cast[uint32](rgba)
|
u32 = cast[uint32](rgba)
|
||||||
u64 = cast[uint64]([u32, u32])
|
u64 = cast[uint64]([u32, u32])
|
||||||
while i < image.data.len - 2:
|
for j in countup(i, start + len - 2, 2):
|
||||||
cast[ptr uint64](image.data[i].addr)[] = u64
|
cast[ptr uint64](image.data[j].addr)[] = u64
|
||||||
i += 2
|
i += 2
|
||||||
# Fill whatever is left the slow way
|
# Fill whatever is left the slow way
|
||||||
for j in i ..< image.data.len:
|
for j in i ..< start + len:
|
||||||
image.data[j] = rgba
|
data[j] = rgba
|
||||||
|
|
||||||
|
proc fill*(image: Image, rgba: ColorRgba) {.inline.} =
|
||||||
|
## Fills the image with a solid color.
|
||||||
|
fillUnsafe(image.data, rgba, 0, image.data.len)
|
||||||
|
|
||||||
|
when defined(release):
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
|
proc draw*(a, b: Image, mat: Mat3, blendMode = bmNormal)
|
||||||
|
proc draw*(a, b: Image, pos = vec2(0, 0), blendMode = bmNormal) {.inline.}
|
||||||
|
|
||||||
proc invert*(image: Image) =
|
proc invert*(image: Image) =
|
||||||
## Inverts all of the colors and alpha.
|
## Inverts all of the colors and alpha.
|
||||||
|
@ -493,7 +497,7 @@ proc drawUber(
|
||||||
|
|
||||||
if blendMode == bmIntersectMask:
|
if blendMode == bmIntersectMask:
|
||||||
if xMin > 0:
|
if xMin > 0:
|
||||||
zeroMem(a.getAddr(0, y), 4 * xMin)
|
zeroMem(a.data[a.dataIndex(0, y)].addr, 4 * xMin)
|
||||||
|
|
||||||
for x in xMin ..< xMax:
|
for x in xMin ..< xMax:
|
||||||
let
|
let
|
||||||
|
@ -510,7 +514,7 @@ proc drawUber(
|
||||||
|
|
||||||
if blendMode == bmIntersectMask:
|
if blendMode == bmIntersectMask:
|
||||||
if a.width - xMax > 0:
|
if a.width - xMax > 0:
|
||||||
zeroMem(a.getAddr(xMax, y), 4 * (a.width - xMax))
|
zeroMem(a.data[a.dataIndex(xMax, y)].addr, 4 * (a.width - xMax))
|
||||||
|
|
||||||
proc draw*(a, b: Image, mat: Mat3, blendMode: BlendMode) =
|
proc draw*(a, b: Image, mat: Mat3, blendMode: BlendMode) =
|
||||||
## Draws one image onto another using matrix with color blending.
|
## Draws one image onto another using matrix with color blending.
|
||||||
|
|
Loading…
Reference in a new issue