Accepted Colors

Joined
Dec 13, 2018
Likes
16
Location
UK
#22
I will be nice and give you a little demo.
Similar to what I had in mind but if the objective is to specify the color in word form then why not use a map/dict/hash/table whatever its called in CX as I mentioned? Can't see that it would be a huge performance drain - if someone was making that many calls to SetColor() per frame there's probably a much better way of writing the program :D
 

Holzchopf

Moderator
Staff member
Joined
Jul 31, 2017
Likes
152
Location
Bern, Switzerland
#23
It's also a question of good practice; string identifiers < magic numbers < named consts.

But every solution has it's pros and cons.

Named constants are easier to read than just plain numbers (a.k.a. magic numbers). If you have SetColor(COLOR_DARKKHAKI) it's clearer than SetColor($BDB76B). But they're equally performant. String identifiers have to be looked up at runtime from a table, which is (due to string comparison) multiple times slower than just passing a 32bit value. But they're equally readable as named constants. With named constants, you'll be informed about typos at compile-time, with string identifiers at runtime (given you implemented this), with magic numbers, it depends on your eye to notice typos. With string identifiers, you can allow the end user to define colours (e.g. in themes) by their html name. With magic numbers, you're absolutely free when prototyping.

However: I would suggest going dawlanes way with the Color consts and additionally either make a StringMap<Color> or a ColorFromString:Color(col:String) function. Providing both at the same time minimizes typing work (there are 140? html colours I would provide and with clever copypasting, making the two out of one is no biggie).

Performance-wise it's no problem then: when you don't use the string identifier solution, there won't be any drawbacks of having there somewhere in a module. Maybe compilation will take 0.02s longer. But thanks to compilers optimizing stuff, just because they're there wouldn't mean they'd bloat up your executable.
 

dawlane

Active Member
CX Code Contributor
Joined
Jun 21, 2017
Likes
198
#24
why not use a map/dict/hash/table whatever its called in CX as I mentioned? Can't see that it would be a huge performance drain
Let's see:
  1. Memory usage, not every device is going to have Giga bytes of volatile memory available for program use.
  2. Why over complicate what is basically a simple thing that can easily be achieved with a constant data type of int.
  3. And of course there is the time taken to look up a colour in what needs to be the fastest, optimised part of a program. The Rendering.
If you need to set and get a colour currently being used. A simple wrapper class can be used:
Cerberus X:
Class Colour

    ' Constants
    Const RED:Int = $ff0000
    Const GREEN:Int = $00ff00
    Const BLUE:Int = $0000ff

    Function GetColour:Int()
        Return _colour
    End

    Function SetColor:Void(colour:Int)
        _colour = colour
        SetColor(colour Shr 16 & $ff, colour Shr 8 & $ff, colour & $ff)
    End Function

    Private
        Global _colour:Int
End Class

' Usage
Colour.SetColour(Colour.RED)

If Colour.GetColor=Colour.RED
if someone was making that many calls to SetColor() per frame there's probably a much better way of writing the program
Yes there is, using the method I've outlined and by limiting the number of times SetColor is used. The same goes for SetAlpha, DrawText and DrawImage. Each of these have a performance hit, read Render Hell.
If you need to use multiple images, use a image/sprite map and use DrawImageRect.

Edit:
additionally either make a StringMap<Color> or a ColorFromString:Color(col:String)
I would only use something like this for setting a value in a game object, not during a render.
 
Last edited:

Holzchopf

Moderator
Staff member
Joined
Jul 31, 2017
Likes
152
Location
Bern, Switzerland
#25
My preliminary proposal: See attached files

mojo/color.cxs - provides the Color class
mojo/colornames.cxs - provides the named consts and a NamedHtmlColor function taking a string and returning a hex value
mojo/graphics.cxs - provides SetColor( Color )
mojo2/graphics.cxs - provides SetColor( Color )

You can download these files and add them to your modules directory (make backups of mojo/graphics and mojo2/graphics beforehand ;) )

Then these examples should run:

Mojo1 Example

Cerberus X:
Strict

Import mojo

Function Main:Int()
    New MyApp
    Return 0
End

