Code archives/Algorithms/ScaleEX: Scaling filter for 2D pixel art

This code has been declared by its author to be Public Domain code.

Download source code

ScaleEX: Scaling filter for 2D pixel art by ErikT2010
Scaling filter ala Scale2X, 2xSAI, SuperEagle, HQ2X etc. Great for scaling pixel art or other low-color/cartoony imagery. There are three versions: two of them (relatively) fast and one slow. Try'em all out, the slow one is generally better but this varies in some cases. Inspired by the EPX/Scale2X algorithms and Xenowhirl's and Screaming Duck's expanded versions. Big thanks to Jesse for optimising the code.

Usage:
For preloading images use it like this in your loading function:

P.Pixmap = LoadPixmap("ImagePacks/Hero_Pack1.png")
ScaleEX(P.Pixmap)
P.Sheet1 = LoadImage(OutputPixmap)

If you want to try it in realtime then do something like this in the main loop after all your screen drawing is complete:
Local scr:TPixmap = GrabPixmap(0,0,WIDTH,HEIGHT)

ScaleEX(scr)
scaledscreen = LoadImage(Outputpixmap)
DrawImage(scaledscreen,0,0)

Flip


It's too slow for realtime screenscaling for now, but that *might* change if I ever figure out how to convert the filter to a shader.
SuperStrict

Function ScaleEX(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[4]	' 1 = R, 2 = G, 3 = B
	Local A_Clr:Int[4]	' 1 = R, 2 = G, 3 = B
	Local B_Clr:Int[4]	' 1 = R, 2 = G, 3 = B
	Local C_Clr:Int[4]	' 1 = R, 2 = G, 3 = B
	Local D_Clr:Int[4]	' 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
	
	' 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[1] = (P & $FF0000) Shr 16	' red
			P_Clr[2] = (P & $FF00) Shr 8		' green
			P_Clr[3] = (P & $FF)			' blue
			A_Clr[1] = (A & $FF0000) Shr 16	' red
			A_Clr[2] = (A & $FF00) Shr 8		' green
			A_Clr[3] = (A & $FF)			' blue
			B_Clr[1] = (B & $FF0000) Shr 16	' red
			B_Clr[2] = (B & $FF00) Shr 8		' green
			B_Clr[3] = (B & $FF)			' blue
			C_Clr[1] = (C & $FF0000) Shr 16	' red
			C_Clr[2] = (C & $FF00) Shr 8		' green
			C_Clr[3] = (C & $FF)			' blue
			D_Clr[1] = (D & $FF0000) Shr 16	' red
			D_Clr[2] = (D & $FF00) Shr 8		' green
			D_Clr[3] = (D & $FF)			' blue

			' set average value from RGB
			P_Value = (P_Clr[1] + P_Clr[2] + P_Clr[3]) / 3
			A_Value = (A_Clr[1] + A_Clr[2] + A_Clr[3]) / 3
			B_Value = (B_Clr[1] + B_Clr[2] + B_Clr[3]) / 3
			C_Value = (C_Clr[1] + C_Clr[2] + C_Clr[3]) / 3
			D_Value = (D_Clr[1] + D_Clr[2] + D_Clr[3]) / 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

				' 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

				' 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

				' 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
				EndIf
			EndIf


			' if equal pixel conditions aren't met then check for similar pixels instead:
			If P_1 = P And A <> C	' P_1 is unmodified and A & C are similar but not equal
				' if A is similar to C and very different from P then turn P_1 into C
				If A_Value < C_Value + 40 And A_Value > C_Value - 40 And (P_Value >= A_Value + 40 Or P_Value <= A_Value - 40) And B <> D And A <> B
					If A <> D And B <> C Then P_1 = C
				EndIf
			EndIf
			If P_2 = P And A <> B	' P_2 is unmodified and A & B are similar but not equal
				' if B is similar to A and very different from P then turn P_2 into A
				If A_Value < B_Value + 40 And A_Value > B_Value - 40 And (P_Value >= B_Value + 40 Or P_Value <= B_Value - 40) And A <> C And B <> D
					If B <> C And A <> D Then P_2 = A
				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 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) And B <> D And C <> A
					If A <> D And B <> C Then P_3 = D
				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 B
				If B_Value < D_Value + 40 And B_Value > D_Value - 40 And (P_Value >= D_Value + 40 Or P_Value <= D_Value - 40) And B <> A And D <> C
					If B <> C And A <> D Then P_4 = B
				EndIf
			EndIf

			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



