[Solved ] Mojo1 works, Mojo2 has bug?

Jimmy

Active Member
Joined
Jan 2, 2020
I'm doing a water level and really need pixels for doing a few things.

I got everything working with Mojo1 but now I'm using Mojo2 so I built an example for you to show what I mean and I made it on purpose to be as close to Mojo1 as possible.

So when we use Mojo1 to draw to a image, we'll do this :
And it works perfectly in every way.

Mojo1 pixels:
Import mojo

Class MyApp Extends App

    Field myImage:Image  
   
    Method OnCreate()
    End
   
    Method OnRender()
   
        If Not myImage
            ' ------------ CREATE MYIMAGE ---------------
            Local w=256,h=256
            myImage=CreateImage(w,h,1,Image.MidHandle)
            ' ------ DRAW INSIDE MYIMAGE, NORMALLY -------
            Cls 128,0,255
            SetColor 255,128,0
            DrawRect 16,16,w-32,h-32
            SetColor 255,255,255
            DrawText "Hello World",w/2,h/2,.5,.5
            ' - DRAW INSIDE MYIMAGE, INDIVIDUALLY PIXELS -
            '   The raw format is : rrggbb & aa, where 0=transparent, 255 = opaque)
            Local buf:=New Int[w*2*h]
            ReadPixels buf,0,0,w,h ,w,w*2 ' First read existing pixels in case we want to use it in next step
            For Local i=0 Until h Step 4
                For Local j=0 Until w*2
                    buf[i*w*2+j]=buf[i*w*2+j] + $ff00ff | $ff Shl 24 ' < Change ($rrggbb & $aa) here to change what to draw. We draw only on every forth row so we can see some of the original contents
                Next
            Next
            myImage.WritePixels buf,0,0,w,h ,w,w*2
        End
            ' -----------------------------------
        Cls
        SetColor 255,255,255
        DrawText "Testing...",0,0
        DrawImage myImage,MouseX/2.0,MouseY/2.0
    End
End

Function Main()
    New MyApp
End

Let's try to do the same thing with Mojo2.

It will stay opaque or have strange alpha levels. Try for instance to change the alpha so the white rectangle becomes more or less transparent by setting alpha.

Cerberus X:
Strict

Import mojo2
Import brl.databuffer
Import cerberus.maths

Class MyApp Extends App

    Field myCanvas:Canvas
    Field myImage:Image
    Field myData:DataBuffer
    Field imageCanvas:Canvas
    Field patternSeed:Float = 1
   
    Method OnCreate:Int()
    ' ------------ CREATE MYIMAGE ---------------
        myCanvas = New Canvas()
        myImage = New Image(256, 256)
        imageCanvas = New Canvas(myImage)
        myData = New DataBuffer(16 * 16 * 4)
        Return 0
    End
   
    Method OnRender:Int()
        ' ------ DRAW INSIDE MYIMAGE, NORMALLY -------
        patternSeed += 0.1
        For Local x:Int = 0 Until 128
            For Local y:Int = 0 Until 128
                If (x & y & Int(patternSeed))
                    imageCanvas.SetColor(Sin(Millisecs() * 0.1 ) * 0.5 + 0.5, Cos(Millisecs() * 0.1) * 0.5 + 0.5, 0.5)
                Else
                    imageCanvas.SetColor(0, 0, 0)
                Endif
                imageCanvas.DrawRect(x * 2, y * 2, 2, 2)
            Next
        Next
                ' - DRAW INSIDE MYIMAGE, INDIVIDUALLY PIXELS -
                '   Paint the upper left 16 x 16 so we can see some of the original contents
                For Local i:Int = 0 Until 16 * 16 * 4 Step 4
               
               
                    Local r:Int = 255 ' Change red here (we use 0-255 in raw format, not 0.0-1.0)
                    Local g:Int = 255 ' Change green here
                    Local b:Int = 255 ' Change blue here
                    Local a:Int = 255 ' Change alpha here (0 = transparent, 255 = opaque, this does not work yet in Mojo2)
                    Local rgba:Int = a  Shl 24 | b Shl 16 | g Shl 8 | r
                   
                   
                    myData.PokeInt(i, rgba)
                Next

        imageCanvas.Flush()      
        myImage.WritePixels(0, 0, 16, 16, myData)
        ' -----------------------------------      
        ' now render to main canvas
        myCanvas.Clear()  
        myCanvas.DrawImage(myImage, MouseX(), MouseY())
        myCanvas.Flush()
        Return 0
    End
End

Function Main:Int()
    New MyApp()
    Return 0
End
 

Jimmy

Active Member
Joined
Jan 2, 2020
Another Mojo2 code that's closer in that it reads pixels to like the Mojo1
It also shows that If you read and write without touching the values, the pixels are good.