Class MyApp Extends App
    Field myfavouritecolour:Color
    Field leastfavouritecolour:Color
   
    Method OnCreate:Int()
        SetUpdateRate(20)
        ' define Color by named const
        myfavouritecolour = New Color( COLOR_AQUAMARINE )
        ' define Color via string identifier
        leastfavouritecolour = New Color( "darkturquoise" )
        Return 0
    End
   
    Method OnRender:Int()
        Cls(0,0,0)
        ' set color by Color
        SetColor( myfavouritecolour )
        DrawRect( 20, 20, 200, 60 )
        SetColor( leastfavouritecolour )
        DrawRect( 20, 120, 200, 60 )      
        ' set color by hex value, retreived from name (slow!)
        SetColor( NamedHtmlColor( "lightsalmon" ) )
        DrawRect( 20, 220, 200, 60 )
        ' set color by named const (fastest)
        SetColor( COLOR_BISQUE )
        DrawRect( 20, 320, 200, 60 )
        Return 0
    End
End

Mojo2 Example

Cerberus X:
Strict

Import mojo2

Function Main:Int()
    New MyApp
    Return 0
End

Class MyApp Extends App
    Field cvMain:Canvas

    Field myfavouritecolour:Color
    Field leastfavouritecolour:Color
   
    Method OnCreate:Int()
        cvMain = New Canvas
        SetUpdateRate(20)
        ' define Color by named const
        myfavouritecolour = New Color( COLOR_AQUAMARINE )
        ' define Color via string identifier
        leastfavouritecolour = New Color( "darkturquoise" )
        Return 0
    End
   
    Method OnRender:Int()
        cvMain.Clear(0,0,0)
        ' set color by Color (fastest)
        cvMain.SetColor( myfavouritecolour )
        cvMain.DrawRect( 20, 20, 200, 60 )
        cvMain.SetColor( leastfavouritecolour )
        cvMain.DrawRect( 20, 120, 200, 60 )      
        ' set color by hex value, retreived from name (slow!)
        cvMain.SetColor( NamedHtmlColor( "lightsalmon" ) )
        cvMain.DrawRect( 20, 220, 200, 60 )
        ' set color by named const
        cvMain.SetColor( COLOR_BISQUE )
        cvMain.DrawRect( 20, 320, 200, 60 )
        cvMain.Flush()
        Return 0
    End
End

If this looks about right to you, I'll take care of the documentation. Also I'll probably add Cls( Color ) and Clear( Color ) and so on
 

Attachments

Joined
Dec 13, 2018
Likes
16
Location
UK
#26
Let's see:
  1. Memory usage, not every device is going to have Giga bytes of volatile memory available for program use.
  2. Why over complicate what is basically a simple thing that can easily be achieved with a constant data type of int.
  3. And of course there is the time taken to look up a colour in what needs to be the fastest, optimised part of a program. The Rendering.
...
I agree with all those points but I thought the OP and follow up replies were calling for a method of setting colors using a string in mojo and mojo2?

I'd never write a program like that unless there was some very specific use case (and I'm struggling to think of one), I'd just use Consts and some shifting as you've done. I don't know why you'd really want to incorporate setting colors with strings into CX anyway, I'd think it has limited applications.

Yes there is, using the method I've outlined and by limiting the number of times SetColor is used.
I meant if the user was making that many calls to SetColor() they should find a better way to write their program (not a better way to incorporate it into CX). Essentially we are singing off the same hymn sheet - I probably didn't make myself clear enough, sorry.
 

Holzchopf

Moderator
Staff member
Joined
Jul 31, 2017
Likes
152
Location
Bern, Switzerland
#27
One thing: If we provide a colour module that is then imported by mojo and mojo2, we must decide to either
  1. accept that the fields r,g,b of that Color class are either Int [0 ... 255] or Float [0.0 ... 1.0] - mojo users would be used to the Int variant, mojo2 users to the Float one
  2. or - using precompiler directives - make the behaviour switchable and set the switch in mojo or mojo2 before importing the color module