' reads more pixels in some cases - smoother results and less artifacts at roughly the same speed
Function ScaleEX_HQ(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[4]	' 1 = R, 2 = G, 3 = B
	Local A_Clr:Int[4]	' 1 = R, 2 = G, 3 = B
	Local B_Clr:Int[4]	' 1 = R, 2 = G, 3 = B
	Local C_Clr:Int[4]	' 1 = R, 2 = G, 3 = B
	Local D_Clr:Int[4]	' 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
	
	' 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[1] = (P & $FF0000) Shr 16	' red
			P_Clr[2] = (P & $FF00) Shr 8		' green
			P_Clr[3] = (P & $FF)			' blue
			A_Clr[1] = (A & $FF0000) Shr 16	' red
			A_Clr[2] = (A & $FF00) Shr 8		' green
			A_Clr[3] = (A & $FF)			' blue
			B_Clr[1] = (B & $FF0000) Shr 16	' red
			B_Clr[2] = (B & $FF00) Shr 8		' green
			B_Clr[3] = (B & $FF)			' blue
			C_Clr[1] = (C & $FF0000) Shr 16	' red
			C_Clr[2] = (C & $FF00) Shr 8		' green
			C_Clr[3] = (C & $FF)			' blue
			D_Clr[1] = (D & $FF0000) Shr 16	' red
			D_Clr[2] = (D & $FF00) Shr 8		' green
			D_Clr[3] = (D & $FF)			' blue

			' set average value from RGB
			P_Value = (P_Clr[1] + P_Clr[2] + P_Clr[3]) / 3
			A_Value = (A_Clr[1] + A_Clr[2] + A_Clr[3]) / 3
			B_Value = (B_Clr[1] + B_Clr[2] + B_Clr[3]) / 3
			C_Value = (C_Clr[1] + C_Clr[2] + C_Clr[3]) / 3
			D_Value = (D_Clr[1] + D_Clr[2] + D_Clr[3]) / 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

				' 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

				' 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

				' 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
				EndIf
			EndIf


			' if equal pixel conditions aren't met then check for similar pixels instead:
			If P_1 = P And A <> C	' P_1 is unmodified and A & C are similar but not equal
				' if A is similar to C and very different from P then turn P_1 into C
				If A_Value < C_Value + 40 And A_Value > C_Value - 40 And (P_Value >= A_Value + 40 Or P_Value <= A_Value - 40) And B <> D And A <> B
					If A <> D And B <> C
						' 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 Then P_1 = C
					EndIf
				EndIf
			EndIf
			If P_2 = P And A <> B	' P_2 is unmodified and A & B are similar but not equal
				' if B is similar to A and very different from P then turn P_2 into A
				If A_Value < B_Value + 40 And A_Value > B_Value - 40 And (P_Value >= B_Value + 40 Or P_Value <= B_Value - 40) And A <> C And B <> D
					If B <> C And A <> D
						' 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 TR <> P Then P_2 = A
					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 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) And B <> D And C <> A
					If A <> D And B <> C
						' 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 Then P_3 = D
					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 B
				If B_Value < D_Value + 40 And B_Value > D_Value - 40 And (P_Value >= D_Value + 40 Or P_Value <= D_Value - 40) And B <> A And D <> C
					If B <> C And A <> D
						' 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 Then P_4 = B
					EndIf
				EndIf
			EndIf

			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



