Posted: 9th Feb 2023 9:45
This program will flip an image in any direction using either memblocks or a shader
feel free to use
+ Code Snippet
// Project: imageManipulation 
// Created: 2023-02-03

// show all errors
SetErrorMode(2)

// set window properties
SetWindowTitle( "imageManipulation" )
SetWindowSize( 1024, 768, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window

// set display properties
SetVirtualResolution( 1024, 768 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 30, 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
speedtest as float
//path="raw:"+getreadpath()+"media/"
test=loadimage("test.png"):resettimer() : rem timer here is set to 0 so as speed tests acurate to milliseconds can be displayed
a=createsprite(test)         //reading from left to right and top down a = first sprite
b=createsprite(flip(test,1)) //b second sprite 
c=createsprite(flip(test,2)) //c third sprite
d=createsprite(flip(test,3)) //d fourth sprite
SetSpritePosition(a,20,40)
setspriteposition(b,276,40)
setspriteposition(c,532,40)
setspriteposition(d,776,40)
speedtest1$=str(timer())


createShaderFile("flip.ps")
e_s=LoadSpriteShader("flip.ps") 
f_s=LoadSpriteShader("flip.ps")
g_s=LoadSpriteShader("flip.ps")
h_s=LoadSpriteShader("flip.ps")
resettimer()
e=createSprite(test)  //the second row is for the shader flip the sprite 
SetSpritePosition(e,20,220) //from left to right
SetSpriteShader(e,e_s)

f=createSprite(test)
SetSPritePosition(f,276,220)
SetSpriteShader(f,f_s)

g=createSprite(test)
SetSpritePosition(g,532,220)
SetSpriteShader(g,g_s)

h=createSprite(test)
SetSpritePosition(h,776,220)
SetSpriteShader(h,h_s)

SetShaderConstantByName(e_s, "rot", 0.0, 0.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate they may only be 1.0 or 0.0
SetShaderConstantByName(f_s, "rot", 1.0, 0.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
SetShaderConstantByName(g_s, "rot", 0.0, 1.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
SetShaderConstantByName(h_s, "rot", 1.0, 1.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
// the first value passed to the shader is flip on x axis while the secong is the y axis 
swap():render()
newEI=GetImage(getspritex(e),getspritey(e),getspritex(e)+getspritewidth(e),getspritey(e)+getSpriteheight(e)) //here are four more images to be created that make up the bottom 4th row
newFI=GetImage(getspritex(f),getspritey(f),getspritex(f)+getspritewidth(f),getspritey(f)+getSpriteheight(f))
newGI=GetImage(getspritex(g),getspritey(g),getspritex(g)+getspritewidth(g),getspritey(g)+getSpriteheight(g))
newHI=GetImage(getspritex(h),getspritey(h),getspritex(h)+getspritewidth(h),getspritey(h)+getSpriteheight(h))

speedtest=timer()
speedtest2$=str(speedtest)
resetTimer()
newE=createsprite(newEI)
newF=createsprite(newFI)
newG=createsprite(newGI)
newH=createsprite(newHI)
SetSpritePosition(newE,20,580)
SetSpritePosition(newF,276,580)
SetSpritePosition(newG,532,580)
SetSpritePosition(newH,776,580)
speedtest4$=str(timer()+speedtest)

resettimer()
i=createSprite(test)
SetSpritePosition(i,20,400) //the 3rd row demonostrating getint instead of retrieving the colours individually
j=createSprite(flip2(test,1))
SetSPritePosition(j,276,400)
k=createSprite(flip2(test,2))
SetSpritePosition(k,532,400)
l=createSprite(flip2(test,3))
SetSpritePosition(l,776,400)
speedtest3$=(str(timer()))
//SetRawWritePath(getreadpath())
//saveimage(getspriteimageID(h),"testsave.png")
do
    print("flip1="+speedtest1$+"    shader"+speedtest2$+"    flip2="+speedtest3$+"     shader/total="+speedtest4$)
    Sync()
loop

function flip(img as integer,flip as integer)
imgmemblock = CreateMemblockFromImage(img)
newmemblock = CreateMemblockFromImage(img)
local size as integer     
width = GetMemblockInt(imgmemblock,0)
height = GetMemblockInt(imgmemblock,4)
size=abs(width*height)
for y= 0 to height-1
	for x= 0 to width-1 //to 0 step -1
		
		Offset = (12+((y * width) + x) * 4) - 4
		r=GetMemblockByte(imgmemblock,Offset)
		g=GetMemblockByte(imgmemblock,Offset+1)
		b=GetMemblockByte(imgmemblock,Offset+2)
		a=GetMemblockByte(imgmemblock,Offset+3)
		color#=(r+g+b) 
		if flip=1
			xx=width-1 -x
			Offset = (12+((y * width) + xx) * 4) - 4
		elseif flip=2
			yy=height-1 -y
			Offset = (12+((yy * width) + x) * 4) - 4
		else
			xx=width-1 -x
			yy=height-1-y	
			Offset = (12+((yy * width) + xx) * 4) - 4
		endif		
		SetMemblockByte(newmemblock,Offset,r)
		setMemblockByte(newmemblock,Offset+1,g)
		setMemblockByte(newmemblock,Offset+2,b)
		setMemblockByte(newmemblock,Offset+3,a)
	next x
next y	
newImg=CreateImageFromMemblock(newmemblock)
deletememblock(newmemblock)
deletememblock(imgmemblock)
endfunction newimg 

function createShaderFile(name$ as string)
 
fw=OpenToWrite(name$)
WriteLine(fw,"#ifdef GL_ES")
WriteLine(fw,"precision mediump float;")
WriteLine(fw,"precision mediump int;")
WriteLine(fw,"#endif")
WriteLine(fw,"#define PROCESSING_TEXTURE_SHADER")
WriteLine(fw,"varying mediump vec2 uvVarying;")
WriteLine(fw,"uniform sampler2D texture0;")
WriteLine(fw,"uniform vec2 rot;")
WriteLine(fw,"void main(void)")
WriteLine(fw,"{")
//WriteLine(fw,"  vec2 p = uvVarying;")
WriteLine(fw,"  vec2 p = mix(uvVarying, rot - uvVarying, rot);")
//WriteLine(fw,"  if (rot.x ==1.0)")
//WriteLine(fw,"  {p.x=rot.x-p.x;}")   ///p.x -= mod(p.x, 1.0 / pixels.x);")
//WriteLine(fw,"  if (rot.y==1.0)")
//WriteLine(fw,"  {p.y=rot.y-p.y;}")   ////p.y -= mod(p.y, 1.0 / pixels.y);")
WriteLine(fw,"  vec3 col = texture2D(texture0, p).rgb;")
WriteLine(fw,"  gl_FragColor = vec4(col, 1.0);")
WriteLine(fw,"}")
CloseFile(fw)
endfunction

function flip2(img as integer,flip as integer)
imgmemblock = CreateMemblockFromImage(img)
newmemblock = CreateMemblockFromImage(img)
local size as integer    
width = GetMemblockInt(imgmemblock,0)
height = GetMemblockInt(imgmemblock,4)
size=abs(width*height)
for y= 0 to height-1
 
    SrcOffset = (12+ ((y * width) *4))
 
    for x= 0 to width-1 //to 0 step -1
        
         // Read Source pixel  
        ARGB=GetMemblockInt(imgmemblock,SrcOffset)
    // move to the next pixel along this row
       SrcOffset=SrcOffset+4
 
        if flip=1
            xx=width-1 -x
            Offset = (12+((y * width) + xx) * 4) - 4
        elseif flip=2
            yy=height-1 -y
            Offset = (12+((yy * width) + x) * 4) - 4
        else
            xx=width-1 -x
            yy=height-1-y   
            Offset = (12+((yy * width) + xx) * 4) - 4
        endif      
 
        SetMemblockInt(newmemblock,Offset,ARGB)
    next x
next y  
newImg=CreateImageFromMemblock(newmemblock)
deletememblock(newmemblock)
deletememblock(imgmemblock)
endfunction newimg 

ive also included a test image to demonstrate
Posted: 9th Feb 2023 10:23
Is this an example for image manipulation or is flipping the real target here?
Posted: 9th Feb 2023 11:02
@mikehart Flipping is the target, I know the program is named imageManipulation, originally I had planned to add a rotation vector but that math with shaders is a bit beyond my abilities
Posted: 9th Feb 2023 11:21
For those interested in AppGameKit optimization and genreally tweaking their brute force operations in their AppGameKit programs, stuff like memblock operations are good place to look.

Generally speaking when using a runtime (like AppGameKit uses) it's best to work in the biggest data type that's on offer, so we should be able to swap the batches of byte read /write with a single int read /write.

(untested)

+ Code Snippet
function flip(img as integer,flip as integer)
imgmemblock = CreateMemblockFromImage(img)
newmemblock = CreateMemblockFromImage(img)
local size as integer    
width = GetMemblockInt(imgmemblock,0)
height = GetMemblockInt(imgmemblock,4)
size=abs(width*height)
for y= 0 to height-1
    for x= 0 to width-1 //to 0 step -1
         
        Offset = (12+((y * width) + x) * 4) - 4
        ARGB=GetMemblockInt(imgmemblock,Offset)
        color#=(r+g+b) 
        if flip=1
            xx=width-1 -x
            Offset = (12+((y * width) + xx) * 4) - 4
        elseif flip=2
            yy=height-1 -y
            Offset = (12+((yy * width) + x) * 4) - 4
        else
            xx=width-1 -x
            yy=height-1-y   
            Offset = (12+((yy * width) + xx) * 4) - 4
        endif      
        SetMemblockInt(newmemblock,Offset,ARGB)
    next x
next y  
newImg=CreateImageFromMemblock(newmemblock)
deletememblock(newmemblock)
deletememblock(imgmemblock)
endfunction newimg 
 



Focusing on the inner loop and thinking about how AppGameKit executes code, that being that every operation in your program ( loops / variables/arrays/function calls / operations / etc ) all get converted into an operation in a giant select / case that's being polled for every cycle of the program. That means excessive operations are like a tax on your programs target performance. Too many unnecessary operations will impact the performance of any loop dramatically.

All that probably sounds like a mouth full rubbish to most people, but there's many simple strategy we can all use to boost our 2D programs performance, be in AppGameKit or otherwise .. One handy one; is moving constant calculations outside of the inner worker bee for / next loop. What tends to happen when we work in nested For Y / For X loop blocks is we end up calculating things inside the X loop are in fact constant for the that row..

So we could more than like compute the SrcOffset once per row than step along it


(Untested - For educational purposes only ) )

+ Code Snippet
function flip(img as integer,flip as integer)
imgmemblock = CreateMemblockFromImage(img)
newmemblock = CreateMemblockFromImage(img)
local size as integer    
width = GetMemblockInt(imgmemblock,0)
height = GetMemblockInt(imgmemblock,4)
size=abs(width*height)
for y= 0 to height-1

    SrcOffset = (12+ ((y * width) *4)

    for x= 0 to width-1 //to 0 step -1
       
         // Read Source pixel  
        ARGB=GetMemblockInt(imgmemblock,SrcOffset)
    // move to the next pixel along this row
       SrcOffset=SrcOffset+4

        if flip=1
            xx=width-1 -x
            Offset = (12+((y * width) + xx) * 4) - 4
        elseif flip=2
            yy=height-1 -y
            Offset = (12+((yy * width) + x) * 4) - 4
        else
            xx=width-1 -x
            yy=height-1-y   
            Offset = (12+((yy * width) + xx) * 4) - 4
        endif      

        SetMemblockInt(newmemblock,Offset,ARGB)
    next x
next y  
newImg=CreateImageFromMemblock(newmemblock)
deletememblock(newmemblock)
deletememblock(imgmemblock)
endfunction newimg 
 


So reading the source pixel is now one call to GetMemblockInt followed by an addition.

Now the question we need to ask ourselves is does that FLIP variable every change within the inner X loop ? In this case it' doesn't; so we could split the complex loop into three simple loops and remove the decisions per pixel

Perhaps like

+ Code Snippet
function flip(img as integer,flip as integer)
imgmemblock = CreateMemblockFromImage(img)
newmemblock = CreateMemblockFromImage(img)
local size as integer    
width = GetMemblockInt(imgmemblock,0)
height = GetMemblockInt(imgmemblock,4)
size=abs(width*height)
for y= 0 to height-1

    SrcOffset = (12+ ((y * width) *4)

     // -----------------------------------------------------------------------------------
    if flip=1
     // -----------------------------------------------------------------------------------
       for x= 0 to width-1 //to 0 step -1
       
         // Read Source pixel  
        ARGB=GetMemblockInt(imgmemblock,SrcOffset)
    // move to the next pixel along this row
       SrcOffset=SrcOffset+4
            xx=width-1 -x
            Offset = (12+((y * width) + xx) * 4) - 4
        SetMemblockInt(newmemblock,Offset,ARGB)
      next x
   endif


     // -----------------------------------------------------------------------------------
    elseif flip=2
     // -----------------------------------------------------------------------------------
       for x= 0 to width-1 //to 0 step -1
       
         // Read Source pixel  
        ARGB=GetMemblockInt(imgmemblock,SrcOffset)
    // move to the next pixel along this row
       SrcOffset=SrcOffset+4
            yy=height-1 -y
            Offset = (12+((yy * width) + x) * 4) - 4
          SetMemblockInt(newmemblock,Offset,ARGB)
      next x

     // -----------------------------------------------------------------------------------
   else
     // -----------------------------------------------------------------------------------

       for x= 0 to width-1 //to 0 step -1
       
         // Read Source pixel  
        ARGB=GetMemblockInt(imgmemblock,SrcOffset)
    // move to the next pixel along this row
       SrcOffset=SrcOffset+4
            xx=width-1 -x
            yy=height-1-y   
            Offset = (12+((yy * width) + xx) * 4) - 4
        SetMemblockInt(newmemblock,Offset,ARGB)
      next x

   endif


next y  
newImg=CreateImageFromMemblock(newmemblock)
deletememblock(newmemblock)
deletememblock(imgmemblock)
endfunction newimg 
 


Yep it's longer.. yep harder to maintain.. well not that much harder, but this type of approach can help you will back performance a program be lacking.
Posted: 9th Feb 2023 11:53
Thats a great concept @Kevin and would improve the performance I feel aswell thankyou

For my image drawing software I work in smaller image sizes as there's lots in the background stored
The now optimed shader should be fastest ideally I should do a performance test of the now 3
methods.
Posted: 9th Feb 2023 12:18
Doing speed tests using the following code
+ Code Snippet
// Project: imageManipulation 
// Created: 2023-02-03

// show all errors
SetErrorMode(2)

// set window properties
SetWindowTitle( "imageManipulation" )
SetWindowSize( 1024, 768, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window

// set display properties
SetVirtualResolution( 1024, 768 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 30, 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

//path="raw:"+getreadpath()+"media/"
test=loadimage("test.png"):resettimer()
a=createsprite(test)
b=createsprite(flip(test,1))
c=createsprite(flip(test,2))
d=createsprite(flip(test,3))
SetSpritePosition(a,20,40)
setspriteposition(b,276,40)
setspriteposition(c,532,40)
setspriteposition(d,776,40)
speedtest1$=str(timer())


createShaderFile("flip.ps")
e_s=LoadSpriteShader("flip.ps")
f_s=LoadSpriteShader("flip.ps")
g_s=LoadSpriteShader("flip.ps")
h_s=LoadSpriteShader("flip.ps")
resettimer()
e=createSprite(test)
SetSpritePosition(e,20,220)
SetSpriteShader(e,e_s)

f=createSprite(test)
SetSPritePosition(f,276,220)
SetSpriteShader(f,f_s)

g=createSprite(test)
SetSpritePosition(g,532,220)
SetSpriteShader(g,g_s)

h=createSprite(test)
SetSpritePosition(h,776,220)
SetSpriteShader(h,h_s)

SetShaderConstantByName(e_s, "rot", 0.0, 0.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
SetShaderConstantByName(f_s, "rot", 1.0, 0.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
SetShaderConstantByName(g_s, "rot", 0.0, 1.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
SetShaderConstantByName(h_s, "rot", 1.0, 1.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
speedtest2$=str(timer())

resettimer()
i=createSprite(test)
SetSpritePosition(i,20,400)
j=createSprite(flip(test,1))
SetSPritePosition(j,276,400)
k=createSprite(flip(test,2))
SetSpritePosition(k,532,400)
l=createSprite(flip(test,3))
SetSpritePosition(l,776,400)
speedtest3$=(str(timer()))
do
    print(speedtest1$+"    "+speedtest2$+"    "+speedtest3$+"     FPS"+str(Screenfps()))
    Sync()
loop

function flip(img as integer,flip as integer)
imgmemblock = CreateMemblockFromImage(img)
newmemblock = CreateMemblockFromImage(img)
local size as integer     
width = GetMemblockInt(imgmemblock,0)
height = GetMemblockInt(imgmemblock,4)
size=abs(width*height)
for y= 0 to height-1
	for x= 0 to width-1 //to 0 step -1
		
		Offset = (12+((y * width) + x) * 4) - 4
		r=GetMemblockByte(imgmemblock,Offset)
		g=GetMemblockByte(imgmemblock,Offset+1)
		b=GetMemblockByte(imgmemblock,Offset+2)
		a=GetMemblockByte(imgmemblock,Offset+3)
		color#=(r+g+b) 
		if flip=1
			xx=width-1 -x
			Offset = (12+((y * width) + xx) * 4) - 4
		elseif flip=2
			yy=height-1 -y
			Offset = (12+((yy * width) + x) * 4) - 4
		else
			xx=width-1 -x
			yy=height-1-y	
			Offset = (12+((yy * width) + xx) * 4) - 4
		endif		
		SetMemblockByte(newmemblock,Offset,r)
		setMemblockByte(newmemblock,Offset+1,g)
		setMemblockByte(newmemblock,Offset+2,b)
		setMemblockByte(newmemblock,Offset+3,a)
	next x
next y	
newImg=CreateImageFromMemblock(newmemblock)
deletememblock(newmemblock)
deletememblock(imgmemblock)
endfunction newimg 

function createShaderFile(name$ as string)
 
fw=OpenToWrite(name$)
WriteLine(fw,"#ifdef GL_ES")
WriteLine(fw,"precision mediump float;")
WriteLine(fw,"precision mediump int;")
WriteLine(fw,"#endif")
WriteLine(fw,"#define PROCESSING_TEXTURE_SHADER")
WriteLine(fw,"varying mediump vec2 uvVarying;")
WriteLine(fw,"uniform sampler2D texture0;")
WriteLine(fw,"uniform vec2 rot;")
WriteLine(fw,"void main(void)")
WriteLine(fw,"{")
//WriteLine(fw,"  vec2 p = uvVarying;")
WriteLine(fw,"  vec2 p = mix(uvVarying, rot - uvVarying, rot);")
//WriteLine(fw,"  if (rot.x ==1.0)")
//WriteLine(fw,"  {p.x=rot.x-p.x;}")   ///p.x -= mod(p.x, 1.0 / pixels.x);")
//WriteLine(fw,"  if (rot.y==1.0)")
//WriteLine(fw,"  {p.y=rot.y-p.y;}")   ////p.y -= mod(p.y, 1.0 / pixels.y);")
WriteLine(fw,"  vec3 col = texture2D(texture0, p).rgb;")
WriteLine(fw,"  gl_FragColor = vec4(col, 1.0);")
WriteLine(fw,"}")
CloseFile(fw)
endfunction

function flip2(img as integer,flip as integer)
imgmemblock = CreateMemblockFromImage(img)
newmemblock = CreateMemblockFromImage(img)
local size as integer    
width = GetMemblockInt(imgmemblock,0)
height = GetMemblockInt(imgmemblock,4)
size=abs(width*height)
for y= 0 to height-1
 
    SrcOffset = (12+ ((y * width) *4))
 
    for x= 0 to width-1 //to 0 step -1
        
         // Read Source pixel  
        ARGB=GetMemblockInt(imgmemblock,SrcOffset)
    // move to the next pixel along this row
       SrcOffset=SrcOffset+4
 
        if flip=1
            xx=width-1 -x
            Offset = (12+((y * width) + xx) * 4) - 4
        elseif flip=2
            yy=height-1 -y
            Offset = (12+((yy * width) + x) * 4) - 4
        else
            xx=width-1 -x
            yy=height-1-y   
            Offset = (12+((yy * width) + xx) * 4) - 4
        endif      
 
        SetMemblockInt(newmemblock,Offset,ARGB)
    next x
next y  
newImg=CreateImageFromMemblock(newmemblock)
deletememblock(newmemblock)
deletememblock(imgmemblock)
endfunction newimg 

I get from the original memblock method
0.123284
with my improvised shader
0.000047
and kevins improvement
0.114965
Posted: 9th Feb 2023 16:57
Not sure if your shader test is fair as you actually change the source of the images with memblock commands but with shaders you'd need to render it back into an image.
This depends ofc. if you actually need it to change the source.
Wanna make another speed test ?
Posted: 9th Feb 2023 22:20
using render and grabimage I made some modifications as suggested the shader still comes off faster, Im not sure if thats what you meant by ofc janbo

+ Code Snippet
// Project: imageManipulation 
// Created: 2023-02-03

// show all errors
SetErrorMode(2)

// set window properties
SetWindowTitle( "imageManipulation" )
SetWindowSize( 1024, 768, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window

// set display properties
SetVirtualResolution( 1024, 768 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 30, 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

//path="raw:"+getreadpath()+"media/"
test=loadimage("test.png"):resettimer()
a=createsprite(test)
b=createsprite(flip(test,1))
c=createsprite(flip(test,2))
d=createsprite(flip(test,3))
SetSpritePosition(a,20,40)
setspriteposition(b,276,40)
setspriteposition(c,532,40)
setspriteposition(d,776,40)
speedtest1$=str(timer())


createShaderFile("flip.ps")
e_s=LoadSpriteShader("flip.ps")
f_s=LoadSpriteShader("flip.ps")
g_s=LoadSpriteShader("flip.ps")
h_s=LoadSpriteShader("flip.ps")
resettimer()
e=createSprite(test)
SetSpritePosition(e,20,220)
SetSpriteShader(e,e_s)

f=createSprite(test)
SetSPritePosition(f,276,220)
SetSpriteShader(f,f_s)

g=createSprite(test)
SetSpritePosition(g,532,220)
SetSpriteShader(g,g_s)

h=createSprite(test)
SetSpritePosition(h,776,220)
SetSpriteShader(h,h_s)

SetShaderConstantByName(e_s, "rot", 0.0, 0.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
SetShaderConstantByName(f_s, "rot", 1.0, 0.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
SetShaderConstantByName(g_s, "rot", 0.0, 1.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate
SetShaderConstantByName(h_s, "rot", 1.0, 1.0, 0.0, 0.0 ) //the first two passed values may be set to 1.0 to rotate or 0 not to rotate

swap():render()
newEI=GetImage(getspritex(e),getspritey(e),getspritex(e)+getspritewidth(e),getspritey(e)+getSpriteheight(e))
newFI=GetImage(getspritex(f),getspritey(f),getspritex(f)+getspritewidth(f),getspritey(f)+getSpriteheight(f))
newGI=GetImage(getspritex(g),getspritey(g),getspritex(g)+getspritewidth(g),getspritey(g)+getSpriteheight(g))
newHI=GetImage(getspritex(h),getspritey(h),getspritex(h)+getspritewidth(h),getspritey(h)+getSpriteheight(h))

speedtest2$=str(timer())

newE=createsprite(newEI)
newF=createsprite(newFI)
newG=createsprite(newGI)
newH=createsprite(newHI)
SetSpritePosition(newE,20,580)
SetSpritePosition(newF,276,580)
SetSpritePosition(newG,532,580)
SetSpritePosition(newH,776,580)


resettimer()
i=createSprite(test)
SetSpritePosition(i,20,400)
j=createSprite(flip(test,1))
SetSPritePosition(j,276,400)
k=createSprite(flip(test,2))
SetSpritePosition(k,532,400)
l=createSprite(flip(test,3))
SetSpritePosition(l,776,400)
speedtest3$=(str(timer()))
//SetRawWritePath(getreadpath())
//saveimage(getspriteimageID(h),"testsave.png")
do
    print(speedtest1$+"    "+speedtest2$+"    "+speedtest3$+"     FPS"+str(Screenfps()))
    Sync()
loop

function flip(img as integer,flip as integer)
imgmemblock = CreateMemblockFromImage(img)
newmemblock = CreateMemblockFromImage(img)
local size as integer     
width = GetMemblockInt(imgmemblock,0)
height = GetMemblockInt(imgmemblock,4)
size=abs(width*height)
for y= 0 to height-1
	for x= 0 to width-1 //to 0 step -1
		
		Offset = (12+((y * width) + x) * 4) - 4
		r=GetMemblockByte(imgmemblock,Offset)
		g=GetMemblockByte(imgmemblock,Offset+1)
		b=GetMemblockByte(imgmemblock,Offset+2)
		a=GetMemblockByte(imgmemblock,Offset+3)
		color#=(r+g+b) 
		if flip=1
			xx=width-1 -x
			Offset = (12+((y * width) + xx) * 4) - 4
		elseif flip=2
			yy=height-1 -y
			Offset = (12+((yy * width) + x) * 4) - 4
		else
			xx=width-1 -x
			yy=height-1-y	
			Offset = (12+((yy * width) + xx) * 4) - 4
		endif		
		SetMemblockByte(newmemblock,Offset,r)
		setMemblockByte(newmemblock,Offset+1,g)
		setMemblockByte(newmemblock,Offset+2,b)
		setMemblockByte(newmemblock,Offset+3,a)
	next x
next y	
newImg=CreateImageFromMemblock(newmemblock)
deletememblock(newmemblock)
deletememblock(imgmemblock)
endfunction newimg 

function createShaderFile(name$ as string)
 
fw=OpenToWrite(name$)
WriteLine(fw,"#ifdef GL_ES")
WriteLine(fw,"precision mediump float;")
WriteLine(fw,"precision mediump int;")
WriteLine(fw,"#endif")
WriteLine(fw,"#define PROCESSING_TEXTURE_SHADER")
WriteLine(fw,"varying mediump vec2 uvVarying;")
WriteLine(fw,"uniform sampler2D texture0;")
WriteLine(fw,"uniform vec2 rot;")
WriteLine(fw,"void main(void)")
WriteLine(fw,"{")
//WriteLine(fw,"  vec2 p = uvVarying;")
WriteLine(fw,"  vec2 p = mix(uvVarying, rot - uvVarying, rot);")
//WriteLine(fw,"  if (rot.x ==1.0)")
//WriteLine(fw,"  {p.x=rot.x-p.x;}")   ///p.x -= mod(p.x, 1.0 / pixels.x);")
//WriteLine(fw,"  if (rot.y==1.0)")
//WriteLine(fw,"  {p.y=rot.y-p.y;}")   ////p.y -= mod(p.y, 1.0 / pixels.y);")
WriteLine(fw,"  vec3 col = texture2D(texture0, p).rgb;")
WriteLine(fw,"  gl_FragColor = vec4(col, 1.0);")
WriteLine(fw,"}")
CloseFile(fw)
endfunction

function flip2(img as integer,flip as integer)
imgmemblock = CreateMemblockFromImage(img)
newmemblock = CreateMemblockFromImage(img)
local size as integer    
width = GetMemblockInt(imgmemblock,0)
height = GetMemblockInt(imgmemblock,4)
size=abs(width*height)
for y= 0 to height-1
 
    SrcOffset = (12+ ((y * width) *4))
 
    for x= 0 to width-1 //to 0 step -1
        
         // Read Source pixel  
        ARGB=GetMemblockInt(imgmemblock,SrcOffset)
    // move to the next pixel along this row
       SrcOffset=SrcOffset+4
 
        if flip=1
            xx=width-1 -x
            Offset = (12+((y * width) + xx) * 4) - 4
        elseif flip=2
            yy=height-1 -y
            Offset = (12+((yy * width) + x) * 4) - 4
        else
            xx=width-1 -x
            yy=height-1-y   
            Offset = (12+((yy * width) + xx) * 4) - 4
        endif      
 
        SetMemblockInt(newmemblock,Offset,ARGB)
    next x
next y  
newImg=CreateImageFromMemblock(newmemblock)
deletememblock(newmemblock)
deletememblock(imgmemblock)
endfunction newimg 
Posted: 9th Feb 2023 22:43
Is there a problem with SetSpiteFlip()?
Posted: 9th Feb 2023 22:57
Is there a problem with SetSpriteFlip()?

never knew of the command until now, I guess its a rather pointless to many thread useful only to those who want to make
drawing software, which was my original plan for the command. Atleast it gives an idea of the workings behind the command
and how a shader could've been used.

I was only going to add it to the snippets thread but that's now closed.
Posted: 10th Feb 2023 0:54
I see you're looping through all pixels in the image, there's no need to do that and it can be cut in half. Also, make some constants or at the very least document what the values 1,2,3 actually mean.
Posted: 10th Feb 2023 2:28
I was only going to add it to the snippets thread but that's now closed.

Not anymore

One other thing. I think if you try GetMemblockInt() it will go a lot faster
Posted: 10th Feb 2023 7:29
Thanks the documented program got moved to https://forum.thegamecreators.com/thread/222027?page=4 and at the top of this thread.
@Phaelex other than placing the x loops inside a nested if Im not sure how else it could be improved. I wish I could rotate the UVs inside a memblock or
a shader but the math in that is quite extensive and beyond me. I did research on rotation but it needed the a Vs shader to do so and not just a pixel
shader

which can be achieved better with SetSpriteUV ( iSpriteIndex, u1, v1, u2, v2, u3, v3, u4, v4 ) see [href]file:///C:/Program%20Files%20(x86)/Steam/steamapps/common/App%20Game%20Kit%202/Tier%201/Help/Reference/Sprite/SetSpriteUV.htm[/href]
Posted: 10th Feb 2023 19:16
To flip it you could also scale it by a factor of -1.
Posted: 11th Feb 2023 20:43
To flip it you could also scale it by a factor of -1.

I thought that too but it didn't work for me
Posted: 12th Feb 2023 1:16
The reverse scale technique may nolonger work, dont know, but ideally if we retrieve each of the uv positions of a sprite image, we should be able to
perform a rotation from the sprites offset and a bit of math. I'm sure both memblocks and a shader could achieve this, but I have no idea how to do this,
with my research there was alsorts of complicated math that frankly i didnt understand. If anyone would like to attempt rotation rather than just flipping
ide love to see it shared here. ("This would have the effect of the sprite standing still but the image rotating as if there was 2 sprites and a mask.")