The first solution would not be consistent to one of the mojos. The second one would make documenting almost impossible... Personally I'm for Nr. 1 with Int components: I looked into mojo2.graphics and to me it looks as if mojo2 is using Ints ranging [0 ... 255] internally anyway. Which makes sense, as the ogl colours by using a 32bit int representing ARGB byte components (I'm still confused by the use of ABGR in case of mojo2...).


Edit

Furthermore: Should we go for $AARRGGBB or $RRGGBB? I'd say $RRGGBB only because otherwise setting e.g. SetColor($ff0000) would result in nothing being visible as the alpha component was left blank. Thoughts?
 
Last edited:

Phil7

Active Member
Joined
Jun 26, 2017
Likes
61
#28
What about a basic implementation inside the mojo /mojo2 module. You don't have to switch and it can be seen as a convenient enhancement of mojo1/2.
 

Holzchopf

Moderator
Staff member
Joined
Jul 31, 2017
Likes
152
Location
Bern, Switzerland
#29
That would mean having the code twice, once in mojo with components going 0...255 and once in mojo2 with components going to 0...1 for user "experience" (while in the background mojo2 silently stretches those components to 0...255). Well I'm honestly no fan of that. Even though I'm a happy user of mojo2 and usually am all for consistency - I wouldn't mind having a Color class that openly uses 0...255 ranging colour components. They just seem so natural :oops:
 

Holzchopf

Moderator
Staff member
Joined
Jul 31, 2017
Likes
152
Location
Bern, Switzerland
#31
New version for you to test is up - backup mojo/graphics.cxs, mojo2/mojo2.cxs and mojo2/graphics.cxs beforehand, if you want to be able to revert later.

Still no .cerberusdoc documentation, but I'll summarize a bit:

This provides:

Class Color
  • color component fields r,g,b (either Ints 0-255 or Floats 0.0-1.0 depending on mojo version you use)
  • modification of color's hue, saturation and brightness
  • mixing of colors in rgb and hsb space

Function NamedHtmlColor:Int( name:String )
  • returns a color value for a given html color name, e.g. "Red" would return $ff0000

Consts COLOR_ALICEBLUE etc.
  • all 140 html colors as Int constants

Modifications to mojo/mojo2
  • SetColor( Color )
  • Cls/Clear( hex )
  • Cls/Clear( Color )

Example for mojo
Cerberus X:
Strict

Import mojo

Function Main:Int()
    New MyApp
    Return 0
End