I'm still very confused.

Cerberus X:
Strict

Import mojo2
Import brl.databuffer
Import cerberus.maths

Class MyApp Extends App

    Field myCanvas:Canvas
    Field myImage:Image
    Field myData:DataBuffer
    Field imageCanvas:Canvas
    Field patternSeed:Float = 1
    
    Method OnCreate:Int()
    ' ------------ CREATE MYIMAGE ---------------
        myCanvas = New Canvas()
        myImage = New Image(256, 256)
        imageCanvas = New Canvas(myImage)
        myData = New DataBuffer(128 * 128 * 4)
        Return 0
    End
    
    Method OnRender:Int()
        ' ------ DRAW INSIDE MYIMAGE, NORMALLY -------
        patternSeed += 0.1
        imageCanvas.Clear(0,0,0,0)
        imageCanvas.SetColor 255,255,255
        
            ' - DRAW INSIDE MYIMAGE, INDIVIDUALLY PIXEL
            
        For Local x:Int = 0 Until 128
            For Local y:Int = 0 Until 128
                If (x & y & Int(patternSeed))
                    imageCanvas.SetColor(Sin(Millisecs() * 0.1 ) * 0.5 + 0.5, Cos(Millisecs() * 0.1) * 0.5 + 0.5, 0.5)
                    imageCanvas.DrawRect(x * 2, y * 2, 2, 2)
                Endif
            Next
        Next
        
        imageCanvas.SetColor 1,1,1
        imageCanvas.DrawText "Hello World",256/2+32,256/2,.5,.5
            
        imageCanvas.ReadPixels(0, 0, 128, 128, myData) ' offset, pitch
        
        ' - DRAW INSIDE MYIMAGE, INDIVIDUALLY PIXELS -
        '   Paint the upper left 64 x 64 so we can see some of the original contents
        For Local i:Int = 0 Until 64 * 256 * 4 Step 4

            Local r:Int = 255 ' Change red here (we use 0-255 in raw format, not 0.0-1.0)
            Local g:Int = 255 ' Change green here
            Local b:Int = 255 ' Change blue here
            Local a:Int = 0   ' Change alpha here
            Local rgba:Int = a  Shl 24 | b Shl 16 | g Shl 8 | r
            myData.PokeInt(i, rgba)
        Next

        imageCanvas.Flush()       
        myImage.WritePixels(0, 0, 128, 128, myData)
        ' -----------------------------------       
        ' now render to main canvas
        myCanvas.Clear()   

        myCanvas.SetColor(1,1,1)
        myCanvas.DrawText "Testing...",0,0
        myCanvas.DrawImage(myImage, MouseX(), MouseY())
        myCanvas.Flush()
        Return 0
    End
End

Function Main:Int()
    New MyApp()
    Return 0
End
 

Jimmy

Active Member
Joined
Jan 2, 2020
I get some kind of saturated brightness. Tthe white underneath the gray (128,128,128,128) box does not show through but looks as it's on top, while the secondary gray box underneath (a stamped copy of itself) looks as they should when the boxeds overlap.



2.png
1.png
 

Jimmy

Active Member
Joined
Jan 2, 2020
I followed the suggestion and it seems to work but to me It does not make sense to me exactly why?
Why should Opaque2 be set? Is this a general solution that you should use Opaque for images?

Because I'm drawing in images that I want to draw using normal Alpha mode and also, using subtractive mode.

Cerberus X:
Strict

Import mojo2
Import brl.databuffer
Import cerberus.maths