' smooths pixels by color groups instead of just RGB averages,
' generally smoother and less artifacty than ScaleEX_HQ but MUCH slower.
Function ScaleEX_SQ(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[4]	' 1 = R, 2 = G, 3 = B
	Local A_Clr:Int[4]	' 1 = R, 2 = G, 3 = B
	Local B_Clr:Int[4]	' 1 = R, 2 = G, 3 = B
	Local C_Clr:Int[4]	' 1 = R, 2 = G, 3 = B
	Local D_Clr:Int[4]	' 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

	' Sort by color
	Local Red:Int = 1
	Local Orange:Int = 2
	Local Yellow:Int = 3
	Local Army:Int = 4
	Local Green:Int = 5
	Local Jade:Int = 6
	Local Cyan:Int = 7
	Local Azure:Int = 8
	Local Blue:Int = 9
	Local Indigo:Int = 10
	Local Purple:Int = 11
	Local Magenta:Int = 12
	Local Grey:Int = 20
	Local P_Class:Int
	Local A_Class:Int
	Local B_Class:Int
	Local C_Class:Int
	Local D_Class:Int


	' 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[1] = (P & $FF0000) Shr 16	' red
			P_Clr[2] = (P & $FF00) Shr 8		' green
			P_Clr[3] = (P & $FF)			' blue
			A_Clr[1] = (A & $FF0000) Shr 16	' red
			A_Clr[2] = (A & $FF00) Shr 8		' green
			A_Clr[3] = (A & $FF)			' blue
			B_Clr[1] = (B & $FF0000) Shr 16	' red
			B_Clr[2] = (B & $FF00) Shr 8		' green
			B_Clr[3] = (B & $FF)			' blue
			C_Clr[1] = (C & $FF0000) Shr 16	' red
			C_Clr[2] = (C & $FF00) Shr 8		' green
			C_Clr[3] = (C & $FF)			' blue
			D_Clr[1] = (D & $FF0000) Shr 16	' red
			D_Clr[2] = (D & $FF00) Shr 8		' green
			D_Clr[3] = (D & $FF)			' blue

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

			' get color class
			' red
			If A_Clr[1] > A_Clr[2] + 21 And A_Clr[1] > A_Clr[3] + 21 And (A_Clr[2] < A_Clr[3] + 21 And A_Clr[2] > A_Clr[3] - 21) Then A_Class = Red
			' orange
			If A_Clr[1] > A_Clr[3] + 21 And A_Clr[2] <= A_Clr[1] - 21 And A_Clr[2] >= A_Clr[3] + 21 Then A_Class = Orange
			' yellow
			If A_Clr[1] <= A_Clr[2] + 21 And A_Clr[1] >= A_Clr[2] - 21 And A_Clr[1] >= A_Clr[3] + 21 Then A_Class = Yellow
			' army green
			If A_Clr[2] > A_Clr[3] + 21 And A_Clr[1] <= A_Clr[2] - 21 And A_Clr[1] >= A_Clr[3] + 21 Then A_Class = Army
			' green
			If A_Clr[2] > A_Clr[1] + 21 And A_Clr[2] > A_Clr[3] + 21 And (A_Clr[1] < A_Clr[3] + 21 And A_Clr[1] > A_Clr[3] - 21) Then A_Class = Green
			' jade green
			If A_Clr[2] > A_Clr[1] + 21 And A_Clr[3] <= A_Clr[2] - 21 And A_Clr[3] >= A_Clr[1] + 21 Then A_Class = Jade
			' Cyan
			If A_Clr[2] <= A_Clr[3] + 21 And A_Clr[2] >= A_Clr[3] - 21 And A_Clr[3] >= A_Clr[1] + 21 Then A_Class = Cyan
			' Azure blue
			If A_Clr[3] > A_Clr[1] + 21 And A_Clr[2] <= A_Clr[3] - 21 And A_Clr[2] >= A_Clr[1] + 21 Then A_Class = Azure
			' blue
			If A_Clr[3] > A_Clr[2] + 21 And A_Clr[3] > A_Clr[1] + 21 And (A_Clr[1] < A_Clr[2] + 21 And A_Clr[1] > A_Clr[2] - 21) Then A_Class = Blue
			' indigo
			If A_Clr[3] > A_Clr[2] + 21 And A_Clr[1] <= A_Clr[3] - 21 And A_Clr[1] >= A_Clr[2] + 21 Then A_Class = Indigo
			' purple
			If A_Clr[1] <= A_Clr[3] + 21 And A_Clr[1] >= A_Clr[3] - 21 And A_Clr[1] >= A_Clr[2] + 21 Then A_Class = Purple
			' magenta
			If A_Clr[1] > A_Clr[2] + 21 And A_Clr[3] <= A_Clr[1] - 21 And A_Clr[3] >= A_Clr[2] + 21 Then A_Class = Magenta
			' greyscales (includes desaturated colors)
			If A_Clr[1] <= A_Clr[2] + 21 And A_Clr[1] >= A_Clr[2] - 21 And A_Clr[1] <= A_Clr[3] + 21 And A_Clr[1] >= A_Clr[3] - 21 Then A_Class = Grey

			' red
			If B_Clr[1] > B_Clr[2] + 21 And B_Clr[1] > B_Clr[3] + 21 And (B_Clr[2] < B_Clr[3] + 21 And B_Clr[2] > B_Clr[3] - 21) Then B_Class = Red
			' orange
			If B_Clr[1] > B_Clr[3] + 21 And B_Clr[2] <= B_Clr[1] - 21 And B_Clr[2] >= B_Clr[3] + 21 Then B_Class = Orange
			' yellow
			If B_Clr[1] <= B_Clr[2] + 21 And B_Clr[1] >= B_Clr[2] - 21 And B_Clr[1] >= B_Clr[3] + 21 Then B_Class = Yellow
			' army green
			If B_Clr[2] > B_Clr[3] + 21 And B_Clr[1] <= B_Clr[2] - 21 And B_Clr[1] >= B_Clr[3] + 21 Then B_Class = Army
			' green
			If B_Clr[2] > B_Clr[1] + 21 And B_Clr[2] > B_Clr[3] + 21 And (B_Clr[1] < B_Clr[3] + 21 And B_Clr[1] > B_Clr[3] - 21) Then B_Class = Green
			' jade green
			If B_Clr[2] > B_Clr[1] + 21 And B_Clr[3] <= B_Clr[2] - 21 And B_Clr[3] >= B_Clr[1] + 21 Then B_Class = Jade
			' Cyan
			If B_Clr[2] <= B_Clr[3] + 21 And B_Clr[2] >= B_Clr[3] - 21 And B_Clr[3] >= B_Clr[1] + 21 Then B_Class = Cyan
			' Azure blue
			If B_Clr[3] > B_Clr[1] + 21 And B_Clr[2] <= B_Clr[3] - 21 And B_Clr[2] >= B_Clr[1] + 21 Then B_Class = Azure
			' blue
			If B_Clr[3] > B_Clr[2] + 21 And B_Clr[3] > B_Clr[1] + 21 And (B_Clr[1] < B_Clr[2] + 21 And B_Clr[1] > B_Clr[2] - 21) Then B_Class = Blue
			' indigo
			If B_Clr[3] > B_Clr[2] + 21 And B_Clr[1] <= B_Clr[3] - 21 And B_Clr[1] >= B_Clr[2] + 21 Then B_Class = Indigo
			' purple
			If B_Clr[1] <= B_Clr[3] + 21 And B_Clr[1] >= B_Clr[3] - 21 And B_Clr[1] >= B_Clr[2] + 21 Then B_Class = Purple
			' magenta
			If B_Clr[1] > B_Clr[2] + 21 And B_Clr[3] <= B_Clr[1] - 21 And B_Clr[3] >= B_Clr[2] + 21 Then B_Class = Magenta
			' greyscales (includes desaturated colors)
			If B_Clr[1] <= B_Clr[2] + 21 And B_Clr[1] >= B_Clr[2] - 21 And B_Clr[1] <= B_Clr[3] + 21 And B_Clr[1] >= B_Clr[3] - 21 Then B_Class = Grey

			' red
			If C_Clr[1] > C_Clr[2] + 21 And C_Clr[1] > C_Clr[3] + 21 And (C_Clr[2] < C_Clr[3] + 21 And C_Clr[2] > C_Clr[3] - 21) Then C_Class = Red
			' orange
			If C_Clr[1] > C_Clr[3] + 21 And C_Clr[2] <= C_Clr[1] - 21 And C_Clr[2] >= C_Clr[3] + 21 Then C_Class = Orange
			' yellow
			If C_Clr[1] <= C_Clr[2] + 21 And C_Clr[1] >= C_Clr[2] - 21 And C_Clr[1] >= C_Clr[3] + 21 Then C_Class = Yellow
			' army green
			If C_Clr[2] > C_Clr[3] + 21 And C_Clr[1] <= C_Clr[2] - 21 And C_Clr[1] >= C_Clr[3] + 21 Then C_Class = Army
			' green
			If C_Clr[2] > C_Clr[1] + 21 And C_Clr[2] > C_Clr[3] + 21 And (C_Clr[1] < C_Clr[3] + 21 And C_Clr[1] > C_Clr[3] - 21) Then C_Class = Green
			' jade green
			If C_Clr[2] > C_Clr[1] + 21 And C_Clr[3] <= C_Clr[2] - 21 And C_Clr[3] >= C_Clr[1] + 21 Then C_Class = Jade
			' Cyan
			If C_Clr[2] <= C_Clr[3] + 21 And C_Clr[2] >= C_Clr[3] - 21 And C_Clr[3] >= C_Clr[1] + 21 Then C_Class = Cyan
			' Azure blue
			If C_Clr[3] > C_Clr[1] + 21 And C_Clr[2] <= C_Clr[3] - 21 And C_Clr[2] >= C_Clr[1] + 21 Then C_Class = Azure
			' blue
			If C_Clr[3] > C_Clr[2] + 21 And C_Clr[3] > C_Clr[1] + 21 And (C_Clr[1] < C_Clr[2] + 21 And C_Clr[1] > C_Clr[2] - 21) Then C_Class = Blue
			' indigo
			If C_Clr[3] > C_Clr[2] + 21 And C_Clr[1] <= C_Clr[3] - 21 And C_Clr[1] >= C_Clr[2] + 21 Then C_Class = Indigo
			' purple
			If C_Clr[1] <= C_Clr[3] + 21 And C_Clr[1] >= C_Clr[3] - 21 And C_Clr[1] >= C_Clr[2] + 21 Then C_Class = Purple
			' magenta
			If C_Clr[1] > C_Clr[2] + 21 And C_Clr[3] <= C_Clr[1] - 21 And C_Clr[3] >= C_Clr[2] + 21 Then C_Class = Magenta
			' greyscales (includes desaturated colors)
			If C_Clr[1] <= C_Clr[2] + 21 And C_Clr[1] >= C_Clr[2] - 21 And C_Clr[1] <= C_Clr[3] + 21 And C_Clr[1] >= C_Clr[3] - 21 Then C_Class = Grey

			' red
			If D_Clr[1] > D_Clr[2] + 21 And D_Clr[1] > D_Clr[3] + 21 And (D_Clr[2] < D_Clr[3] + 21 And D_Clr[2] > D_Clr[3] - 21) Then D_Class = Red
			' orange
			If D_Clr[1] > D_Clr[3] + 21 And D_Clr[2] <= D_Clr[1] - 21 And D_Clr[2] >= D_Clr[3] + 21 Then D_Class = Orange
			' yellow
			If D_Clr[1] <= D_Clr[2] + 21 And D_Clr[1] >= D_Clr[2] - 21 And D_Clr[1] >= D_Clr[3] + 21 Then D_Class = Yellow
			' army green
			If D_Clr[2] > D_Clr[3] + 21 And D_Clr[1] <= D_Clr[2] - 21 And D_Clr[1] >= D_Clr[3] + 21 Then D_Class = Army
			' green
			If D_Clr[2] > D_Clr[1] + 21 And D_Clr[2] > D_Clr[3] + 21 And (D_Clr[1] < D_Clr[3] + 21 And D_Clr[1] > D_Clr[3] - 21) Then D_Class = Green
			' jade green
			If D_Clr[2] > D_Clr[1] + 21 And D_Clr[3] <= D_Clr[2] - 21 And D_Clr[3] >= D_Clr[1] + 21 Then D_Class = Jade
			' Cyan
			If D_Clr[2] <= D_Clr[3] + 21 And D_Clr[2] >= D_Clr[3] - 21 And D_Clr[3] >= D_Clr[1] + 21 Then D_Class = Cyan
			' Azure blue
			If D_Clr[3] > D_Clr[1] + 21 And D_Clr[2] <= D_Clr[3] - 21 And D_Clr[2] >= D_Clr[1] + 21 Then D_Class = Azure
			' blue
			If D_Clr[3] > D_Clr[2] + 21 And D_Clr[3] > D_Clr[1] + 21 And (D_Clr[1] < D_Clr[2] + 21 And D_Clr[1] > D_Clr[2] - 21) Then D_Class = Blue
			' indigo
			If D_Clr[3] > D_Clr[2] + 21 And D_Clr[1] <= D_Clr[3] - 21 And D_Clr[1] >= D_Clr[2] + 21 Then D_Class = Indigo
			' purple
			If D_Clr[1] <= D_Clr[3] + 21 And D_Clr[1] >= D_Clr[3] - 21 And D_Clr[1] >= D_Clr[2] + 21 Then D_Class = Purple
			' magenta
			If D_Clr[1] > D_Clr[2] + 21 And D_Clr[3] <= D_Clr[1] - 21 And D_Clr[3] >= D_Clr[2] + 21 Then D_Class = Magenta
			' greyscales (includes desaturated colors)
			If D_Clr[1] <= D_Clr[2] + 21 And D_Clr[1] >= D_Clr[2] - 21 And D_Clr[1] <= D_Clr[3] + 21 And D_Clr[1] >= D_Clr[3] - 21 Then D_Class = Grey



			' 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

				' 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

				' 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

				' 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
				EndIf
			EndIf




			' if equal pixel conditions aren't met then check for similar pixels instead:
			If P_1 = P And A <> C	' P_1 is unmodified and A & C are similar but not equal
				' if A is similar to C and very different from P then turn P_1 into C
				If A_Value < C_Value + 70 And A_Value > C_Value - 70 And ((P_Value >= A_Value + 70 Or P_Value <= A_Value - 70) Or (P_Value >= C_Value + 70 Or P_Value <= C_Value - 70)) And B <> D And A <> B
					If A <> D And B <> C And (A_Class <= C_Class + 1 And (A_Class >= C_Class - 1 And C_Class >= 1))	' same color?
						' 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 Then P_1 = C
					EndIf
				EndIf
			EndIf
			If P_2 = P And A <> B	' P_2 is unmodified and A & B are similar but not equal
				' if B is similar to A and very different from P then turn P_2 into A
				If A_Value < B_Value + 70 And A_Value > B_Value - 70 And ((P_Value >= B_Value + 70 Or P_Value <= B_Value - 70) Or (P_Value >= B_Value + 70 Or P_Value <= B_Value - 70)) And A <> C And B <> D
					If B <> C And A <> D And (A_Class <= B_Class + 1 And (A_Class >= B_Class - 1 And B_Class >= 1))	' same color?
						' 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 TR <> P Then P_2 = A
					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 D
				If D_Value < C_Value + 70 And D_Value > C_Value - 70 And ((P_Value >= C_Value + 70 Or P_Value <= C_Value - 70) Or (P_Value >= D_Value + 70 Or P_Value <= D_Value - 70)) And B <> D And C <> A
					If A <> D And B <> C And P_1 <> C And P_2 <> A And (C_Class <= D_Class + 1 And (C_Class >= D_Class - 1 And D_Class >= 1))	' same color?
						' 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 Then P_3 = D
					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 B
				If B_Value < D_Value + 70 And B_Value > D_Value - 70 And ((P_Value >= D_Value + 70 Or P_Value <= D_Value - 70) Or (P_Value >= B_Value + 70 Or P_Value <= B_Value - 70)) And B <> A And D <> C
					If B <> C And A <> D And P_1 <> C And P_2 <> A And (B_Class <= D_Class + 1 And (B_Class >= D_Class - 1 And D_Class >= 1))	' same color?
						' 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 Then P_4 = B
					EndIf
				EndIf
			EndIf


			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




Global InputPixmap:TPixmap = LoadPixmap("input.bmp")	' alpha'ed pngs also work
inputpixmap = inputPixmap.convert(PF_RGBA8888)
Global OutputPixmap:TPixmap

Global TestImg:TImage



Graphics 800,600,32

Local secs:Int= MilliSecs()
ScaleEX(InputPixmap)
Print MilliSecs() - secs
TestImg = LoadImage(OutputPixmap)


While Not KeyDown(KEY_ESCAPE)

	Cls
		DrawImage(TestImg,0,0)
	Flip

Wend

End

Comments

Panno2010
great! THX


Streaksy2011
I LOVE this.


Code Archives Forum