diff --git a/src/pixie/images.nim b/src/pixie/images.nim index 5a10aa6..61886e8 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -70,18 +70,16 @@ proc `[]=`*(image: Image, x, y: int, rgba: ColorRGBA) {.inline.} = proc fill*(image: Image, rgba: ColorRgba) = ## Fills the image with a solid color. - for i in 0 ..< image.data.len: - image.data[i] = rgba + for c in image.data.mitems: + c = rgba proc invert*(image: Image) = ## Inverts all of the colors and alpha. - for i in 0 ..< image.data.len: - var rgba = image.data[i] + for rgba in image.data.mitems: rgba.r = 255 - rgba.r rgba.g = 255 - rgba.g rgba.b = 255 - rgba.b rgba.a = 255 - rgba.a - image.data[i] = rgba proc subImage*(image: Image, x, y, w, h: int): Image = ## Gets a sub image of the main image. diff --git a/tests/benchmark_images.nim b/tests/benchmark_images.nim new file mode 100644 index 0000000..6db1b6a --- /dev/null +++ b/tests/benchmark_images.nim @@ -0,0 +1,40 @@ +import chroma, pixie, fidget/opengl/perf + +const iterations = 100 + +proc fillOriginal(a: Image, rgba: ColorRGBA) = + for y in 0 ..< a.height: + for x in 0 ..< a.width: + a.setRgbaUnsafe(x, y, rgba) + +proc invertOriginal(a: Image) = + for y in 0 ..< a.height: + for x in 0 ..< a.width: + var rgba = a.getRgbaUnsafe(x, y) + rgba.r = 255 - rgba.r + rgba.g = 255 - rgba.g + rgba.b = 255 - rgba.b + rgba.a = 255 - rgba.a + a.setRgbaUnsafe(x, y, rgba) + +timeIt "fillOriginal": + var a = newImage(2560, 1440) + for i in 0 ..< iterations: + a.fillOriginal(rgba(255, 255, 255, 255)) + doAssert a[0, 0] == rgba(255, 255, 255, 255) + +timeIt "fill": + var a = newImage(2560, 1440) + for i in 0 ..< iterations: + a.fill(rgba(255, 255, 255, 255)) + doAssert a[0, 0] == rgba(255, 255, 255, 255) + +timeIt "invertOriginal": + var a = newImage(2560, 1440) + for i in 0 ..< iterations: + a.invertOriginal() + +timeIt "invert": + var a = newImage(2560, 1440) + for i in 0 ..< iterations: + a.invert() diff --git a/tests/benchmark_draw.nim b/tests/benchmark_images_draw.nim similarity index 100% rename from tests/benchmark_draw.nim rename to tests/benchmark_images_draw.nim diff --git a/tests/benchmark_shadows.nim b/tests/benchmark_images_shadows.nim similarity index 70% rename from tests/benchmark_shadows.nim rename to tests/benchmark_images_shadows.nim index adb684b..ee42cc1 100644 --- a/tests/benchmark_shadows.nim +++ b/tests/benchmark_images_shadows.nim @@ -4,13 +4,15 @@ timeIt "spread": var tmp = 0 var spread: Image for i in 0 ..< 100: - var a = newImageFill(100, 100, rgba(0, 0, 0, 0)) - var b = newImageFill(50, 50, rgba(0, 0, 0, 255)) + var a = newImage(100, 100) + var b = newImage(50, 50) + b.fill(rgba(0, 0, 0, 255)) a.draw(b, vec2(25, 25)) spread = a.spread(spread = 10) - b = newImageFill(50, 50, rgba(255, 255, 255, 255)) + b = newImage(50, 50) + b.fill(rgba(255, 255, 255, 255)) spread.draw(b, vec2(25, 25)) tmp += spread.width * spread.height @@ -21,13 +23,15 @@ timeIt "blur": var tmp = 0 var blur: Image for i in 0 ..< 100: - var a = newImageFill(100, 100, rgba(0, 0, 0, 0)) - var b = newImageFill(50, 50, rgba(255, 255, 255, 255)) + var a = newImage(100, 100) + var b = newImage(50, 50) + b.fill(rgba(255, 255, 255, 255)) a.draw(b, vec2(25, 25)) blur = a.blur(radius = 10) - b = newImageFill(50, 50, rgba(255, 255, 255, 255)) + b = newImage(50, 50) + b.fill(rgba(255, 255, 255, 255)) blur.draw(b, vec2(25, 25)) tmp += blur.width * blur.height @@ -38,14 +42,16 @@ timeIt "shadow": var tmp = 0 var shadow: Image for i in 0 ..< 100: - var a = newImageFill(100, 100, rgba(0, 0, 0, 0)) - var b = newImageFill(50, 50, rgba(0, 0, 0, 255)) + var a = newImage(100, 100) + var b = newImage(50, 50) + b.fill(rgba(0, 0, 0, 255)) a.draw(b, vec2(25, 25)) shadow = a.shadow( offset = vec2(0, 0), spread = 10, blur = 10, color = rgba(0, 0, 0, 255)) - b = newImageFill(50, 50, rgba(255, 255, 255, 255)) + b = newImage(50, 50) + b.fill(rgba(255, 255, 255, 255)) shadow.draw(b, vec2(25, 25)) tmp += shadow.width * shadow.height @@ -57,8 +63,9 @@ timeIt "shadow": # var tmp = 0 # var shadow: Image # for i in 0 ..< 1: -# var a = newImageFill(10, 200, rgba(0, 0, 0, 0)) -# var b = newImageFill(50, 50, rgba(0, 0, 0, 255)) +# var a = newImage(10, 200) +# var b = newImage(50, 50) +# b.fill(rgba(0, 0, 0, 255)) # a.draw(b, vec2(-25, -25)) # for spread in 0 .. 0: @@ -73,7 +80,8 @@ timeIt "shadow": # for y in 25 ..< (25 + spread + blur).int: # echo y - 25, ":", shadow[5, y].a -# b = newImageFill(50, 50, rgba(255, 255, 255, 255)) +# b = newImage(50, 50) +# b.fill(rgba(255, 255, 255, 255)) # shadow.draw(b, vec2(-25, -25)) # tmp += shadow.width * shadow.height diff --git a/tests/benchmark_inplace.nim b/tests/benchmark_inplace.nim deleted file mode 100644 index 6c9a641..0000000 --- a/tests/benchmark_inplace.nim +++ /dev/null @@ -1,209 +0,0 @@ -import pixie, chroma, vmath, fidget/opengl/perf, pixie/fileformats/bmp - -proc inPlaceDraw*(destImage: Image, srcImage: Image, mat: Mat3, blendMode = bmNormal) = - ## Draws one image onto another using matrix with color blending. - for y in 0 ..< destImage.width: - for x in 0 ..< destImage.height: - let srcPos = mat * vec2(x.float32, y.float32) - let destRgba = destImage.getRgbaUnsafe(x, y) - var rgba = destRgba - var srcRgba = rgba(0, 0, 0, 0) - if srcImage.inside(srcPos.x.floor.int, srcPos.y.floor.int): - srcRgba = srcImage.getRgbaSmooth(srcPos.x - 0.5, srcPos.y - 0.5) - if blendMode.hasEffect(srcRgba): - rgba = blendMode.mix(destRgba, srcRgba) - destImage.setRgbaUnsafe(x, y, rgba) - -proc inPlaceDraw*(destImage: Image, srcImage: Image, pos = vec2(0, 0), blendMode = bmNormal) = - destImage.inPlaceDraw(srcImage, translate(-pos), blendMode) - -proc drawStepperInPlace*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode) = - ## Draws one image onto another using matrix with color blending. - - type Segment = object - ## A math segment from point "at" to point "to" - at*: Vec2 - to*: Vec2 - - proc segment(at, to: Vec2): Segment = - result.at = at - result.to = to - - proc intersects(a, b: Segment, at: var Vec2): bool = - ## Checks if the a segment intersects b segment. - ## If it returns true, at will have point of intersection - var s1x, s1y, s2x, s2y: float32 - s1x = a.to.x - a.at.x - s1y = a.to.y - a.at.y - s2x = b.to.x - b.at.x - s2y = b.to.y - b.at.y - - var s, t: float32 - s = (-s1y * (a.at.x - b.at.x) + s1x * (a.at.y - b.at.y)) / - (-s2x * s1y + s1x * s2y) - t = (s2x * (a.at.y - b.at.y) - s2y * (a.at.x - b.at.x)) / - (-s2x * s1y + s1x * s2y) - - if s >= 0 and s < 1 and t >= 0 and t < 1: - at.x = a.at.x + (t * s1x) - at.y = a.at.y + (t * s1y) - return true - return false - - var - matInv = mat.inverse() - # compute movement vectors - h = 0.5.float32 - start = matInv * vec2(0 + h, 0 + h) - stepX = matInv * vec2(1 + h, 0 + h) - start - stepY = matInv * vec2(0 + h, 1 + h) - start - minFilterBy2 = max(stepX.length, stepY.length) - b = b - - let corners = [ - mat * vec2(0, 0), - mat * vec2(b.width.float32, 0), - mat * vec2(b.width.float32, b.height.float32), - mat * vec2(0, b.height.float32) - ] - - let lines = [ - segment(corners[0], corners[1]), - segment(corners[1], corners[2]), - segment(corners[2], corners[3]), - segment(corners[3], corners[0]) - ] - - while minFilterBy2 > 2.0: - b = b.minifyBy2() - start /= 2 - stepX /= 2 - stepY /= 2 - minFilterBy2 /= 2 - - template forBlend( - mixer: proc(a, b: ColorRGBA): ColorRGBA, - getRgba: proc(a: Image, x, y: float32): ColorRGBA {.inline.}, - ) = - for y in 0 ..< a.height: - var - xMin = 0 - xMax = 0 - hasIntersection = false - for yOffset in [0.float32, 1]: - var scanLine = segment( - vec2(-100000, y.float32 + yOffset), - vec2(10000, y.float32 + yOffset) - ) - for l in lines: - var at: Vec2 - if intersects(l, scanLine, at): - if hasIntersection: - xMin = min(xMin, at.x.floor.int) - xMax = max(xMax, at.x.ceil.int) - else: - hasIntersection = true - xMin = at.x.floor.int - xMax = at.x.ceil.int - - xMin = xMin.clamp(0, a.width) - xMax = xMax.clamp(0, a.width) - - # for x in 0 ..< xMin: - # result.setRgbaUnsafe(x, y, a.getRgbaUnsafe(x, y)) - # if xMin > 0: - # copyMem(a.getAddr(0, y), a.getAddr(0, y), 4*xMin) - - for x in xMin ..< xMax: - let srcV = start + stepX * float32(x) + stepY * float32(y) - var rgba = a.getRgbaUnsafe(x, y) - if b.inside((srcV.x - h).int, (srcV.y - h).int): - let rgba2 = b.getRgba(srcV.x - h, srcV.y - h) - rgba = mixer(rgba, rgba2) - a.setRgbaUnsafe(x, y, rgba) - - #for x in xMax ..< a.width: - # result.setRgbaUnsafe(x, y, a.getRgbaUnsafe(x, y)) - # if a.width - xMax > 0: - # copyMem(result.getAddr(xMax, y), a.getAddr(xMax, y), 4*(a.width - xMax)) - - proc getRgba(a: Image, x, y: float32): ColorRGBA {.inline.} = - a.getRgbaUnsafe(x.int, y.int) - - if stepX.length == 1.0 and stepY.length == 1.0: - case blendMode - of bmNormal: forBlend(blendNormal, getRgba) - of bmDarken: forBlend(blendDarken, getRgba) - of bmMultiply: forBlend(blendMultiply, getRgba) - of bmLinearBurn: forBlend(blendLinearBurn, getRgba) - of bmColorBurn: forBlend(blendColorBurn, getRgba) - of bmLighten: forBlend(blendLighten, getRgba) - of bmScreen: forBlend(blendScreen, getRgba) - of bmLinearDodge: forBlend(blendLinearDodge, getRgba) - of bmColorDodge: forBlend(blendColorDodge, getRgba) - of bmOverlay: forBlend(blendOverlay, getRgba) - of bmSoftLight: forBlend(blendSoftLight, getRgba) - of bmHardLight: forBlend(blendHardLight, getRgba) - of bmDifference: forBlend(blendDifference, getRgba) - of bmExclusion: forBlend(blendExclusion, getRgba) - of bmHue: forBlend(blendHue, getRgba) - of bmSaturation: forBlend(blendSaturation, getRgba) - of bmColor: forBlend(blendColor, getRgba) - of bmLuminosity: forBlend(blendLuminosity, getRgba) - of bmMask: forBlend(blendMask, getRgba) - of bmOverwrite: forBlend(blendOverwrite, getRgba) - of bmSubtractMask: forBlend(blendSubtractMask, getRgba) - of bmIntersectMask: forBlend(blendIntersectMask, getRgba) - of bmExcludeMask: forBlend(blendExcludeMask, getRgba) - else: - case blendMode - of bmNormal: forBlend(blendNormal, getRgbaSmooth) - of bmDarken: forBlend(blendDarken, getRgbaSmooth) - of bmMultiply: forBlend(blendMultiply, getRgbaSmooth) - of bmLinearBurn: forBlend(blendLinearBurn, getRgbaSmooth) - of bmColorBurn: forBlend(blendColorBurn, getRgbaSmooth) - of bmLighten: forBlend(blendLighten, getRgbaSmooth) - of bmScreen: forBlend(blendScreen, getRgbaSmooth) - of bmLinearDodge: forBlend(blendLinearDodge, getRgbaSmooth) - of bmColorDodge: forBlend(blendColorDodge, getRgbaSmooth) - of bmOverlay: forBlend(blendOverlay, getRgbaSmooth) - of bmSoftLight: forBlend(blendSoftLight, getRgbaSmooth) - of bmHardLight: forBlend(blendHardLight, getRgbaSmooth) - of bmDifference: forBlend(blendDifference, getRgbaSmooth) - of bmExclusion: forBlend(blendExclusion, getRgbaSmooth) - of bmHue: forBlend(blendHue, getRgbaSmooth) - of bmSaturation: forBlend(blendSaturation, getRgbaSmooth) - of bmColor: forBlend(blendColor, getRgbaSmooth) - of bmLuminosity: forBlend(blendLuminosity, getRgbaSmooth) - of bmMask: forBlend(blendMask, getRgbaSmooth) - of bmOverwrite: forBlend(blendOverwrite, getRgbaSmooth) - of bmSubtractMask: forBlend(blendSubtractMask, getRgbaSmooth) - of bmIntersectMask: forBlend(blendIntersectMask, getRgbaSmooth) - of bmExcludeMask: forBlend(blendExcludeMask, getRgbaSmooth) - -timeIt "inPlaceDraw": - var tmp = 0 - for i in 0 ..< 1000: - var a = newImageFill(1000, 1000, rgba(0, 255, 0, 255)) - var b = newImageFill(100, 100, rgba(0, 255, 0, 255)) - a.inPlaceDraw(b, pos=vec2(25, 25)) - tmp += a.width * a.height - echo tmp - -timeIt "drawStepper": - var tmp = 0 - for i in 0 ..< 1000: - var a = newImageFill(1000, 1000, rgba(255, 0, 0, 255)) - var b = newImageFill(100, 100, rgba(0, 255, 0, 255)) - var c = a.drawStepper(b, translate(vec2(25, 25)), bmNormal) - tmp += c.width * c.height - echo tmp - -timeIt "drawStepperInPlace": - var tmp = 0 - for i in 0 ..< 1000: - var a = newImageFill(1000, 1000, rgba(0, 255, 0, 255)) - var b = newImageFill(100, 100, rgba(0, 255, 0, 255)) - drawStepperInPlace(a, b, translate(vec2(25, 25)), bmNormal) - tmp += a.width * a.height - echo tmp diff --git a/tests/images/blur1.png b/tests/images/blur1.png index 469632a..c129e3b 100644 Binary files a/tests/images/blur1.png and b/tests/images/blur1.png differ diff --git a/tests/images/shadow1.png b/tests/images/shadow1.png index 62ac574..3ae66d5 100644 Binary files a/tests/images/shadow1.png and b/tests/images/shadow1.png differ diff --git a/tests/images/spread1.png b/tests/images/spread1.png index 24b105a..3522111 100644 Binary files a/tests/images/spread1.png and b/tests/images/spread1.png differ