Class MyApp Extends App

    Field myCanvas:Canvas
    Field myImage:Image
    Field myData:DataBuffer
    Field imageCanvas:Canvas
    Field patternSeed:Float = 1
    
    Method OnCreate:Int()
    ' ------------ CREATE MYIMAGE ---------------
        myCanvas = New Canvas()
        myImage = New Image(256, 256)
        imageCanvas = New Canvas(myImage)
        myData = New DataBuffer(256 * 256 * 4)
        Return 0
    End
    
    Method OnRender:Int()
        myCanvas.SetBlendMode(BlendMode.Opaque2)
        ' ------ DRAW INSIDE MYIMAGE, NORMALLY -------
        patternSeed += 0.1
        imageCanvas.Clear(0,0,0,0)
        imageCanvas.SetColor 255,255,255
        
            ' - DRAW INSIDE MYIMAGE, INDIVIDUALLY PIXEL
            
        For Local x:Int = 0 Until 128
            For Local y:Int = 0 Until 128
                If (x & y & Int(patternSeed))
                    imageCanvas.SetColor(Sin(Millisecs() * 0.1 ) * 0.5 + 0.5, Cos(Millisecs() * 0.1) * 0.5 + 0.5, 0.5)
                    imageCanvas.DrawRect(x * 2, y * 2, 2, 2)
                Endif
            Next
        Next
        
        imageCanvas.SetColor 1,1,1
        imageCanvas.DrawText "Hello World",256/2+32,256/2,.5,.5
        'imageCanvas.SetAlpha (0.5)
        'imageCanvas.SetColor 1,1,1
        'imageCanvas.DrawRect(0,0,80,80)
        imageCanvas.SetAlpha (1.0)
        imageCanvas.SetColor 1,1,1
        
        imageCanvas.ReadPixels(0, 0, 128, 128, myData)
    
        ' - DRAW INSIDE MYIMAGE, INDIVIDUALLY PIXELS -
        '   Paint the upper left 64 x 64 so we can see some of the original contents
        For Local j:Int = 0 To 63   
            For Local i:Int = 0 To 63
                Local r:Int = 128 ' Change red here
                Local g:Int = 128 ' Change green here
                Local b:Int = 128 ' Change blue here
                Local a:Int = 128 ' Change alpha here
                Local rgba:Int = a  Shl 24 | b Shl 16 | g Shl 8 | r
                myData.PokeInt((i*128+j)*4, rgba)
            Next
        Next
        
        imageCanvas.Flush()       
        myImage.WritePixels(0, 0, 128, 128, myData)
        ' -----------------------------------       
        ' now render to main canvas
        myCanvas.Clear()   
        myCanvas.SetColor(1,1,1)
        myCanvas.DrawImage(myImage, 200,200)
        myCanvas.DrawText "Testing...",0,0
        myCanvas.DrawImage(myImage, MouseX(), MouseY())
        myCanvas.Flush()
        Return 0
    End
End

Function Main:Int()
    New MyApp()
    Return 0
End
 

MikeHart

Administrator
Joined
Jun 19, 2017
Location
Germany
As you read in the link I posted, mojo2 uses pre-multiplied ABGR values. Why? Because Mark Sibly designed it that way. I guess this way mojo2 doesn't need to do this in the shader and so is faster. But that is just my guess.
If you don't want to use Opague2, then you need to set the color pre-multiplied.
 

Jimmy

Active Member
Joined
Jan 2, 2020
How come that Mojo1 works so fine using a regular alpha mode instead of opaque2 blendmode?

Opaque2 sounds arbitrary For being such an important thing. It makes you curious about the other modes.
Does alpha2 And opaque have special roles in Cerberus?
 

Jimmy

Active Member
Joined
Jan 2, 2020
I hand't seen any ABGR yet in Cerberus OpenGl source code. I guess you are right about the shader part, as that's the big thing that differs Mojo1 and Mojo2
 

Holzchopf

Moderator
3rd Party Module Dev
Joined
Jul 31, 2017
Location
Bern, Switzerland
How come that Mojo1 works so fine using a regular alpha mode instead of opaque2 blendmode?
mojo2 works fine too using a regular alpha mode - when using premultiplied ABGR ;) Because the transformation is done automatically when loading an image, users only stumble upon it when manually writing image data.

Opaque2 sounds arbitrary For being such an important thing. It makes you curious about the other modes.
Does alpha2 And opaque have special roles in Cerberus?
Opaque2 is not "such an important thing" when using mojo2 the way it was intended - with premultiplied component values. It was designed that way by the initial author. That's the way it is. Alpha2 has a special role when it comes to writing image data directly to image buffers. Opaque does not have a "special" role, it's probably the least special blend mode at all. Again, this applies when using mojo2 with premultiplied color components.
And yes, the "2" in Opaque2 might sound arbitrary - but there was even a thread here about its naming when it was introduced. We went for Opaque2 because lazily numbering identifiers is the way to go in mojo :p

I hand't seen any ABGR yet in Cerberus OpenGl source code. I guess you are right about the shader part, as that's the big thing that differs Mojo1 and Mojo2
It's hidden somewhere Texture.Load() I guess. I don't know what the idea behind using premultiplied ABGR was. Well, there are two parts to that - first ABGR vs ARGB and second premultiplied. Maybe some targets were bound to ABGR, that's why M.S. went for ABGR everywhere. Using premultiplied alpha seems to be a quality thing: https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied

Lastly: For backwards compatibility, we're stuck now with premultiplied ABGR. That's why we introduced these helper functions for people who like to manipulate image data (who doesn't?): https://www.cerberus-x.com/cxDocs/Modules_mojo2.graphics_DrawList.html#PremultiplyArgb2Abgr(Int)
 

Jimmy

Active Member
Joined
Jan 2, 2020
I didn't understand much yet how it all works together but will try to look into this, but thank you for your answer.
 
Top Bottom