 AuthorTopic: [WIP] Rescale&Rotate Tool  (Read 15696 times) lithander

• 0001
• • Posts: 43
• Karma: +0/-0 [WIP] Rescale&Rotate Tool

on: May 15, 2011, 02:15:10 am
The original post is edited beyond recognition

I'd like to to animate a low resolution sprites using a skeleton system. One requirement is to be able to rescale&rotate pixel art. Harder then expected as discussed in this thread

I started to implement a couple of algorithms to experiment. Here is a little tool that allows you to apply different upscaling, transformation and sampling algorithms to pixel art and compare their quality. • For upscaling I implemented the non anti-aliasing algorithms mentioned in the wikipedia article: http://en.wikipedia.org/wiki/Pixel_art_scaling_algorithms
My improved versions aim to solve some artifact-problems and are marked with the suffix HQ.
• For transformation I implemented a standared projective approach that resamples the image based on a homogeneous 3x3 matrix. The visual effect resembles a projection from one plane through a point on another plane.
I also implemented a bilinear transformation that does not preserve lines and just fits the content of the source image into the target quadrilateral.
• Lastly sampling. The transformation yields a coordinate (with fractional part) in the source image for each pixel in the target. Based on that coordinate the sampling algorithms decide on a color to put there.

Sampling methods do NOT preserve the palette:

Smooth bilinear considers the 4 closest pixels assigns them weights (that sum up to 1) and and mixes a new color.

Smooth bicubic works similarily but considers the closest 16 pixels. (Just google bicubic sampling)

Sampling methods DO preserve the palette:

Major bilinear works like smooth bilinear but instead of mixing 4 pixels it choses the one with the biggest weight.

Second bilinear does the same but returns the pixel with the second biggest weight.

Best fit narrow works like Smooth bicubic but instead of returning the calculated color it returns that out of the 4 cloest pixels that match this color best. (best aka least squares of the difference in R, G and B)

Best fit wide does the same but returns the best fit out of the 16 closest pixels.

Best fit Any does the same but returns the best fit out of ALL pixels. (So every color in the palette is considered as an option)

Bilinear Mix is a little crazy. In the first pass it does the same thing as "major bilinear". Then it compares the result with "smooth bicubic" building a difference map. Pixels that are a local maxima in the difference map and where the difference exceeds a certain "Mix Threshold" are replaced with the result of Second bilinear in that location. However, the threshold required for the second best option to be chosen scales with the weight of that second best option.

Usage:

1. Drag a PixelArt image into the program.

2. Chose a Upscaling method.

3. Chose a Transform method. If transformation is enabled you can...

a) Skew the image. Click & Drag edges and corners with your LEFT mouse button
b) Rotate the image. Click & Drag somewhere with your RIGHT mouse button to rotate around that point.

4. Chose a Sampling method. This only takes effect if transformation is enabled.

Last but not least you can...
...adjust the size of the image to your screen with the Zoom slider.
...hold SPACE to compare the current Sampling-Type with the previous one.
...toggle Fullscreen by pressing the F key.
...save the image (as you currently see it) by pressing the S key.

Which combination do you think works best? What other approach could be promising?
« Last Edit: June 16, 2011, 07:58:28 pm by lithander » Mathias

• 0100
•   • • Posts: 1795
• Karma: +2/-0
• im not real Re: [WIP] Rescale&Rotate Tool

Reply #1 on: May 16, 2011, 07:35:14 pm
Just to clarify - interpolation is anytime a computer processes pixels during supersampling and subsampling; changing a pic's size.
Whether or not the interpolation method/algorithm uses anti-aliasing is what's in question.