Class MyApp Extends App
    Field colfixed:Color
    Field colmod:Color
    Field colmodhue:Color
    Field colmodsat:Color
    Field colmodbri:Color
    
    Field color1:Color, color2:Color
    Field colorhmix:Color, colorcmix:Color
    
    Method OnCreate:Int()
        SetUpdateRate(20)
        ' define Color by named const
        colfixed = New Color( COLOR_AQUAMARINE )
        ' define Color by hex value
        colmod = New Color( $99ffcc )
        ' define Color via string identifier
        colmodhue = New Color( "darkturquoise" )
        ' define Color by rgb triplet
        colmodsat = New Color( 255, 0, 0 )
        ' define Color by rbg array
        colmodbri = New Color( [128,0,255] )
        ' define Colors to mix by hex value
        color1 = New Color( $ff0000 )
        color2 = New Color( $00ff00 )
        colorhmix = New Color()
        colorcmix = New Color()
        Return 0
    End
    
    Method OnUpdate:Int()
        Local at:Float = Millisecs() / 4000.0 * 360.0
        ' modulate red component
        colmod.r = 127.5 + 127.5 * Sin(at)
        ' modulate hue
        colmodhue.hue += 1
        ' modulate saturation
        colmodsat.sat = 0.5 + 0.5*Sin(at)
        ' modulate brightness
        colmodbri.bri = 0.5 + 0.5*Sin(at)
        Return 0
    End
    
    Method OnRender:Int()
        Local val:Float
        Cls( COLOR_DARKSLATEGREY )
        SetBlend( AlphaBlend )
        ' fixed color
        SetColor( COLOR_WHITE )
        DrawText( "fixed color", 0+20, 0+5 )
        SetColor( colfixed )
        DrawRect( 0+20, 0+20, 280, 50 )
        ' fixed color
        SetColor( COLOR_WHITE )
        DrawText( "fixed color 2", 320+20, 0+5 )
        SetColor( NamedHtmlColor( "lightsalmon" ) )
        DrawRect( 320+20, 0+20, 280, 50 )
        ' demo: component change
        SetColor( COLOR_WHITE )
        DrawText( "one component changing color, r="+colmod.r, 0+20, 80+5 )
        SetColor( colmod )
        DrawRect( 0+20, 80+20, 280, 50 )
        ' demo: hue change
        val = Int(colmodhue.hue * 100)/100.0
        SetColor( COLOR_WHITE )
        DrawText( "hue changing color, hue="+val, 320+20, 80+5 )
        SetColor( colmodhue )
        DrawRect( 320+20, 80+20, 280, 50 )
        ' demo: saturation change
        val = Int(colmodsat.sat * 100)/100.0
        SetColor( COLOR_WHITE )
        DrawText( "saturation changing color, sat="+val, 0+20, 160+5 )
        SetColor( colmodsat )
        DrawRect( 0+20, 160+20, 280, 50 )
        ' demo: brightness change
        val = Int(colmodbri.bri * 100)/100.0
        SetColor( COLOR_WHITE )
        DrawText( "brightness changing color, bri="+val, 320+20, 160+5 )
        SetColor( colmodbri )
        DrawRect( 320+20, 160+20, 280, 50 )
        ' demo: color mixing
        SetColor( COLOR_WHITE )
        DrawText( "color mixing in RGB space", 0+20, 320+5 )
        DrawText( "color mixing in HSB space", 0+20, 400+5 )
        For Local i:Int = 0 To 19
            Local mix:Float = i / 19.0
            colorcmix.Mix( color1, color2, mix )
            colorhmix.MixHsb( color1, color2, mix )
            SetColor( colorcmix )
            DrawRect( 20+i*30, 340, 30, 50 )
            SetColor( colorhmix )
            DrawRect( 20+i*30, 420, 30, 50 )
        Next
        Return 0
    End
End

Example for mojo2
Cerberus X:
Strict

Import mojo2

Function Main:Int()
    New MyApp
    Return 0
End

