From 41edec4711d3e03a06bd8b207e25dbc86b683ae3 Mon Sep 17 00:00:00 2001 From: treeform Date: Fri, 27 Nov 2020 10:18:43 -0800 Subject: [PATCH] Add toAlphy and fromAlphy. --- src/pixie/draw.nim | 145 ---------------------------------- src/pixie/images.nim | 63 +++++++++++---- tests/images/transCompose.png | Bin 1709 -> 1716 bytes 3 files changed, 48 insertions(+), 160 deletions(-) delete mode 100644 src/pixie/draw.nim diff --git a/src/pixie/draw.nim b/src/pixie/draw.nim deleted file mode 100644 index 830653b..0000000 --- a/src/pixie/draw.nim +++ /dev/null @@ -1,145 +0,0 @@ -import chroma, blends, vmath, common, images - -proc drawUberTemplate( - a: Image, - b: Image, - c: Image, - mat: Mat3, - lines: array[4, Segment], - blendMode: BlendMode, - inPlace: bool, - smooth: bool, -) = - 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(c.getAddr(0, y), a.getAddr(0, y), 4*xMin) - - for x in xMin ..< xMax: - let srcPos = start + stepX * float32(x) + stepY * float32(y) - #let srcPos = matInv * vec2(x.float32 + h, y.float32 + h) - var rgba = a.getRgbaUnsafe(x, y) - let rgba2 = b.getRgbaFn(srcPos.x - h, srcPos.y - h) - rgba = mixer(rgba, rgba2) - c.setRgbaUnsafe(x, y, rgba) - - #for x in xMax ..< a.width: - # result.setRgbaUnsafe(x, y, a.getRgbaUnsafe(x, y)) - if a.width - xMax > 0: - copyMem(c.getAddr(xMax, y), a.getAddr(xMax, y), 4*(a.width - xMax)) - -proc drawUber*(a: Image, b: Image, c: Image, mat: Mat3, blendMode: BlendMode, inPlace: bool) = - ## Draws one image onto another using matrix with color blending. - - 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 - matInv = matInv * scale(vec2(0.5, 0.5)) - - proc getRgbaUnsafe(a: Image, x, y: float32): ColorRGBA {.inline.} = - a.getRgbaUnsafe(x.round.int, y.round.int) - - if stepX.length == 1.0 and stepY.length == 1.0 and - mat[2, 0].fractional == 0.0 and mat[2, 1].fractional == 0.0: - #echo "copy non-smooth" - case blendMode - of bmNormal: forBlend(blendNormal, getRgbaUnsafe) - of bmDarken: forBlend(blendDarken, getRgbaUnsafe) - of bmMultiply: forBlend(blendMultiply, getRgbaUnsafe) - of bmLinearBurn: forBlend(blendLinearBurn, getRgbaUnsafe) - of bmColorBurn: forBlend(blendColorBurn, getRgbaUnsafe) - of bmLighten: forBlend(blendLighten, getRgbaUnsafe) - of bmScreen: forBlend(blendScreen, getRgbaUnsafe) - of bmLinearDodge: forBlend(blendLinearDodge, getRgbaUnsafe) - of bmColorDodge: forBlend(blendColorDodge, getRgbaUnsafe) - of bmOverlay: forBlend(blendOverlay, getRgbaUnsafe) - of bmSoftLight: forBlend(blendSoftLight, getRgbaUnsafe) - of bmHardLight: forBlend(blendHardLight, getRgbaUnsafe) - of bmDifference: forBlend(blendDifference, getRgbaUnsafe) - of bmExclusion: forBlend(blendExclusion, getRgbaUnsafe) - of bmHue: forBlend(blendHue, getRgbaUnsafe) - of bmSaturation: forBlend(blendSaturation, getRgbaUnsafe) - of bmColor: forBlend(blendColor, getRgbaUnsafe) - of bmLuminosity: forBlend(blendLuminosity, getRgbaUnsafe) - of bmMask: forBlend(blendMask, getRgbaUnsafe) - of bmOverwrite: forBlend(blendOverwrite, getRgbaUnsafe) - of bmSubtractMask: forBlend(blendSubtractMask, getRgbaUnsafe) - of bmIntersectMask: forBlend(blendIntersectMask, getRgbaUnsafe) - of bmExcludeMask: forBlend(blendExcludeMask, getRgbaUnsafe) - else: - #echo "copy smooth" - 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) diff --git a/src/pixie/images.nim b/src/pixie/images.nim index 21a664a..97e03c6 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -155,29 +155,62 @@ proc magnifyBy2*(image: Image, scale2x: int): Image = proc magnifyBy2*(image: Image): Image = image.magnifyBy2(2) +proc flipHorizontal*(image: Image): Image = + ## Flips the image around the Y axis. + result = newImage(image.width, image.height) + for y in 0 ..< image.height: + for x in 0 ..< image.width: + let rgba = image.getRgbaUnsafe(x, y) + result.setRgbaUnsafe(image.width - x - 1, y, rgba) + +proc flipVertical*(image: Image): Image = + ## Flips the image around the X axis. + result = newImage(image.width, image.height) + for y in 0 ..< image.height: + for x in 0 ..< image.width: + let rgba = image.getRgbaUnsafe(x, y) + result.setRgbaUnsafe(x, image.height - y - 1, rgba) + func lerp(a, b: Color, v: float32): Color {.inline.} = result.r = lerp(a.r, b.r, v) result.g = lerp(a.g, b.g, v) result.b = lerp(a.b, b.b, v) result.a = lerp(a.a, b.a, v) +proc toAlphy*(c: Color): Color = + ## Converts a color to premultiplied alpha from straight. + result.r = c.r * c.a + result.g = c.g * c.a + result.b = c.b * c.a + result.a = c.a + +proc fromAlphy*(c: Color): Color = + ## Converts a color to from premultiplied alpha to straight. + if c.a == 0: + return + result.r = c.r / c.a + result.g = c.g / c.a + result.b = c.b / c.a + result.a = c.a + +proc toAlphy*(image: Image) = + ## Converts an image to premultiplied alpha from straight. + for c in image.data.mitems: + c.g = ((c.r.uint32 * c.a.uint32) div 255).uint8 + c.r = ((c.r.uint32 * c.a.uint32) div 255).uint8 + c.b = ((c.r.uint32 * c.a.uint32) div 255).uint8 + +proc fromAlphy*(image: Image) = + ## Converts an image to from premultiplied alpha to straight. + for c in image.data.mitems: + if c.a == 0: + continue + c.r = ((c.r.int32 * 255) div c.a.int32).uint8 + c.g = ((c.g.int32 * 255) div c.a.int32).uint8 + c.b = ((c.b.int32 * 255) div c.a.int32).uint8 + proc getRgbaSmooth*(image: Image, x, y: float32): ColorRGBA {.inline.} = ## Gets a pixel as (x, y) floats. - - proc toAlphy(c: Color): Color = - result.r = c.r * c.a - result.g = c.g * c.a - result.b = c.b * c.a - result.a = c.a - - proc fromAlphy(c: Color): Color = - if c.a == 0: - return - result.r = c.r / c.a - result.g = c.g / c.a - result.b = c.b / c.a - result.a = c.a - var x = x # TODO: look at maybe +0.5 y = y # TODO: look at maybe +0.5 diff --git a/tests/images/transCompose.png b/tests/images/transCompose.png index 53194e92d8482d9b2b9b677ca420cd5889f7b23e..39553dff2c6632107c70b64b2c04a42421707332 100644 GIT binary patch delta 1613 zcmV-T2D16B4YUoAB!7EJL_t(|0qvYua8*SZfIo7H1c88*5Q=mLP@0GeiSdOJM~CW+ z1u2e*V;O-*#X>{{9t9nBM0k*<&VWeKA%IU10d)`o1!>YD0hEM5NOk_ZiGavWAiI0^ z9`<{>{oXy_{pb8=_y0FiQBhHm<#C3H{1QCQ;DtDb7vdORh?8;#C6j#y3x9I40Jg1$ zwR50-Kd7G^sG?g_$f519@pD)<9Tq$fTfc%oe}>lGq2s+?h;fR}Uyg!>lVSC2*tH(= z4qpA$b?dFv)}qq|_!VpuT)KAQ+XEQNzVLgCqKj+GS>gG+yd5mIsqwtolN9}ta#Z{CF+YvI&B zpu);Ak4b>(uRS5heuJFFu)Hb?mcW4kqaYTg#n3GSQaZ(Yl@ZJdkwHlmtou+!!HRcO z6y!P*1=W$<5_%1(?tfL<(JSQmZWRS`@aIibw-0QE^JldGaN}GGCjHeblcVLQ0C&RCZXXdm9XR!s=-B z3Tb+$n$!ymXAZ;gX;w$0SBP9t?b&$kIzzltRsvnqp=D=lBY)5(#MV=HiKbNq@&%OMmqX5srl{{cYU? zI@w8o%?Occ6IsI!3z0+^@T8T|m=V$}gz4{y*W*}-Tu}Y7N$=8XkVMnpG%M+^nFtZi z<*KTWun?X7HGiM}niG-~!t{5r`SjPE5V@d|LbckIFo1zhvxd z%?pwAS1Qy^)#BPlB0g=bvGmux5aGmb)g}-YT6KfY_Zu0F9U-M9syrwxBuM)El#$Wc z5z@T9s$vfdXAZ%zX_!cV?FfpZ?krBFM>5`aHsdq4d|Dkg%n{ z7xH1q%YWMcUiO4EPEk!cK{es%rXUXnj)6waw0^QZA;O70sxvAqr0P$9?FuO`gUw$M z8ltnmdedLKLYlR&to?PMZA}NK52>9W>Llp+v?z9k$UjQb-)Jvk2q-UuE(0L7E4r{N zM99xmt!-hUQ48q(u(r|I7b5#nZTLvr00JW^8Gr5>rew6eTiegDFGMb=a+U@sI*?~`DJH17Z_XSuO!L%jy-T#z{h#?4h}tn{Y`uxWr0jF6zy z-+wl}A!8hj%7RJrRb!`g+gfgc{y%~dBFJ2j>~CU&+ir)xkMeI6jF<*er)#lyu%-kj zMAF~&2GifRy;SMvXo-Ras=q^;ZXAe$SOg~|xrN$>N!PX;iS<>L>R zhALWoi+(pNQ(}zYIf|6YGk{;^sz9qG78ehs1lqwj)GVODBputJ1_(?r)5q#95TzMKovo>ozyd3iJ*2wq5sej<=L zSRHQH}`=7|o00000 LNkvXXu0mjfmOBWN delta 1615 zcmV-V2C(_G4Xq83B!6^CL_t(|0qvYwa8yMUhQDMIf&u|qLRe(SxUi!F0en%wjnyhe zBw&G9E+z1&pb${eM}#W16tI-I7l?vdY`z#)MMOXW*;f-5g|LQXVx4KM0+pE%y8HHA z`g@vvrsu0u_rJGKcch}Cq9Whp3=#Pyc$~otaSSiSF}x7R@RNH6BY(UQ$M8ZN!wYc? zFXT)KtX~L=r$h5Da9w7gimpu|dp1Df2QY6E%y|;leh#~Tg2p+}va1(joU*J1NAIK1o9uTIN=ZVzAjm}SCSA$xy;!nrVi63iY0Yd(YB>)`ZXm;Z~8?S}y` zT>g0rgh$BPQ}DA?!G9aXs$k)3uxaUks~{d_rI2$MWVMXAB zs$f@wDo8|~Y-raf(W{J+TtZ6E!1}LYiB!Qj_-fjPD%h#-4?1%a?tV(&=UpN03)!~` zRu;e)`S9*2So10D`Vmh3d4)r*BqxfX_v28bP9@)<7x#peo_~c6-+&wo3Zx2Ng^f#~ zc!%DR$D{GBfyG}pgzVo8D+*x26qq#%R())?3KEfC4Z1y&=v77tM#%YdstT6OfP(Sx z}>x5RcV<`)dttyttAC@>Dt|GTj8MbEzrtN{52z_?7TWb3=5Go=swiSXv_$y7sB{E&`$g{BScQ9 zwiSjb{(qKPi@#=s2*-=ne0NyL%!2m)jE}~gkm_NJza?78nZ&mOT!m`dpw%NLh3eyh0}}7sC z=m5j<*SrvkztW*@su$NbCk{jIaAWb;yb$5g7S$RW78>0It?x848aqPD%2d~SSV&F7 zRQ$Cgq<(XdF7~i+bPx2OfQk5PM@S?cwy(0gq`)N6a%j^7vh~MbJ3<6G7|J@2u#njR zI)B`&eKhujgf0G_J`R1JO=2Onv(%!Tu*BbzBhX_Q)T*adioFUE!WMtCbF{}_yF#Mn zu=*21LnNZAL?d>E)N3BHtG~O|(zldUZA$D4k#-u1zpkt$yjr3Xf4f0;TXbPph#*&g z!xVpO>5aekg~)zX%ik0=$&JML&i&Q7NPpAYw2j5S5SdSqwTCXQzD-24j?jA~44Dd} z-%(pRX_O(@7a|qsk8=LezRco;Dg`aiEWrZKo%P%uKG4#(eS9f-C2JTwJH z&r&-&$S!l1_C5(lNc|RSCm$Eqt7lw~s)BKlH&bn3ChOA!*fc;0Mo29Ft|qp8I)4b7 zw1?h#FgPE^%ux$FWwov4CD8vzFhT^m79=}6*r3{V(CGnnEXbPy11G3m8Z53ItSrF^ zk@&mJVEk>`4tfrUA^%jtP_^jBfhveca6)80Vbw?aw%tgt4xR3YzGG2UFi~}@J6Hwr z2u_Ib$2MYnp;Q$)2apbs!INRkdw*)4TY7H|H$CAa|IWgGk~kh)1wOQX{bAU-4J^ zfjiy@eWVJe!{ABkdXY2~gro}M5v&lQ