Nice tool. Think about batching capabilities if you really want to make it useful. Dropping an unknown filetype into the app crashes it. (haven't taken the time to test your algorithms yet)

« Last Edit: May 16, 2011, 07:39:30 pm by Mathias » Atnas Re: [WIP] Rescale&Rotate Tool

Reply #2 on: May 16, 2011, 09:41:02 pm
Very nice program! I ran some of my stuff through it: I think I prefer scale2x the most.

It'd be nice to have a clear image function, so you could load different files in without having to reload the program.  c : lithander

• 0001
• • Posts: 43
• Karma: +0/-0 Re: [WIP] Rescale&Rotate Tool

Reply #3 on: May 16, 2011, 10:24:44 pm
Thanks for giving it a try! I really appreciate it!

@Mathias: I tried to find the correct term for "algorithms that doesn't mess up the color palette" but of course these are interpolating too. Non-antialiasing is better.
And while at the moment I'm more interested in functionality then usability I agree that commandline parameters are great to build automated content pipelines.

But a way to compare and evaluate different upscaling approaches is the sole purpose of the tool in it's current form.

@Atlas: You shouldn't have to restart the application. Just drop another file and it will replace the old one. Cute little sprites you got there, btw! And thanks for taking the time to screenshot! All the algorithms I found and implemented had some small flaws. Like introducing artefacts or making individual pixels disproportional small. Those weren't bugs in my implementation but just how these simple filters work.
I tried to leverage that and added some custom versions that I have marked with a 'HQ' suffix. They take longer to compute (I now display the time the upscaling takes) but I really like the results. Personally I'd say that Scale4x HQ looks best! What do you think?

PixelPunch02.rar

Last but not least I added two Shortcuts:
Holding SPACE will switch to your previously selected Scale-Type so you can easily compare two different algorithms!
Press F-Key to toggle Fullscreen.
« Last Edit: May 16, 2011, 10:28:37 pm by lithander » ErekT Re: [WIP] Rescale&Rotate Tool

Reply #4 on: May 17, 2011, 06:40:52 am
The app doesn't work here, all I get are some grey and white boxes where the text should be. My gfx "card" is extremely poor; gma950, so that's probably why If you're interested, I tried to do my own scaling algorithm some time ago. It's based on the Rotsprite trickery with checking similar pixels instead of just equals, and also some extra step-smoothing vanilla Scale2X lacks. Let me know and I'll post the source here with some explanations (written in Blitzmax). lithander

• 0001
• • Posts: 43
• Karma: +0/-0 Re: [WIP] Rescale&Rotate Tool

Reply #5 on: May 17, 2011, 09:17:15 am
@ErekT: I'd be glad to hear more of your approach, that would be great! The app uses OpenGL to render the screen. That's probably overkill but it's been familiar ground coding-wise so I just used that. I guess gma950 doesn't like non-power-of-two textures. lithander

• 0001
• • Posts: 43
• Karma: +0/-0 Re: [WIP] Rescale&Rotate Tool

Reply #6 on: May 17, 2011, 09:26:56 am
[i messed up editing^^] Fawful

• 0001
• • Posts: 13
• Karma: +0/-0 Re: [WIP] Rescale&Rotate Tool

Reply #7 on: May 17, 2011, 07:23:27 pm
this gives some really nice effects ErekT Re: [WIP] Rescale&Rotate Tool

Reply #8 on: May 18, 2011, 12:03:52 pm
Okay, here's my super-great ScalErekT function. It's written in a Basic language called Blitzmax, which is a really sweet language btw. The code is big and bloated but super-simple, I just go with brute-force tactics all the way. Lots of comments too. But just in case I included some operator and function explanations below.

Ugly it may be but the code works! It smooths better than Scale2X overall imo and also preserves most edges/corners, which helps kill the claymation-effect most pixel-art filters suffer from. The down-side is that it reads two to three times more pixels than Scale2X so it's quite a bit slower also. No biggie if you're not using it for real-time full screen scaling tho.

Some operator differences between C and blitzmax:
'         ==     //
<>     ==     !=
And    ==     &&
Or      ==     ||

Other stuff:

P = PMPixels[pos]       ==     Variable P = pixel at position pos of source image
x2PMPixels[] = P_1     ==     Write pixel value stored in P_1 to output image
Pixmap                      ==     Just a normal bitmap image

And yeah, here's an exe file to check out the effect. Put your image in the same folder as the exe and rename it to test.bmp. Run the exe and press 1 for no scaling, 2 for Scale2X and 3 for ScalErekT:

http://www.mediafire.com/?9prowd8jyydh8sn

And the code:

Quote

' standard Scale2X.

Function Scale2X(Pixmap:TPixmap)

Local Pix_W:Int = PixmapWidth(Pixmap)
Local Pix_H:Int = PixmapHeight(Pixmap)
Local x:Int
Local y:Int

Local ScaledPixmap:TPixmap = CreatePixmap(Pix_W*2,Pix_H*2,PF_RGB888)

Local P:Int
Local A:Int
Local B:Int
Local C:Int
Local D:Int

Local P_1:Int
Local P_2:Int
Local P_3:Int
Local P_4:Int

' go through all pixels in the image
For x = 0 To Pix_W - 1
For y = 0 To Pix_H - 1

' read the source pixel and its surrounding pixels
If x > 0 And x < Pix_W - 1 And y > 0 And y < Pix_H - 1
Else
A = P
B = P
C = P
D = P
EndIf

P_1 = P
P_2 = P
P_3 = P
P_4 = P

' check for equal pixels around P and modify output pixels accordingly
If A = C And B <> C And A <> D Then P_1 = A
If A = B And B <> C And A <> D Then P_2 = B
If D = C And A <> D And B <> C Then P_3 = C
If B = D And B <> C And A <> D Then P_4 = D

WritePixel(ScaledPixmap,x*2,y*2,P_1)
WritePixel(ScaledPixmap,(x*2)+1,y*2,P_2)
WritePixel(ScaledPixmap,x*2,(y*2)+1,P_3)
WritePixel(ScaledPixmap,(x*2)+1,(y*2)+1,P_4)
Next
Next

OutputPixmap = CopyPixmap(ScaledPixmap)

End Function

' My function :]

Function ScalErekT(PM:TPixmap)

Local PMPixels: Int Ptr = Int Ptr(pm.pixels)
Local Pix_W:Int = PixmapWidth(PM)
Local Pix_H:Int = PixmapHeight(PM)
Local x:Int
Local y:Int
Local yt:Int
Local yb:Int
Local xl:Int
Local xr:Int
Local yt2:Int
Local yb2:Int
Local xl2:Int
Local xr2:Int

Local ScaledPM:TPixmap = CreatePixmap(Pix_W*2,Pix_H*2,PF_RGBA8888)
Local x2PMPixels:Int Ptr = Int Ptr (ScaledPM.pixels)

Local x2Pix_W:Int = PixmapWidth(ScaledPM)
Local x2Pix_H:Int = PixmapHeight(ScaledPM)

Local P:Int
Local A:Int
Local B:Int
Local C:Int
Local D:Int
Local FL:Int   ' far left pixel (beside C)
Local TR:Int   ' top right pixel
Local FR:Int   ' far right pixel (beside B)
Local TL:Int   ' top left pixel
Local FT:Int   ' far top pixel
Local FB:Int   ' far bottom pixel
Local BL:Int   ' bottom left pixel
Local BR:Int   ' bottom right pixel

Local P_1:Int   ' output pixel top-left
Local P_2:Int   ' output pixel top-right
Local P_3:Int   ' output pixel bottom-left
Local P_4:Int   ' output pixel bottom-right

' RotSprite vars
Local P_Clr:Int   ' 1 = R, 2 = G, 3 = B
Local A_Clr:Int   ' 1 = R, 2 = G, 3 = B
Local B_Clr:Int   ' 1 = R, 2 = G, 3 = B
Local C_Clr:Int   ' 1 = R, 2 = G, 3 = B
Local D_Clr:Int   ' 1 = R, 2 = G, 3 = B
Local P_Value:Int      ' holds average of RGB values
Local A_Value:Int
Local B_Value:Int
Local C_Value:Int
Local D_Value:Int

' color class vars
Local P_Red:Int = False
Local A_Red:Int = False
Local B_Red:Int = False
Local C_Red:Int = False
Local D_Red:Int = False
Local P_Green:Int = False
Local A_Green:Int = False
Local B_Green:Int = False
Local C_Green:Int = False
Local D_Green:Int = False
Local P_Blue:Int = False
Local A_Blue:Int = False
Local B_Blue:Int = False
Local C_Blue:Int = False
Local D_Blue:Int = False

' go through all pixels in the image
For x = 0 To Pix_W - 1
For y = 0 To Pix_H - 1
yt = (y-1)*Pix_W
yb = (y+1)*Pix_W
yt2 = (y-2)*Pix_W
yb2 = (y+2)*Pix_W
xl2 = (x-2)
xr2 = (x+2)
xl = x-1
xr = x+1
Local yc:Int = y*pix_W
' read the source pixel and its surrounding pixels
P = PMPixels[yc+x]
If x > 0 And x < Pix_W - 1 And y > 0 And y < Pix_H - 1
A = PMPixels[yt+x]
B = PMPixels[yc+xr]
C = PMPixels[yc+xl]
D = PMPixels[yb+x]
Else
A = P
B = P
C = P
D = P
EndIf

P_1 = P
P_2 = P
P_3 = P
P_4 = P

' get colors
P_Clr = (P & \$FF0000) Shr 16   ' red
P_Clr = (P & \$FF00) Shr 8      ' green
P_Clr = (P & \$FF)         ' blue
A_Clr = (A & \$FF0000) Shr 16   ' red
A_Clr = (A & \$FF00) Shr 8      ' green
A_Clr = (A & \$FF)         ' blue
B_Clr = (B & \$FF0000) Shr 16   ' red
B_Clr = (B & \$FF00) Shr 8      ' green
B_Clr = (B & \$FF)         ' blue
C_Clr = (C & \$FF0000) Shr 16   ' red
C_Clr = (C & \$FF00) Shr 8      ' green
C_Clr = (C & \$FF)         ' blue
D_Clr = (D & \$FF0000) Shr 16   ' red
D_Clr = (D & \$FF00) Shr 8      ' green
D_Clr = (D & \$FF)         ' blue

' set average value from RGB
P_Value = (P_Clr + P_Clr + P_Clr) / 3
A_Value = (A_Clr + A_Clr + A_Clr) / 3
B_Value = (B_Clr + B_Clr + B_Clr) / 3
C_Value = (C_Clr + C_Clr + C_Clr) / 3
D_Value = (D_Clr + D_Clr + D_Clr) / 3

' check for equal pixels around P and modify output pixels accordingly
If B <> C And A <> D And (A = C Or A = B Or C = D Or B = D)
' if A = C then check far left and top right pixel to see if it's necessary
' to modify P_2 as well
If A = C
P_1 = A
' check pixel at far left and top right to see if stepping needs easing
If x > 1 Then FL = PMPixels[yc+xl2]
' if FL = C then read another pixel: TR
If FL = C
If x < Pix_W - 1 And y > 0 Then TR = PMPixels[yt+xr]
' If both = A && C then set P_2 to be A as well.
If FL = C And TR = A Then P_2 = A
EndIf

' If FL is C and TR is A then we already know what to smooth here so
' we can skip checking for vertical stepping and save CPU load. But if not
' then do another check for P_3 to see if the bottom left and far top
' pixels are the same.
If FL <> C Or TR <> A
' check pixel at far top and bottom left to see if stepping needs easing
If x > 0 And y < Pix_H - 1 Then BL = PMPixels[yb+xl]
If BL = C
If y > 1 Then FT = PMPixels[yt2+x]
' If both = A && C then set P_3 to be C as well
If BL = C And FT = A Then P_3 = C
EndIf
EndIf

' stepping has been applied but if BL and TL and TR are equal then
' P_1-4 make up a corner so undo stepping
If P_1 = A
' check top left first, if it's equal to A then check bottom left
If x > 0 And y > 0 Then TL = PMPixels[yt+xl]
If TL = A
' TL has been read; this way we don't need to read the pixel twice
' now check bottom left
If x > 0 And y < Pix_H - 1 Then BL = PMPixels[yb+xl]
If BL = A
' BL has been read; this way we don't need to read the pixel twice
' if P_2 = A then we don't need to check TR: it's already checked
If P_2 <> A
If x < Pix_W - 1 And y > 0 Then TR = PMPixels[yt+xr]
EndIf
' now compare them all and see if they make up a corner
' NOTE: checking TR would be enough; the others have been
' ability
If TL = A And BL = A And TR = A
P_1 = P
P_2 = P
EndIf
EndIf
EndIf
EndIf

' if A = B then check far right and top left pixel to see if it's necessary
' to modify P_1 as well
ElseIf A = B
P_2 = B
If x < Pix_W - 2 Then FR = PMPixels[yc+xr2]
If FR = B
If x > 0 And y > 0 Then TL = PMPixels[yt+xl]
If FR = B And TL = A Then P_1 = B
EndIf

If FR <> B Or TL <> A
If x < Pix_W - 1 And y < Pix_H - 1 Then BR = PMPixels[yb+xr]
If BR = B
If y > 1 Then FT = PMPixels[yt2+x]
If BR = B And FT = A Then P_4 = B
EndIf
EndIf

' stepping has been applied but if TL and TR and BR are equal then
' P_1-4 make up a corner so undo stepping
If P_2 = A
' check top right first, if it's equal to B then check bottom right
If x < Pix_W - 1 And y > 0 Then TR = PMPixels[yt+xr]
If TR = A
' now check bottom right
If x < Pix_W - 1 And y < Pix_H - 1 Then BR = PMPixels[yb+xr]
If BR = A
' if P_1 = A then we don't need to check TL: it's already checked
If P_1 <> A
If x > 0 And y > 0 Then TL = PMPixels[yt+xl]
EndIf
' now compare them all and see if they make up a corner
' NOTE: checking TL would be enough; the others have been
' ability
If TR = A And BR = A And TL = A
P_1 = P
P_2 = P
EndIf
EndIf
EndIf
EndIf

' if C = D then check far left and bottom right pixel to see if it's necessary
' to modify P_4 as well
ElseIf C = D
P_3 = C
If x > 1 Then FL = PMPixels[yc+xl2]
If FL = C
If x > 0 And y < Pix_H - 1 Then BR = PMPixels[yb+xr]
If FL = C And BR = D Then P_4 = C
EndIf

If FL <> C Or BR <> D
If x > 0 And y > 0 Then TL = PMPixels[yt+xl]
If TL = C
If y < Pix_H - 2 Then FB = PMPixels[yb2+x]
If TL = C And FB = D Then P_1 = C
EndIf
EndIf

' stepping has been applied but if TL and BL and BR are equal then
' P_1-4 make up a corner so undo stepping
If P_3 = D
' check top left first, if it's equal to A then check bottom left
If x > 0 And y > 0 Then TL = PMPixels[yt+xl]
If TL = D
' now check bottom left
If x > 0 And y < Pix_H - 1 Then BL = PMPixels[yb+xl]
If BL = D
' if P_4 = D then we don't need to check BR: it's already checked
If P_4 <> D
If x > 0 And y < Pix_H - 1 Then BR = PMPixels[yb+xr]
EndIf
' now compare them all and see if they make up a corner
' NOTE: checking BR would be enough; the others have been
' ability
If TL = D And BL = D And BR = D
P_3 = P
P_4 = P
EndIf
EndIf
EndIf
EndIf

' if B = D then check far right and bottom left pixel to see if it's necessary
' to modify P_3 as well
ElseIf B = D
P_4 = D
If x < Pix_W - 2 Then FR = PMPixels[yc+xr2]
If FR = B
If x > 0 And y < Pix_H - 1 Then BL = PMPixels[yb+xl]
If FR = B And BL = D Then P_3 = D
EndIf

If FR <> B Or BL <> D
If x < Pix_W - 1 And y > 0 Then TR = PMPixels[yt+xr]
If TR = B
If y < Pix_H - 2 Then FB = PMPixels[yb2+x]
If TR = B And FB = D Then P_2 = B
EndIf
EndIf

' stepping has been applied but if TR and BR and BL are equal then
' P_1-4 make up a corner so undo stepping
If P_3 = D
' check top right first, if it's equal to D then check bottom right
If x < Pix_W - 1 And y > 0 Then TR = PMPixels[yt+xr]
If TR = D
' now check bottom right
If x < Pix_W - 1 And y < Pix_H - 1 Then BR = PMPixels[yb+xr]
If BR = D
' if P_3 = D then we don't need to check BL: it's already checked
If P_3 <> D
If x > 0 And y < Pix_H - 1 Then BL = PMPixels[yb+xl]
EndIf
' now compare them all and see if they make up a corner
' NOTE: checking BL would be enough; the others have been
' ability
If TR = D And BR = D And BL = D
P_3 = P
P_4 = P
EndIf
EndIf
EndIf
EndIf

EndIf
EndIf

' vars to get value difference between P and B/C/D. Used to compare and set P
' to the value closest to it; prevents "stray" pixel artifacts
Local A_P_Difference:Int
Local B_P_Difference:Int
Local C_P_Difference:Int
Local D_P_Difference:Int

If P_1 <> P And P_1 <> P_3 And P_1 <> P_2   ' compare P_1 and P_3 to make sure there's been no vertical step smoothing
' check which pixel is closer to P: C or A?
If C_Value > P_Value Then C_P_Difference = C_Value - P_Value
If C_Value <= P_Value Then C_P_Difference = P_Value - C_Value
If A_Value > P_Value Then A_P_Difference = A_Value - P_Value
If A_Value <= P_Value Then A_P_Difference = P_Value - A_Value

If C_P_Difference > A_P_Difference
P_1 = A
Else
P_1 = C
EndIf
EndIf
If P_2 <> P And P_2 <> P_4 And P_1 <> P_2   ' compare P_2 and P_4 to make sure there's been no vertical step smoothing
' check which pixel is closer to P: B or A?
If B_Value > P_Value Then B_P_Difference = B_Value - P_Value
If B_Value <= P_Value Then B_P_Difference = P_Value - B_Value
If A_Value > P_Value Then A_P_Difference = A_Value - P_Value
If A_Value <= P_Value Then A_P_Difference = P_Value - A_Value

If B_P_Difference > A_P_Difference
P_2 = A
Else
P_2 = B
EndIf
EndIf
If P_3 <> P And P_3 <> P_1 And P_4 <> P_3   ' compare P_3 and P_1 to make sure there's been no vertical step smoothing
' check which pixel is closer to P: C or D?
If C_Value > P_Value Then C_P_Difference = C_Value - P_Value
If C_Value <= P_Value Then C_P_Difference = P_Value - C_Value
If D_Value > P_Value Then D_P_Difference = D_Value - P_Value
If D_Value <= P_Value Then D_P_Difference = P_Value - D_Value

If C_P_Difference > D_P_Difference
P_3 = D
Else
P_3 = C
EndIf
EndIf
If P_4 <> P And P_4 <> P_2 And P_4 <> P_3   ' compare P_4 and P_2 to make sure there's been no vertical step smoothing
' check which pixel is closer to P: B or D?
If B_Value > P_Value Then B_P_Difference = B_Value - P_Value
If B_Value <= P_Value Then B_P_Difference = P_Value - B_Value
If D_Value > P_Value Then D_P_Difference = D_Value - P_Value
If D_Value <= P_Value Then D_P_Difference = P_Value - D_Value

If B_P_Difference > D_P_Difference
P_4 = D
Else
P_4 = B
EndIf
EndIf

' if equal pixel conditions aren't met then check for similar pixels instead:

If P_1 = P And C <> A   ' P_3 is unmodified and C & D are similar but not equal
' if C is similar to A and very different from P then turn P_1 into either C or A
If A_Value < C_Value + 40 And A_Value > C_Value - 40 And ((P_Value >= C_Value + 40 Or P_Value <= C_Value - 40) Or (P_Value >= A_Value + 40 Or P_Value <= A_Value - 40)) And C <> D And B <> A
If A <> D And B <> C And P_3 <> D And P_4 <> D And P_3 <> C And P_4 <> B
' check top-left pixel; only modify P_1 if TL <> P (no diagonal line from TL to P)
If x > 0 And y > 0 Then TL = PMPixels[yt+xl]
If TL <> P
' check BL and TR -> if they're the same as TL then P is a
' corner = don't modify
If x > 0 And y < Pix_H - 1 Then BL = PMPixels[yb+xl]
' TL = BL -> OK to check TR as well
If TL = BL
If x < Pix_W - 1 And y > 0 Then TR = PMPixels[yt+xr]
EndIf
' if they're all the same then leave P_3 as-is
If TL = BL And TL = TR
P_1 = P
Else
' check which pixel is closer to P: C or A?
If C_Value > P_Value Then C_P_Difference = C_Value - P_Value
If C_Value <= P_Value Then C_P_Difference = P_Value - C_Value
If A_Value > P_Value Then A_P_Difference = A_Value - P_Value
If A_Value <= P_Value Then A_P_Difference = P_Value - A_Value

If C_P_Difference > A_P_Difference
P_1 = A
Else
P_1 = C
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf

If P_2 = P And A <> B   ' P_2 is unmodified and A & B are similar but not equal
' if D is similar to B and very different from P then turn P_4 into either B or D
If B_Value < A_Value + 40 And B_Value > A_Value - 40 And ((P_Value >= A_Value + 40 Or P_Value <= A_Value - 40) Or (P_Value >= B_Value + 40 Or P_Value <= B_Value - 40)) And C <> A And D <> B
If B <> C And A <> D And P_3 <> C And P_4 <> D And P_3 <> D And P_4 <> B
' check top-right pixel; only modify P_2 if TR <> P (no diagonal line from TR to P)
If x < Pix_W - 1 And y > 0 Then TR = PMPixels[yt+xr]
If BR <> P   ' old: If BR <> P Then P_4 = B
' check BR and TL -> if they're the same as TR then P is a
' corner = don't modify
If x < Pix_W - 1 And y < Pix_H - 1 Then BR = PMPixels[yb+xr]
' TR = BR -> OK to check TL as well
If TR = BR
If x > 0 And y > 0 Then TL = PMPixels[yt+xl]
EndIf
' if they're all the same then leave P_2 as-is
If TR = BR And TR = TL
P_2 = P
Else
' check which pixel is closer to P: B or A?
If B_Value > P_Value Then B_P_Difference = B_Value - P_Value
If B_Value <= P_Value Then B_P_Difference = P_Value - B_Value
If A_Value > P_Value Then A_P_Difference = A_Value - P_Value
If A_Value <= P_Value Then A_P_Difference = P_Value - A_Value

If B_P_Difference > A_P_Difference
P_2 = A
Else
P_2 = B
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf

If P_3 = P And C <> D   ' P_3 is unmodified and C & D are similar but not equal
' if C is similar to D and very different from P then turn P_3 into either C or D
If D_Value < C_Value + 40 And D_Value > C_Value - 40 And ((P_Value >= C_Value + 40 Or P_Value <= C_Value - 40) Or (P_Value >= D_Value + 40 Or P_Value <= D_Value - 40)) And B <> D And C <> A
If A <> D And B <> C And P_1 <> C And P_2 <> A And P_1 <> A And P_2 <> B
' check bottom-left pixel; only modify P_3 if BL <> P (no diagonal line from BL to P)
If x > 0 And y < Pix_H - 1 Then BL = PMPixels[yb+xl]
If BL <> P   ' old: If BL <> P Then P_3 = D
' check TL and BR -> if they're the same as BL then P is a
' corner = don't modify
If x > 0 And y > 0 Then TL = PMPixels[yt+xl]
' TL = BL -> OK to check BR as well
If TL = BL
If x < Pix_W - 1 And y < Pix_H - 1 Then BR = PMPixels[yb+xr]
EndIf
' if they're all the same then leave P_3 as-is
If TL = BL And TL = BR
P_3 = P
Else
' check which pixel is closer to P: C or D?
If C_Value > P_Value Then C_P_Difference = C_Value - P_Value
If C_Value <= P_Value Then C_P_Difference = P_Value - C_Value
If D_Value > P_Value Then D_P_Difference = D_Value - P_Value
If D_Value <= P_Value Then D_P_Difference = P_Value - D_Value

If C_P_Difference > D_P_Difference
P_3 = D
Else
P_3 = C
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf

If P_4 = P And B <> D   ' P_4 is unmodified and B & D are similar but not equal
' if D is similar to B and very different from P then turn P_4 into either B or D
If B_Value < D_Value + 40 And B_Value > D_Value - 40 And ((P_Value >= D_Value + 40 Or P_Value <= D_Value - 40) Or (P_Value >= B_Value + 40 Or P_Value <= B_Value - 40)) And B <> A And D <> C
If B <> C And A <> D And P_1 <> C And P_2 <> A And P_1 <> A And P_2 <> B
' check bottom-right pixel; only modify P_4 if BR <> P (no diagonal line from BR to P)
If x < Pix_W - 1 And y < Pix_H - 1 Then BR = PMPixels[yb+xr]
If BR <> P   ' old: If BR <> P Then P_4 = B
' check TR and BL -> if they're the same as BR then P is a
' corner = don't modify
If x < Pix_W - 1 And y > 0 Then TR = PMPixels[yt+xr]
' TR = BR -> OK to check BL as well
If TR = BR
If x > 0 And y < Pix_H - 1 Then BL = PMPixels[yb+xl]
EndIf
' if they're all the same then leave P_4 as-is
If TR = BR And TR = BL
P_4 = P
Else
' check which pixel is closer to P: B or D?
If B_Value > P_Value Then B_P_Difference = B_Value - P_Value
If B_Value <= P_Value Then B_P_Difference = P_Value - B_Value
If D_Value > P_Value Then D_P_Difference = D_Value - P_Value
If D_Value <= P_Value Then D_P_Difference = P_Value - D_Value

If B_P_Difference > D_P_Difference
P_4 = D
Else
P_4 = B
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf

' Write pixels to output pixmap
x2PMPixels[(y*2  )*ScaledPM.Width+(x*2  )] = P_1
x2PMPixels[(y*2  )*ScaledPM.width+(x*2+1)] = P_2
x2PMPixels[(y*2+1)*ScaledPM.width+(x*2  )] = P_3
x2PMPixels[(y*2+1)*ScaledPM.Width+(x*2+1)] = P_4
Next
Next

OutputPixmap = ScaledPM

End Function

Hope it's useful, if you're wondering about anything just let me know « Last Edit: May 18, 2011, 12:12:38 pm by ErekT » Ai

• 0100
•   • Posts: 1057
• Karma: +2/-0
• finti Re: [WIP] Rescale&Rotate Tool

Reply #9 on: May 18, 2011, 12:41:56 pm
Your method is interesting. Can you post some example before+after images, for those who cannot run ".exe" files?
If you insist on being pessimistic about your own abilities, consider also being pessimistic about the accuracy of that pessimistic judgement.