Class MyApp Extends App
    Field cvMain:Canvas
    Field colfixed:Color
    Field colmod:Color
    Field colmodhue:Color
    Field colmodsat:Color
    Field colmodbri:Color
    
    Field color1:Color, color2:Color
    Field colorhmix:Color, colorcmix:Color
    
    Method OnCreate:Int()
        cvMain = New Canvas()
        SetUpdateRate(20)
        ' define Color by named const
        colfixed = New Color( COLOR_AQUAMARINE )
        ' define Color by hex value
        colmod = New Color( $99ffcc )
        ' define Color via string identifier
        colmodhue = New Color( "darkturquoise" )
        ' define Color by rgb triplet
        colmodsat = New Color( 1, 0, 0 )
        ' define Color by rbg array
        colmodbri = New Color( [0.1,0,0.1] )
        ' define Colors to mix by hex value
        color1 = New Color( $ff0000 )
        color2 = New Color( $00ff00 )
        colorhmix = New Color()
        colorcmix = New Color()
        Return 0
    End
    
    Method OnUpdate:Int()
        Local at:Float = Millisecs() / 4000.0 * 360.0
        ' modulate red component
        colmod.r = 0.5 + 0.5 * Sin(at)
        ' modulate hue
        colmodhue.hue += 1
        ' modulate saturation
        colmodsat.sat = 0.5 + 0.5*Sin(at)
        ' modulate brightness
        colmodbri.bri = 0.5 + 0.5*Sin(at)
        Return 0
    End
    
    Method OnRender:Int()
        Local val:Float
        cvMain.Clear( COLOR_DARKSLATEGREY )
        cvMain.SetBlendMode( BlendMode.Alpha )
        ' fixed color
        cvMain.SetColor( COLOR_WHITE )
        cvMain.DrawText( "fixed color", 0+20, 0+5 )
        cvMain.SetColor( colfixed )
        cvMain.DrawRect( 0+20, 0+20, 280, 50 )
        ' fixed color
        cvMain.SetColor( COLOR_WHITE )
        cvMain.DrawText( "fixed color 2", 320+20, 0+5 )
        cvMain.SetColor( NamedHtmlColor( "lightsalmon" ) )
        cvMain.DrawRect( 320+20, 0+20, 280, 50 )
        ' demo: component change
        val = Int(colmod.r * 100)/100.0
        cvMain.SetColor( COLOR_WHITE )
        cvMain.DrawText( "one component changing color, r="+val, 0+20, 80+5 )
        cvMain.SetColor( colmod )
        cvMain.DrawRect( 0+20, 80+20, 280, 50 )
        ' demo: hue change
        val = Int(colmodhue.hue * 100)/100.0
        cvMain.SetColor( COLOR_WHITE )
        cvMain.DrawText( "hue changing color, hue="+val, 320+20, 80+5 )
        cvMain.SetColor( colmodhue )
        cvMain.DrawRect( 320+20, 80+20, 280, 50 )
        ' demo: saturation change
        val = Int(colmodsat.sat * 100)/100.0
        cvMain.SetColor( COLOR_WHITE )
        cvMain.DrawText( "saturation changing color, sat="+val, 0+20, 160+5 )
        cvMain.SetColor( colmodsat )
        cvMain.DrawRect( 0+20, 160+20, 280, 50 )
        ' demo: brightness change
        val = Int(colmodbri.bri * 100)/100.0
        cvMain.SetColor( COLOR_WHITE )
        cvMain.DrawText( "brightness changing color, bri="+val, 320+20, 160+5 )
        cvMain.SetColor( colmodbri )
        cvMain.DrawRect( 320+20, 160+20, 280, 50 )
        ' demo: color mixing
        cvMain.SetColor( COLOR_WHITE )
        cvMain.DrawText( "color mixing in RGB space", 0+20, 320+5 )
        cvMain.DrawText( "color mixing in HSB space", 0+20, 400+5 )
        For Local i:Int = 0 To 19
            Local mix:Float = i / 19.0
            colorcmix.Mix( color1, color2, mix )
            colorhmix.MixHsb( color1, color2, mix )
            cvMain.SetColor( colorcmix )
            cvMain.DrawRect( 20+i*30, 340, 30, 50 )
            cvMain.SetColor( colorhmix )
            cvMain.DrawRect( 20+i*30, 420, 30, 50 )
        Next
        cvMain.Flush()
        Return 0
    End
End

Feedback is welcome.
 

Attachments

dawlane

Active Member
CX Code Contributor
Joined
Jun 21, 2017
Likes
198
#32
You should not be importing the colour module directly in to the graphics.cxs. It should be placed in the main mojo import file or added as a separate module. Not everyone will require it.

I understand it the Const keyword will have a memory foot print adding to the over-all size of the application. So if you are not going to use it you will end up taking up memory that may cause problems on devices where memory is limited.
 

Holzchopf

Moderator
Staff member
Joined
Jul 31, 2017
Likes
152
Location
Bern, Switzerland
#33
If graphics does not import it, then graphics can not provide a SetColor( Color ) function/method.

Furthermore, transcc optimises your code upon translation - stuff you don't use will not find their way in your application*. For functions, classes, methods and fields I'm certain of that. For consts I haven't tested, but I'm almost certain it would behave the same as the optimisation is based on a flag that's set when a declaration is invoked/accessed and I believe to remember that the crumpet of code responsible for that does not distinguish between whatever kind of decl it is. AND: even if the consts would stay in, we're talking about 148 32bit ints - 592 bytes of memory. What I'm saying is: THIS is not going to cause problems on devices where memory is limited ;) you have my word.

* until I learned about this, I shared your opinion completely. I'm no fan of libraries that mostly consist of fillings. But luckily trans does a good job there by removing the stuff an over-motivated dev put into his code =)
 

Gerry Quinn

Active Member
Joined
Jun 24, 2017
Likes
44
#36
Heh, long ago I made a module with an array of Float[][3] and named const ints used as indexes. Never thought of packing the colours themselves into the consts!
 
Top Bottom