Yes, shaders are much more faster. The shader code is really small and easy. The init of the agk code is a little bit complex. Loading media makes some functions obsolote
scratch.vs
attribute vec4 position;
attribute vec4 color;
attribute vec2 uv;
varying vec2 uvVarying;
varying vec4 colorVarying;
varying vec2 posVarying;
uniform mat4 agk_Ortho;
void main()
{
gl_Position = agk_Ortho * position;
posVarying = position.xy;
uvVarying = uv;
colorVarying = color;
}
scratch.ps
varying vec2 uvVarying;
varying vec2 posVarying;
uniform sampler2D texture0;
uniform vec2 agk_resolution;
uniform float alpha_mask [1024];
void main()
{
int x, y, p;
x = int(uvVarying.x * 32.0);
y = int(uvVarying.y * 32.0);
p = (x + y * 32);
float alpha = alpha_mask[p];
vec4 texturecolor = texture2D(texture0, uvVarying);
gl_FragColor = vec4(texturecolor.rgb, alpha);
}
agk code;
// Project: Shader Scratch
// Created: 2022-04-08
// show all errors
SetErrorMode(2)
// set window properties
SetWindowTitle( "Shader Scratch" )
SetWindowSize( 1920, 1080, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window
// set display properties
SetVirtualResolution( 1920, 1080 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 0, 0 ) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts
#Constant _SPRITE_SIZE_BITS_ 5
#Constant _MAX_SPRITES_ 2048
Global _IMAGE_LIST_ As Integer[_MAX_SPRITES_]
Global _SPRITE_LIST_ As Integer[_MAX_SPRITES_]
Global _SHADER_LIST_ As Integer[_MAX_SPRITES_]
Global _ALPHA_LIST_ As Float[_MAX_SPRITES_, 1024] //If you change the spritesizebits, you must change here also!
Global _SPRITE_COUNT_X_ As Integer
Global _SPRITE_COUNT_Y_ As Integer
Global _VIEW_LEFT_ As Float, _VIEW_TOP_ As Float
Global _TOTAL_SPRITES_ As Integer
Global _CURSOR_IMAGE_ As Float[1024, 1024]
Global _CURSOR_WIDTH_ As Integer, _CURSOR_HEIGHT_ As Integer
Global _CURSOR_MID_X_ As Float, _CURSOR_MID_Y_ As Float
Global _BACK_SPRITE_ As Integer
_INIT_("test.png", "cursor.png", 300, 300) //image to be scratched, the image of the cursor, left and top positions
Do
If GetPointerState() Then _SCRATCH_(GetPointerX(), GetPointerY())
Print( ScreenFPS() )
Sync()
Loop
Function _SCRATCH_(_X_ As Float, _Y_ As Float)
Local X As Integer, Y As Integer
Local XStart As Float, dY As Float, dX As Float
XStart = _X_ - _CURSOR_MID_X_
dY = _Y_ - _CURSOR_MID_Y_
For Y = 0 To _CURSOR_HEIGHT_ - 1
dX = XStart
For X = 0 To _CURSOR_WIDTH_ - 1
_SET_PIXELS_ALPHA_(dX, dY, _CURSOR_IMAGE_[X, Y])
dX = dX + 1
Next
dY = dY + 1
Next
EndFunction
Function _SET_PIXELS_ALPHA_(_X_ As Float, _Y_ As Float, _A_ As Float)
Local X As Integer, Y As Integer, Sy As Integer
Local EntityNo As Integer, ShaderIndex As Integer, PixelIndex As Integer
Local A As Float
X = Floor(_X_ - _VIEW_LEFT_)
If X < 0 Then ExitFunction
Y = Floor(_Y_ - _VIEW_TOP_)
If Y < 0 Then ExitFunction
Sx = X >> _SPRITE_SIZE_BITS_
If Sx >= _SPRITE_COUNT_X_ Then ExitFunction
Sy = Y >> _SPRITE_SIZE_BITS_
If Sy >= _SPRITE_COUNT_Y_ Then ExitFunction
EntityNo = Sx + Sy * _SPRITE_COUNT_X_
ShaderIndex = _SHADER_LIST_[EntityNo]
X = X - (Sx << _SPRITE_SIZE_BITS_)
Y = Y - (Sy << _SPRITE_SIZE_BITS_)
PixelIndex = X + (Y << _SPRITE_SIZE_BITS_)
A = _ALPHA_LIST_[EntityNo, PixelIndex]
A = A - _A_
If A < 0 Then A = 0
_ALPHA_LIST_[EntityNo, PixelIndex] = A
SetShaderConstantArrayFloatByName(ShaderIndex, "alpha_mask", PixelIndex, A)
EndFunction
////////////////////////////////////////////////////////////////////////////////////////////////////////////
Function _INIT_(_S_ As String, _Q_ As String, _L_ As Float, _T_ As Float)
Local i As Integer, j As Integer
_VIEW_LEFT_ = _L_
_VIEW_TOP_ = _T_
//i = LoadImage(back_image.png")
Local Mem As Integer, W As Integer, H As Integer
Local Offset As Integer, C As Integer, R As Integer, G As Integer, B As Integer
W = 256 + (Random() >> 8)
H = 256 + (Random() >> 8)
Mem = CreateMemblock(12 + W * H * 4)
SetMemblockInt(Mem, 0, W)
SetMemblockInt(Mem, 4, H)
SetMemblockInt(Mem, 8, 32)
Offset = 12
For i = 0 To H - 1
For j = 0 To W - 1
R = (i ~~ j) && 255
G = R
B = R
C = 0xFF000000
C = C || (B << 16) || (G << 8) || R
SetMemblockInt(Mem, Offset, C)
Offset = Offset + 4
Next
Next
i = CreateImageFromMemblock(Mem)
_BACK_SPRITE_ = CreateSprite(i)
DeleteMemblock(Mem)
SetSpritePosition(_BACK_SPRITE_, _VIEW_LEFT_, _VIEW_TOP_)
//i = LoadImage(_S_)
i = _CREATE_RANDOM_IMAGE_(W, H)
j = _FIX_IMAGE_SIZE_(i)
_SUB_DIVIDE_IMAGE_(j)
_CREATE_CURSOR_IMAGE_(_Q_)
EndFunction
Function _CREATE_CURSOR_IMAGE_(_Q_ As String)
/*
Local i As Integer, Mem As Integer
i = LoadImage(_Q_)
Mem = CreateMemblockFromImgae(i)
_FILL_CURSOR_ARRAY_(Mem)
DeleteMemblock(Mem)
DeleteImage(i)
//EndFunction
*/
//Again no media;
Local i As Integer, j As Integer, k As Integer
Local AlphaStart As Float, AlphaEnd As Float, dAlpha As Float
Local Mem As Integer
_CURSOR_WIDTH_ = 40
_CURSOR_HEIGHT_ = _CURSOR_WIDTH_
_CURSOR_MID_X_ = _CURSOR_WIDTH_/2
_CURSOR_MID_Y_ = _CURSOR_HEIGHT_/2
Mem = CreateMemblock(12 + _CURSOR_WIDTH_ * _CURSOR_HEIGHT_ * 4)
SetMemblockInt(Mem, 0, _CURSOR_WIDTH_)
SetMemblockInt(Mem, 4, _CURSOR_HEIGHT_)
SetMemblockInt(Mem, 8, 32)
AlphaStart = 255
AlphaEnd = 10
dAlpha = _CURSOR_WIDTH_/2
dAlpha = (AlphaStart - AlphaEnd)/dAlpha
k = 0
For i = _CURSOR_WIDTH_/2 To 1 Step -1
j = _FILL_CIRCLE_IMAGE_(Mem, _CURSOR_MID_X_, _CURSOR_MID_Y_, i, AlphaEnd)
AlphaEnd = AlphaEnd + dAlpha
If GetImageExists(k) Then DeleteImage(k)
k = j
DeleteMemblock(Mem)
Mem = CreateMemblockFromImage(k)
Next
_FILL_CURSOR_ARRAY_(Mem)
DeleteMemblock(Mem)
If GetImageExists(k) Then DeleteImage(k)
EndFunction
Function _FILL_CURSOR_ARRAY_(_O_ As Integer)
Local W As Integer, H As Integer
Local X As Integer, Y As Integer
Local i As Integer, f As Float
Local Offset As Integer
W = GetMemblockInt(_O_, 0)
H = GetMemblockInt(_O_, 4)
Offset = 12
For Y = 0 To H - 1
For X = 0 To W - 1
i = GetMemblockInt(_O_, Offset) && 255
f = i
_CURSOR_IMAGE_[X, Y] = f
Offset = Offset + 4
Next
Next
EndFunction
Function _FILL_CIRCLE_IMAGE_(_O_ As Integer, _X_ As Integer, _Y_ As Integer, _R_ As Integer, _C_ As Integer) //Yes we can!
Local Ax As Integer, Ay As Integer, E As Integer, L As Integer
Local XStart As Integer, XStop As Integer, LY As Integer, Offset As Integer
Local XLim As Integer, YLim As Integer, Pitch As Integer
Pitch = GetMemblockInt(_O_, 0)
XLim = Pitch - 1
YLim = GetMemblockInt(_O_, 4) - 1
Ax = _R_
Ay = 0
E = 0
Do
If Ax < Ay Then Exit
XStart = _X_ - Ax
XStop = _X_ + Ax
If XStart < 0 Then XStart = 0
If XStop > XLim Then XStop = XLim
//drawline(xtart, Ly, xstop, Ly, _C_)
Ly = _Y_ + Ay
If (Ly >= 0) And (Ly <= YLim)
Offset = (3 + (XStart + LY * Pitch)) << 2 //12 + (X + Y * W) * 4
For L = XStart To XStop
SetMemblockInt(_O_, Offset, _C_)
Offset = Offset + 4
Next
EndIf
Ly = _Y_ - Ay
If (Ly >= 0) And (Ly <= YLim)
Offset = (3 + (XStart + LY * Pitch)) << 2 //12 + (X + Y * W) * 4
For L = XStart To XStop
SetMemblockInt(_O_, Offset, _C_)
Offset = Offset + 4
Next
EndIf
XStart = _X_ - Ay
XStop = _X_ + Ay
If XStart < 0 Then XStart = 0
If XStop > XLim Then XStop = XLim
Ly = _Y_ + Ax
If (Ly >= 0) And (Ly <= YLim)
Offset = (3 + (XStart + LY * Pitch)) << 2 //12 + (X + Y * W) * 4
For L = XStart To XStop
SetMemblockInt(_O_, Offset, _C_)
Offset = Offset + 4
Next
EndIf
Ly = _Y_ - Ax
If (Ly >= 0) And (Ly <= YLim)
Offset = (3 + (XStart + LY * Pitch)) << 2 //12 + (X + Y * W) * 4
For L = XStart To XStop
SetMemblockInt(_O_, Offset, _C_)
Offset = Offset + 4
Next
EndIf
If E <= 0
Ay = Ay + 1
E = E + Ay * 2 + 1
EndIf
If E > 0
Ax = Ax - 1
E = E - Ax * 2 + 1
EndIf
Loop
X = CreateImageFromMemblock(_O_)
EndFunction X
Function _CREATE_RANDOM_IMAGE_(_W_ As Integer, _H_ As Integer)
Local Mem As Integer, Image As Integer, X As Integer, Y As Integer
Local Offset As Integer, Pixel As Integer
Mem = CreateMemblock(12 + _W_ * _H_ * 4)
SetMemblockInt(Mem, 0, _W_)
SetMemblockInt(Mem, 4, _H_)
SetMemblockInt(Mem, 8, 32)
Offset = 12
For Y = 0 To _H_ - 1
For X = 0 To _W_ - 1
Pixel = MakeColor(Random() >> 8, Random() >> 8, Random() >> 8)
Pixel = Pixel || 0xFF000000
SetMemblockInt(Mem, Offset, Pixel)
Offset = Offset + 4
Next
Next
Image = CreateImageFromMemblock(Mem)
DeleteMemblock(Mem)
EndFunction Image
Function _SUB_DIVIDE_IMAGE_(_I_ As Integer)
Local ImageMem As Integer, ImageWidth As Integer, ImageHeight As Integer
Local X As Integer, Y As Integer, P As Integer, Q As Integer, XTiles As Integer, YTiles As Integer
Local i As Integer, j As Integer, sx As Integer, sy As Integer
Local ImageLinePicth As Integer, ImageBlockPitch As Integer
Local NewMem As Integer, BlitSize As Integer
Local OffsetA As Integer, OffsetB As Integer, OffsetC As Integer
For i = 0 To _MAX_SPRITES_ - 1
If GetSpriteExists(_SPRITE_LIST_[i])
DeleteImage(_IMAGE_LIST_[i])
DeleteSprite(_SPRITE_LIST_[i])
DeleteShader(_SHADER_LIST_[i])
EndIf
Next
ImageMem = CreateMemblockFromImage(_I_)
ImageWidth = GetMemblockInt(ImageMem, 0)
ImageHeight = GetMemblockInt(ImageMem, 4)
ImageLinePicth = ImageWidth << 2
ImageBlockPitch = ImageLinePicth << _SPRITE_SIZE_BITS_
XTiles = ImageWidth >> _SPRITE_SIZE_BITS_
YTiles = ImageHeight >> _SPRITE_SIZE_BITS_
_SPRITE_COUNT_X_ = XTiles
_SPRITE_COUNT_Y_ = YTiles
NewMem = CreateMemblock(12 + (1 << (_SPRITE_SIZE_BITS_ * 2 + 2)))
BlitSize = 1 << (_SPRITE_SIZE_BITS_ + 2)
j = 1 << _SPRITE_SIZE_BITS_
SetMemblockInt(NewMem, 0, j)
SetMemblockInt(NewMem, 4, j)
SetMemblockInt(NewMem, 8, 32)
OffsetA = 12
For Y = 0 To YTiles - 1
For X = 0 To XTiles - 1
OffsetC = 12
OffsetB = OffsetA + X * BlitSize
For i = 0 To j - 1
CopyMemblock(ImageMem, NewMem, OffsetB, OffsetC, BlitSize)
OffsetB = OffsetB + ImageLinePicth
OffsetC = OffsetC + BlitSize
Next
_IMAGE_LIST_[TotalEntities] = CreateImageFromMemblock(NewMem)
SetImageTransparentColor(_IMAGE_LIST_[TotalEntities], 0, 0, 0)
_SPRITE_LIST_[TotalEntities] = CreateSprite(_IMAGE_LIST_[TotalEntities])
SetSpriteTransparency(_SPRITE_LIST_[TotalEntities], 1)
SetSpritePosition(_SPRITE_LIST_[TotalEntities], _VIEW_LEFT_ + (X << _SPRITE_SIZE_BITS_), _VIEW_TOP_ + (Y << _SPRITE_SIZE_BITS_))
//SetSpriteColor(_SPRITE_LIST_[TotalEntities], Random()>>8, Random()>>8, Random()>>8, 255)
_SHADER_LIST_[TotalEntities] = LoadShader("scratch.vs", "scratch.ps")
For sy = 0 To 1023
SetShaderConstantArrayByName(_SHADER_LIST_[TotalEntities], "alpha_mask", sy, 255, 0, 0, 0)
_ALPHA_LIST_[TotalEntities, sy] = 255
Next
SetSpriteShader(_SPRITE_LIST_[TotalEntities], _SHADER_LIST_[TotalEntities])
TotalEntities = TotalEntities + 1
Next
OffsetA = OffsetA + ImageBlockPitch
Next
DeleteMemblock(ImageMem)
DeleteMemblock(NewMem)
_TOTAL_SPRITES_ = TotalEntities
EndFunction
Function _FIX_SIZE_(_X_ As Integer)
//round up the integer value
//to the multiplies of 64's
//12 -> 32
//257 -> 288
//256 -> 256
Local A As Integer, B As Integer, S As Integer
S = 1 << _SPRITE_SIZE_BITS_
B = S - 1
A = _X_ && B
If A = 0 Then ExitFunction _X_
A = _X_ >> _SPRITE_SIZE_BITS_
A = (A + 1) << _SPRITE_SIZE_BITS_
EndFunction A
Function _FIX_IMAGE_SIZE_(_I_ As Integer)
//Create a new Image with the sizes of 64's
//e.g. 123x61 -> 128x64
//65x11 -> 128x64
//and return the new image index
Local Mem As Integer, NewMem As Integer, BlitMem As Integer
Local ImageWidth As Integer, ImageHeight As Integer, NewWidth As Integer, NewHeight As Integer
Local X As Integer, Y As Integer, ImagePitch As Integer, NewPicth As Integer
Local ImageOffset As Integer, NewOffset As Integer
Local Pixel As Integer, FixedImage As Integer
Mem = CreateMemblockFromImage(_I_)
ImageWidth = GetMemblockInt(Mem, 0)
ImageHeight = GetMemblockInt(Mem, 4)
NewWidth = _FIX_SIZE_(ImageWidth)
NewHeight = _FIX_SIZE_(ImageHeight)
NewMem = CreateMemblock(12 + NewWidth * NewHeight * 4)
SetMemblockInt(NewMem, 0, NewWidth)
SetMemblockInt(NewMem, 4, NewHeight)
ImagePitch = ImageWidth * 4
NewPitch = NewWidth * 4
BlitMem = CreateMemblock(ImagePitch)
ImageOffset = 12
NewOffset = 12
//simply copy pixels, line by line
For Y = 0 To ImageHeight - 1
CopyMemblock(Mem, NewMem, ImageOffset, NewOffset, ImagePitch)
ImageOffset = ImageOffset + ImagePitch
NewOffset = NewOffset + NewPitch
Next
FixedImage = CreateImageFromMemblock(NewMem)
DeleteMemblock(Mem)
DeleteMemblock(NewMem)
DeleteMemblock(BlitMem)
EndFunction FixedImage