From ff1e6bd12d1c71c85637f073067576573ec35056 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Sat, 11 Dec 2021 17:57:02 -0600 Subject: [PATCH] image.unsafe[x, y] --- src/pixie/images.nim | 85 ++++++++++++++++++++++---------------------- src/pixie/paints.nim | 10 +++--- src/pixie/paths.nim | 12 +++---- 3 files changed, 54 insertions(+), 53 deletions(-) diff --git a/src/pixie/images.nim b/src/pixie/images.nim index 6945fb4..8dba1b7 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -11,6 +11,9 @@ type width*, height*: int data*: seq[ColorRGBX] + UnsafeImage = object + image: Image + when defined(release): {.push checks: off.} @@ -58,31 +61,32 @@ proc inside*(image: Image, x, y: int): bool {.inline, raises: [].} = proc dataIndex*(image: Image, x, y: int): int {.inline, raises: [].} = image.width * y + x -proc getRgbaUnsafe*(image: Image, x, y: int): ColorRGBX {.inline, raises: [].} = +template unsafe*(src: Image): UnsafeImage = + UnsafeImage(image: src) + +template `[]`*(view: UnsafeImage, x, y: int): ColorRGBX = ## Gets a color from (x, y) coordinates. ## * No bounds checking * ## Make sure that x, y are in bounds. ## Failure in the assumptions will cause unsafe memory reads. - image.data[image.dataIndex(x, y)] + view.image.data[view.image.dataIndex(x, y)] -proc setRgbaUnsafe*( - image: Image, x, y: int, color: ColorRGBX -) {.inline, raises: [].} = +template `[]=`*(view: UnsafeImage, x, y: int, color: ColorRGBX) = ## Sets a color from (x, y) coordinates. ## * No bounds checking * ## Make sure that x, y are in bounds. ## Failure in the assumptions will cause unsafe memory writes. - image.data[image.dataIndex(x, y)] = color + view.image.data[view.image.dataIndex(x, y)] = color proc `[]`*(image: Image, x, y: int): ColorRGBX {.inline, raises: [].} = ## Gets a pixel at (x, y) or returns transparent black if outside of bounds. if image.inside(x, y): - return image.getRgbaUnsafe(x, y) + return image.unsafe[x, y] proc `[]=`*(image: Image, x, y: int, color: SomeColor) {.inline, raises: [].} = ## Sets a pixel at (x, y) or does nothing if outside of bounds. if image.inside(x, y): - image.setRgbaUnsafe(x, y, color.asRgbx()) + image.unsafe[x, y] = color.asRgbx() proc getColor*(image: Image, x, y: int): Color {.inline, raises: [].} = ## Gets a color at (x, y) or returns transparent black if outside of bounds. @@ -239,7 +243,7 @@ proc diff*(master, image: Image): (float32, Image) {.raises: [PixieError].} = c.g = diff.clamp(0, 255).uint8 c.b = (-diff).clamp(0, 255).uint8 c.a = 255 - diffImage.setRgbaUnsafe(x, y, c) + diffImage.unsafe[x, y] = c diffScore += abs(m.r.int - u.r.int) + abs(m.g.int - u.g.int) + abs(m.b.int - u.b.int) + @@ -316,10 +320,10 @@ proc minifyBy2*(image: Image, power = 1): Image {.raises: [PixieError].} = for x in x ..< resultEvenWidth: let - a = src.getRgbaUnsafe(x * 2 + 0, y * 2 + 0) - b = src.getRgbaUnsafe(x * 2 + 1, y * 2 + 0) - c = src.getRgbaUnsafe(x * 2 + 1, y * 2 + 1) - d = src.getRgbaUnsafe(x * 2 + 0, y * 2 + 1) + a = src.unsafe[x * 2 + 0, y * 2 + 0] + b = src.unsafe[x * 2 + 1, y * 2 + 0] + c = src.unsafe[x * 2 + 1, y * 2 + 1] + d = src.unsafe[x * 2 + 0, y * 2 + 1] rgba = rgbx( ((a.r.uint32 + b.r + c.r + d.r) div 4).uint8, ((a.g.uint32 + b.g + c.g + d.g) div 4).uint8, @@ -327,31 +331,28 @@ proc minifyBy2*(image: Image, power = 1): Image {.raises: [PixieError].} = ((a.a.uint32 + b.a + c.a + d.a) div 4).uint8 ) - result.setRgbaUnsafe(x, y, rgba) + result.unsafe[x, y] = rgba if srcWidthIsOdd: let rgbx = mix( - src.getRgbaUnsafe(src.width - 1, y * 2 + 0), - src.getRgbaUnsafe(src.width - 1, y * 2 + 1), + src.unsafe[src.width - 1, y * 2 + 0], + src.unsafe[src.width - 1, y * 2 + 1], 0.5 ) * 0.5 - result.setRgbaUnsafe(result.width - 1, y, rgbx) + result.unsafe[result.width - 1, y] = rgbx if srcHeightIsOdd: for x in 0 ..< resultEvenWidth: let rgbx = mix( - src.getRgbaUnsafe(x * 2 + 0, src.height - 1), - src.getRgbaUnsafe(x * 2 + 1, src.height - 1), + src.unsafe[x * 2 + 0, src.height - 1], + src.unsafe[x * 2 + 1, src.height - 1], 0.5 ) * 0.5 - result.setRgbaUnsafe(x, result.height - 1, rgbx) + result.unsafe[x, result.height - 1] = rgbx if srcWidthIsOdd: - result.setRgbaUnsafe( - result.width - 1, - result.height - 1, - src.getRgbaUnsafe(src.width - 1, src.height - 1) * 0.25 - ) + result.unsafe[result.width - 1, result.height - 1] = + src.unsafe[src.width - 1, src.height - 1] * 0.25 # Set src as this result for if we do another power src = result @@ -384,7 +385,7 @@ proc magnifyBy2*(image: Image, power = 1): Image {.raises: [PixieError].} = x += 2 for _ in x ..< image.width: let - rgbx = image.getRgbaUnsafe(x, y) + rgbx = image.unsafe[x, y] resultIdx = result.dataIndex(x * scale, y * scale) for i in 0 ..< scale: result.data[resultIdx + i] = rgbx @@ -547,10 +548,10 @@ proc blur*( for xx in x - radius ..< min(x + radius, 0): values += outOfBounds * kernel[xx - x + radius] for xx in max(x - radius, 0) .. min(x + radius, image.width - 1): - values += image.getRgbaUnsafe(xx, y) * kernel[xx - x + radius] + values += image.unsafe[xx, y] * kernel[xx - x + radius] for xx in max(x - radius, image.width) .. x + radius: values += outOfBounds * kernel[xx - x + radius] - blurX.setRgbaUnsafe(y, x, rgbx(values)) + blurX.unsafe[y, x] = rgbx(values) # Blur in the Y direction. for y in 0 ..< image.height: @@ -559,10 +560,10 @@ proc blur*( for yy in y - radius ..< min(y + radius, 0): values += outOfBounds * kernel[yy - y + radius] for yy in max(y - radius, 0) .. min(y + radius, image.height - 1): - values += blurX.getRgbaUnsafe(yy, x) * kernel[yy - y + radius] + values += blurX.unsafe[yy, x] * kernel[yy - y + radius] for yy in max(y - radius, image.height) .. y + radius: values += outOfBounds * kernel[yy - y + radius] - image.setRgbaUnsafe(x, y, rgbx(values)) + image.unsafe[x, y] = rgbx(values) proc newMask*(image: Image): Mask {.raises: [PixieError].} = ## Returns a new mask using the alpha values of the image. @@ -611,10 +612,10 @@ proc getRgbaSmooth*( var x0y0, x1y0, x0y1, x1y1: ColorRGBX if wrapped: - x0y0 = image.getRgbaUnsafe(x0 mod image.width, y0 mod image.height) - x1y0 = image.getRgbaUnsafe(x1 mod image.width, y0 mod image.height) - x0y1 = image.getRgbaUnsafe(x0 mod image.width, y1 mod image.height) - x1y1 = image.getRgbaUnsafe(x1 mod image.width, y1 mod image.height) + x0y0 = image.unsafe[x0 mod image.width, y0 mod image.height] + x1y0 = image.unsafe[x1 mod image.width, y0 mod image.height] + x0y1 = image.unsafe[x0 mod image.width, y1 mod image.height] + x1y1 = image.unsafe[x1 mod image.width, y1 mod image.height] else: x0y0 = image[x0, y0] x1y0 = image[x1, y0] @@ -677,7 +678,7 @@ proc drawCorrect( yFloat = samplePos.y - h when type(a) is Image: - let backdrop = a.getRgbaUnsafe(x, y) + let backdrop = a.unsafe[x, y] when type(b) is Image: let sample = b.getRgbaSmooth(xFloat, yFloat, tiled) @@ -686,7 +687,7 @@ proc drawCorrect( let sample = b.getValueSmooth(xFloat, yFloat) blended = blender(backdrop, rgbx(0, 0, 0, sample)) - a.setRgbaUnsafe(x, y, blended) + a.unsafe[x, y] = blended else: # a is a Mask let backdrop = a.getValueUnsafe(x, y) when type(b) is Image: @@ -791,7 +792,7 @@ proc drawUber( for x in xMin ..< xMax: when type(a) is Image: - let backdrop = a.getRgbaUnsafe(x, y) + let backdrop = a.unsafe[x, y] when type(b) is Image: let sample = b.getRgbaSmooth(srcPos.x, srcPos.y) @@ -800,7 +801,7 @@ proc drawUber( let sample = b.getValueSmooth(srcPos.x, srcPos.y) blended = blender(backdrop, rgbx(0, 0, 0, sample)) - a.setRgbaUnsafe(x, y, blended) + a.unsafe[x, y] = blended else: # a is a Mask let backdrop = a.getValueUnsafe(x, y) when type(b) is Image: @@ -892,20 +893,20 @@ proc drawUber( let samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32) when type(a) is Image: - let backdrop = a.getRgbaUnsafe(x, y) + let backdrop = a.unsafe[x, y] when type(b) is Image: let - sample = b.getRgbaUnsafe(samplePos.x, samplePos.y) + sample = b.unsafe[samplePos.x, samplePos.y] blended = blender(backdrop, sample) else: # b is a Mask let sample = b.getValueUnsafe(samplePos.x, samplePos.y) blended = blender(backdrop, rgbx(0, 0, 0, sample)) - a.setRgbaUnsafe(x, y, blended) + a.unsafe[x, y] = blended else: # a is a Mask let backdrop = a.getValueUnsafe(x, y) when type(b) is Image: - let sample = b.getRgbaUnsafe(samplePos.x, samplePos.y).a + let sample = b.unsafe[samplePos.x, samplePos.y].a else: # b is a Mask let sample = b.getValueUnsafe(samplePos.x, samplePos.y) a.setValueUnsafe(x, y, masker(backdrop, sample)) diff --git a/src/pixie/paints.nim b/src/pixie/paints.nim index 9b9bbaa..ba81df6 100644 --- a/src/pixie/paints.nim +++ b/src/pixie/paints.nim @@ -142,7 +142,7 @@ proc fillGradientLinear(image: Image, paint: Paint) = t = toLineSpace(at, to, xy) rgbx = paint.gradientColor(t) for y in 0 ..< image.height: - image.setRgbaUnsafe(x, y, rgbx) + image.unsafe[x, y] = rgbx inc x elif at.x == to.x: # Vertical gradient @@ -158,7 +158,7 @@ proc fillGradientLinear(image: Image, paint: Paint) = mm_storeu_si128(image.data[image.dataIndex(x, y)].addr, colorVec) x += 4 for x in x ..< image.width: - image.setRgbaUnsafe(x, y, rgbx) + image.unsafe[x, y] = rgbx else: for y in 0 ..< image.height: @@ -166,7 +166,7 @@ proc fillGradientLinear(image: Image, paint: Paint) = let xy = vec2(x.float32, y.float32) t = toLineSpace(at, to, xy) - image.setRgbaUnsafe(x, y, paint.gradientColor(t)) + image.unsafe[x, y] = paint.gradientColor(t) proc fillGradientRadial(image: Image, paint: Paint) = ## Fills a radial gradient. @@ -197,7 +197,7 @@ proc fillGradientRadial(image: Image, paint: Paint) = let xy = vec2(x.float32, y.float32) t = (mat * xy).length() - image.setRgbaUnsafe(x, y, paint.gradientColor(t)) + image.unsafe[x, y] = paint.gradientColor(t) proc fillGradientAngular(image: Image, paint: Paint) = ## Fills an angular gradient. @@ -223,7 +223,7 @@ proc fillGradientAngular(image: Image, paint: Paint) = xy = vec2(x.float32, y.float32) angle = normalize(xy - center).angle() t = (angle + gradientAngle + f32PI / 2).fixAngle() / 2 / f32PI + 0.5.float32 - image.setRgbaUnsafe(x, y, paint.gradientColor(t)) + image.unsafe[x, y] = paint.gradientColor(t) proc fillGradient*(image: Image, paint: Paint) {.raises: [PixieError].} = ## Fills with the Paint gradient. diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index acca6bb..67d9a2f 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -1400,7 +1400,7 @@ proc fillCoverage( if coverage != 0 or blendMode == bmExcludeMask: if blendMode == bmNormal and coverage == 255 and rgbx.a == 255: # Skip blending - image.setRgbaUnsafe(x, y, rgbx) + image.unsafe[x, y] = rgbx else: var source = rgbx if coverage != 255: @@ -1408,10 +1408,10 @@ proc fillCoverage( source.g = ((source.g.uint32 * coverage) div 255).uint8 source.b = ((source.b.uint32 * coverage) div 255).uint8 source.a = ((source.a.uint32 * coverage) div 255).uint8 - let backdrop = image.getRgbaUnsafe(x, y) - image.setRgbaUnsafe(x, y, blender(backdrop, source)) + let backdrop = image.unsafe[x, y] + image.unsafe[x, y] = blender(backdrop, source) elif blendMode == bmMask: - image.setRgbaUnsafe(x, y, rgbx(0, 0, 0, 0)) + image.unsafe[x, y] = rgbx(0, 0, 0, 0) inc x if blendMode == bmMask: @@ -1515,8 +1515,8 @@ proc fillHits( x += 4 for x in x ..< fillStart + fillLen: - let backdrop = image.getRgbaUnsafe(x, y) - image.setRgbaUnsafe(x, y, blender(backdrop, rgbx)) + let backdrop = image.unsafe[x, y] + image.unsafe[x, y] = blender(backdrop, rgbx) if blendMode == bmMask: image.clearUnsafe(0, y, startX, y)