QinL / grafx2

Automatically exported from code.google.com/p/grafx2
0 stars 0 forks source link

brush rotation/scaling with rotsprite (or other algorithm) #385

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
Hi folks, I came across this site and was pretty amazed to see what progress 
pixel scaling techniques have made: 
http://en.wikipedia.org/wiki/Pixel_art_scaling_algorithms

For animating things, these improvements would cut down the time one needs to 
repixel and correct
pixel graphics...

Original issue reported on code.google.com by eh...@gmx.de on 12 Sep 2010 at 5:19

GoogleCodeExporter commented 8 years ago
RotSprite does look useful. 
Not so sure about scaling algorithms as they tend to produce antialiased 
results.

Original comment by ilija.melentijevic on 4 Dec 2010 at 11:36

GoogleCodeExporter commented 8 years ago
I was studying this (again) recently. RotSprite would indeed be a very valuable 
addition, either as a Lua script or a replacement of the current brush rotate 
tool.
But the program is closed-source, and even with the author's explaination 
(http://forums.sonicretro.org/index.php?showtopic=8848&st=15&p=159754&#entry1597
54) I haven't made much progress.

Original comment by yrizoud on 4 Dec 2010 at 2:35

GoogleCodeExporter commented 8 years ago
I was talking with him on Pixelation forums, and this is what he said. Hope it 
helps, yrizoud. 

Hi. I saw your post here. Just wanted to let you know that I'm willing to 
provide help on this. I won't show the source (honestly, it wouldn't help you 
much anyway) but let me know if you have any specific questions or any code of 
your own that you want me to review.

I believe the only part I left unspecified in my description of the algorithm 
was how exactly the enlargement was performed, i.e. I didn't explain what I 
meant by two colors being "more similar" to each other. You probably found 
something reasonable already (maybe it's better, for all I know), but in case 
you're looking to reproduce what I did, here's how that part worked:

Code:

// rotsprite 2x enlargement algorithm:
// suppose we are looking at input pixel cE which is surrounded by 8 other 
pixels:
//  cA cB cC
//  cD cE cF
//  cG cH cI
// and for that 1 input pixel cE we want to output 4 pixels oA, oB, oC, and oD:
//  oA oB
//  oC oD
// this is the operation performed per pixel:
{
    // I find that this simple measurement of color distance gives better results for sprites than other fancier methods like luminosity comparison
    #define distance(c1, c2) (((c1)==(c2)) ? 0 : abs((c1).red - (c2).red) + abs((c1).green - (c2).green) + abs((c1).blue - (c2).blue))
    // the "similar" macro checks to see if two colors are less different from each other than either is from the current reference color
    #define similar(c1,c2)   ((c1)==(c2) || (distance(c1,c2) <= distance(cE,c2) && distance(c1,c2) <= distance(cE,c1)))
    #define different(c1,c2) (!similar(c1,c2))

    if (different(cD,cF)
     && different(cH,cB)
     && ((similar(cE,cD) || similar(cE,cH) || similar(cE,cF) || similar(cE,cB) ||
         ((different(cA,cI) || similar(cE,cG) || similar(cE,cC)) &&
          (different(cG,cC) || similar(cE,cA) || similar(cE,cI))))))
    {
        oA = ((similar(cB,cD) && ((different(cE,cA) || different(cB,bakcol)) && (different(cE,cA) || different(cE,cI) || different(cB,cC) || different(cD,cG)))) ? cB : cE);
        oB = ((similar(cF,cB) && ((different(cE,cC) || different(cF,bakcol)) && (different(cE,cC) || different(cE,cG) || different(cF,cI) || different(cB,cA)))) ? cF : cE);
        oC = ((similar(cD,cH) && ((different(cE,cG) || different(cD,bakcol)) && (different(cE,cG) || different(cE,cC) || different(cD,cA) || different(cH,cI)))) ? cD : cE);
        oD = ((similar(cH,cF) && ((different(cE,cI) || different(cH,bakcol)) && (different(cE,cI) || different(cE,cA) || different(cH,cG) || different(cF,cC)))) ? cH : cE);
    }
    else
    {
        oA = cE;
        oB = cE;
        oC = cE;
        oD = cE;
    }       
}

Also, for the code you're writing, keep in mind that "scale" should be an input 
parameter in addition to the "angle" (e.g. scale by targetScale/8 instead of 
1/8). This is because the user might want to rotate and scale something, and 
letting the algorithm do both of those things simultaneously gives much 
higher-quality results than doing them separately (because it's a single lossy 
operation instead of two consecutive lossy operations). Occasionally the only 
way to make a sprite look good rotated is to scale it up slightly too... plus 
the scaling is actually pretty good on its own (I often use it just to scale a 
sprite 1.25x without any rotation, for example).

And one trick about actually using this algorithm: there is always a little bit 
of touch-up work required, but the algorithm is chaotic enough that sometimes 
changing the rotation angle a tiny amount (like 0.1 degrees) gives a 
better-looking result. So when I need a really clean looking result at a single 
angle (even if that angle is 0 degrees!) what I do is batch export 16 or so 
variants between -0.1 and 0.1 degrees from that angle and then choose the one 
that looks least-weird as a starting point, instead of trying to use the first 
thing that comes out. It's occurred to me to try getting the algorithm to do 
this automatically too, but I doubt I would be able to code up a good-enough 
definition of "least-weird" for that to be useful.

Original comment by 00a...@gmail.com on 4 Dec 2010 at 9:13

GoogleCodeExporter commented 8 years ago
scale2x and rotsprite doesn't add extra colors, so there's no antialiasing. 
2xSAI and Eagle do that, so they are less practical for us.

Rotate+scale may be good, but not sure about the UI for that...

Original comment by pulkoma...@gmail.com on 8 Dec 2010 at 7:16

GoogleCodeExporter commented 8 years ago
I'm very tempted to implement RotSprite as the new default for "Rotate brush" 
tool. The upscaling is only a one-time cost (when you select the tool) and then 
you can "try" as many angles as you want and the rotation will be as fast as 
before. The current rotation algorithm is already able to upscale/downscale at 
no cost, the only limitation is that the 4 corners of the rotated image are 
"snapped" to the center of their target pixels. For very small images, this 
limitation means that you can't tweak the rotation super-finely. I can't fix 
this because the current code is completely foreign to me.

Rotscale requires 80 times more memory as normal rotation (during upscaling 
from 4x to 8x : 4x4+8*8) If this is a problem, we can put safety limits that 
automatically limit the upscaling to x4 or x2 or even x1.

Original comment by yrizoud on 10 Dec 2010 at 6:20

GoogleCodeExporter commented 8 years ago
Here's attached a Lua script that performs Scale2x rotation (on current brush). 
Works pretty well.

Original comment by yrizoud on 10 Dec 2010 at 11:09

Attachments:

GoogleCodeExporter commented 8 years ago
Awesome, thanks, gonna try this this weekend :)

Original comment by eh...@gmx.de on 11 Dec 2010 at 9:14

GoogleCodeExporter commented 8 years ago

Original comment by pulkoma...@gmail.com on 20 Jan 2011 at 8:58

GoogleCodeExporter commented 8 years ago

Original comment by pulkoma...@gmail.com on 10 Apr 2011 at 8:43

GoogleCodeExporter commented 8 years ago
Arbitering issues that make it to v2.4

Original comment by yrizoud on 8 Mar 2012 at 7:18