• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] [Snippet] Color

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
JASS:
library Color requires HexString
    
    /*
                    [Snippet] Color v1.2
                            by Quilnez
        
        Descriptions:
            This snippet allows you to store (create) colors
            and store it both in string and integer version.
            As you are also able to get the color compositions.
        
        API:
            struct Color
            
                Constructor
                    | static method new takes integer alpha, integer red, integer green, integer blue returns thistype
                        - Create new color
                        
                Destructor
                    | method destroy takes nothing returns nothing
                        - Destroy Color
                        
                Composer
                    | method operator alphaInt= takes integer value returns nothing
                    | method operator redInt=   takes integer value returns nothing
                    | method operator greenInt= takes integer value returns nothing
                    | method operator blueInt=  takes integer value returns nothing
                        - Modify the color's composition in decimal format
                        
                    | method operator alphaHex= takes string value returns nothing
                    | method operator redHex=   takes string value returns nothing
                    | method operator greenHex= takes string value returns nothing
                    | method operator blueHex=  takes string value returns nothing
                        - Modify the color's composition in hexadecimal format
                
                Reserver
                    | method operator alpha takes nothing returns Color
                    | method operator red   takes nothing returns Color
                    | method operator green takes nothing returns Color
                    | method operator blue  takes nothing returns Color
                        - Reserve color's specific composition
                
                    | method operator mix takes nothing returns string
                        - Reserve returned color without alpha
                    | method operator mixEx takes nothing returns string
                        - Reserve returned color with alpha
                
                Returner
                    | method operator color takes nothing returns string
                        - Return color directly in string format (no alpha)
                        
                    | method operator str takes nothing returns string
                        - Get reserved value in string format (hexadecimal)
                    | method operator int takes nothing returns integer
                        - Get reserved value in integer format (decimal)
                    
                Static member
                    static constant string PREFIX = "|C"
                    static constant string SUFFIX = "|R"
        
        Credits:
            HexString by Bannar
                hiveworkshop.com/forums/jass-resources-412/snippet-hexstring-248993/

    */
    
    //! textmacro NEW_COLOR takes name
        private integer _$name$Int
        method operator $name$Int= takes integer value returns nothing
            set ._$name$Int = value
            set ._$name$Hex = format(I2HS(value, true))
        endmethod
        
        private string _$name$Hex
        method operator $name$Hex= takes string value returns nothing
            set ._$name$Int = HS2I(value)
            set ._$name$Hex = format(value)
        endmethod
        
        method operator $name$ takes nothing returns thistype
            set .dec = ._$name$Int
            set .hex = ._$name$Hex
            return this
        endmethod
    //! endtextmacro 
    
    private module ColorModule
        //! runtextmacro NEW_COLOR("alpha")
        //! runtextmacro NEW_COLOR("red")
        //! runtextmacro NEW_COLOR("green")
        //! runtextmacro NEW_COLOR("blue")
    endmodule
    
    struct Color
        
        private integer dec
        private string  hex
        
        static constant string PREFIX = "|C"
        static constant string SUFFIX = "|R"
        static constant boolean JASS_MODE = true
        
        implement ColorModule
        
        private method format takes string s returns string
            if StringLength(s) > 1 then
                return s
            else
                return "0"+s
            endif
        endmethod
        
        method operator str takes nothing returns string
            local string s = .hex
            set .hex = ""
            return s
        endmethod
        
        method operator int takes nothing returns integer
            local integer i = .dec
            set .dec = 0
            return i
        endmethod
        
        method operator color takes nothing returns string
            static if thistype.JASS_MODE then
                return PREFIX+"FF"+.red.hex+.green.hex+.blue.hex
            else
                return .red.hex+.green.hex+.blue.hex
            endif
        endmethod
        
        method operator mix takes nothing returns thistype
        
            set .hex = .red.hex+.green.hex+.blue.hex
            set .dec = HS2I(.hex)
            static if thistype.JASS_MODE then
                set .hex = PREFIX+"FF"+.hex
            endif
            
            return this
        endmethod
        
        method operator mixEx takes nothing returns thistype
        
            set .hex = .alpha.hex+.red.hex+.green.hex+.blue.hex
            set .dec = HS2I(.hex)
            static if thistype.JASS_MODE then
                set .hex = PREFIX+.hex
            endif
            
            return this
        endmethod
        
        method destroy takes nothing returns nothing
            static if LIBRARY_ColorEx then
                if ColorEx(this).nextColor != 0 then
                    call ColorEx(this).nextColor.destroy()
                endif
            endif
            call deallocate()
        endmethod
        
        static method new takes integer alpha, integer red, integer green, integer blue returns thistype
            
            local thistype this = allocate()
            
            set .alphaInt  = alpha
            set .redInt    = red
            set .greenInt  = green
            set .blueInt   = blue
            
            set .alphaHex  = format(I2HS(alpha, true))
            set .redHex    = format(I2HS(red, true))
            set .greenHex  = format(I2HS(green, true))
            set .blueHex   = format(I2HS(blue, true))
            
            static if LIBRARY_ColorEx then
                set ColorEx(this).nextColor = 0
            endif
            
            static if LIBRARY_AdvColor then
                set AdvColor(this).contrast   = 0
                set AdvColor(this).brightness = 0
            endif
            
            return this
        endmethod
        
    endstruct
    
endlibrary

JASS:
library ColorEx requires Color
    
    /*
                    [Snippet] ColorEx v1.0
                            by Quilnez
        
        Descriptions:
            An extension to Color library. Allows you to
            apply gradation/gradient effect to anything.
        
        API:
            struct ColorEx
                (inherits all methods from Color struct)
            
                | method setTargetColor takes integer alpha, integer red, integer green, integer blue, integer length returns nothing
                | method setTargetColorEx takes Color color, integer length returns nothing
                    - Set gradation target color
                    
                | method next takes boolean alpha returns string
                    - Get the next color (transition) in string
                | method nextEx takes nothing returns Color
                    - Get the next color (transition) in a new Color instance
        
        Credits:
            HexString by Bannar
                hiveworkshop.com/forums/jass-resources-412/snippet-hexstring-248993/

    */
    
    struct ColorEx extends Color
        
        private integer alphaInc
        private integer redInc
        private integer greenInc
        private integer blueInc
        
        private integer count
        private integer length
        public  thistype nextColor
        
        method next takes boolean alpha returns string
        
            local string s
            
            if .nextColor != 0 then
                set .nextColor.alphaInt = .nextColor.alpha.int + .alphaInc
                set .nextColor.redInt   = .nextColor.red.int + .redInc
                set .nextColor.greenInt = .nextColor.green.int + .greenInc
                set .nextColor.blueInt  = .nextColor.blue.int + .blueInc
                
                set s = .nextColor.red.str+.nextColor.green.str+.nextColor.blue.str
                if alpha then
                    set s = .nextColor.alpha.str+s
                else
                    set s = "FF"+s
                endif
                
                static if Color.JASS_MODE then
                    set s = Color.PREFIX+s
                endif
            else
                if alpha then
                    set s = .mixEx.str
                else
                    set s = .mix.str
                endif
            endif
            
            set .count = .count + 1
            if .count == .length then
                set .count    = 0
                set .alphaInc = -.alphaInc
                set .redInc   = -.redInc
                set .greenInc = -.greenInc
                set .blueInc  = -.blueInc
            endif
            
            return s
        endmethod
        
        method nextEx takes nothing returns thistype
            
            if .count == .length then
                set .count    = 0
                set .alphaInc = -.alphaInc
                set .redInc   = -.redInc
                set .greenInc = -.greenInc
                set .blueInc  = -.blueInc
            endif
        
            set .count = .count + 1
            if .nextColor != 0 then
                set .nextColor.alphaInt = .nextColor.alpha.int + .alphaInc
                set .nextColor.redInt   = .nextColor.red.int + .redInc
                set .nextColor.greenInt = .nextColor.green.int + .greenInc
                set .nextColor.blueInt  = .nextColor.blue.int + .blueInc
                return new(.nextColor.alpha.int, .nextColor.red.int, .nextColor.green.int, .nextColor.blue.int)
            else
                return new(.alpha.int, .red.int, .green.int, .blue.int)
            endif
            
        endmethod
        
        method setTargetColor takes integer alpha, integer red, integer green, integer blue, integer length returns nothing
        
            if .nextColor == 0 then
                set .nextColor = new(.alpha.int, .red.int, .green.int, .blue.int)
            else
                set .nextColor.alphaInt = .alpha.int
                set .nextColor.redInt   = .red.int
                set .nextColor.greenInt = .green.int
                set .nextColor.blueInt  = .blue.int
            endif
            
            set .count    = 0
            set .length   = length
            set .alphaInc = (alpha-.alpha.int)/length
            set .redInc   = (red-.red.int)/length
            set .greenInc = (green-.green.int)/length
            set .blueInc  = (blue-.blue.int)/length
            
        endmethod
        
        method setTargetColorEx takes thistype color, integer length returns nothing
            
            if .nextColor == 0 then
                set .nextColor = new(.alpha.int, .red.int, .green.int, .blue.int)
            else
                set .nextColor.alphaInt = .alpha.int
                set .nextColor.redInt   = .red.int
                set .nextColor.greenInt = .green.int
                set .nextColor.blueInt  = .blue.int
            endif
            
            set .count    = 0
            set .length   = length
            set .alphaInc = (color.alpha.int-.alpha.int)/.length
            set .redInc   = (color.red.int-.red.int)/.length
            set .greenInc = (color.green.int-.green.int)/.length
            set .blueInc  = (color.blue.int-.blue.int)/.length
            
        endmethod
        
    endstruct
    
endlibrary

JASS:
    /*
                    [Snippet] AdvColor v1.0
                            by Quilnez
        
        Descriptions:
            An extension to Color(Ex) library. Allows you to
            apply some advanced color operations such as:
                - Grayscale color
                - Negative color (invert color)
                - Sepia color scheme
                - Get a/r/g/b channel
                - Adjust contrast/brightness
        
        API:
            struct AdvColor
                (inherits all methods from Color (and ColorEx) struct)
                
                | method operator contrast takes nothing returns integer
                | method operator contrast= takes integer value returns nothing
                    - Get/set contrast
                
                | method operator brightness takes nothing returns integer
                | method operator brightness= takes integer value returns nothing
                    - Get/set brightness
                    
                | method invert takes nothing returns nothing
                    - Invert color (negative color effect)
                
                | method grayscale takes integer style returns nothing
                | method sepia takes nothing returns nothing
                    - Apply grayscale/sepia color effect
                    
                Static members
                    (Grayscale styles)
                    AdvColor.AVERAGE
                    AdvColor.LUMINOSITY
                    AdvColor.DESATURATION
                    AdvColor.MINIMUM_DECOMPOSITION
                    AdvColor.MAXIMUM_DECOMPOSITION
        
        Credits:
            HexString by Bannar
                hiveworkshop.com/forums/jass-resources-412/snippet-hexstring-248993/
    */
        // You can choose between Color or ColorEx, which you want to extend
                //! runtextmacro TO_EXTEND("ColorEx")
        // Extends Color means you can't use the functionalities of ColorEx
        // Extends ColorEx means you can use the functionalities of both Color and ColorEx
        
//! textmacro NEW_CHANNEL takes name
    method $name$Channel takes nothing returns thistype
        return new(255, .$name$.int, .$name$.int, .$name$.int)
    endmethod
//! endtextmacro
    
module AdvColor__ChannelModule
    //! runtextmacro NEW_CHANNEL("alpha")
    //! runtextmacro NEW_CHANNEL("red")
    //! runtextmacro NEW_CHANNEL("green")
    //! runtextmacro NEW_CHANNEL("blue")
endmodule
    
//! textmacro TO_EXTEND takes name
library AdvColor requires $name$
    
    struct AdvColor extends $name$
        
        private integer contra
        private integer bright
        
        static constant integer AVERAGE               = 0
        static constant integer LUMINOSITY            = 1
        static constant integer DESATURATION          = 2
        static constant integer MINIMUM_DECOMPOSITION = 3
        static constant integer MAXIMUM_DECOMPOSITION = 4
    
        private static constant real RED_LUMINOSITY   = 0.299
        private static constant real GREEN_LUMINOSITY = 0.587
        private static constant real BLUE_LUMINOSITY  = 0.114
        
        private method findMin takes integer a, integer b, integer c returns integer
            return IMinBJ(IMinBJ(a, b), c)
        endmethod
        
        private method findMax takes integer a, integer b, integer c returns integer
            return IMaxBJ(IMaxBJ(a, b), c)
        endmethod
        
        private method trunc takes integer a returns integer
            if a < 0 then
                set a = 0
            elseif a > 255 then
                set a = 255
            endif
            return a
        endmethod
        
        method operator contrast takes nothing returns integer
            return contra
        endmethod
        
        method operator contrast= takes integer value returns nothing
            
            local real fact
            
            if value > 255 then
                set value = 255
            elseif value < -255 then
                set value = -255
            endif
            set contra = value
            
            set fact = (259*(contra+255))/(255*(259-contra))
            set .redInt   = trunc(R2I(fact*(.red.int-128))+128)
            set .greenInt = trunc(R2I(fact*(.green.int-128))+128)
            set .blueInt  = trunc(R2I(fact*(.blue.int-128))+128)
            
        endmethod
        
        method operator brightness takes nothing returns integer
            return bright
        endmethod
        
        method operator brightness= takes integer value returns nothing
        
            if value > 255 then
                set value = 255
            elseif value < -255 then
                set value = -255
            endif
            set bright = value
            
            set .redInt   = trunc(.red.int+bright)
            set .greenInt = trunc(.green.int+bright)
            set .blueInt  = trunc(.blue.int+bright)
            
        endmethod
        
        method invert takes nothing returns nothing
            set .redInt   = 255-.red.int
            set .greenInt = 255-.green.int
            set .blueInt  = 255-.blue.int
        endmethod
        
        method grayscale takes integer style returns nothing
        
            local integer c
            
            if style == AVERAGE then
                set c = (.red.int+.green.int+.blue.int)/3
            elseif style == LUMINOSITY then
                set c = R2I((.red.int*RED_LUMINOSITY)+(.green.int*GREEN_LUMINOSITY)+(.blue.int*BLUE_LUMINOSITY))
            elseif style == DESATURATION then
                set c = (findMin(.red.int, .green.int, .blue.int)+findMax(.red.int, .green.int, .blue.int))/2
            elseif style == MINIMUM_DECOMPOSITION then
                set c = findMin(.red.int, .green.int, .blue.int)
            elseif style == MAXIMUM_DECOMPOSITION then
                set c = findMax(.red.int, .green.int, .blue.int)
            endif
            
            set .redInt   = c
            set .greenInt = c
            set .blueInt  = c
            
        endmethod
        
        method sepia takes nothing returns nothing
            set .redInt   = trunc(R2I((.red.int*.393)+(.green.int*.769)+(.blue.int*.189)))
            set .greenInt = trunc(R2I((.red.int*.349)+(.green.int*.686)+(.blue.int*.168)))
            set .blueInt  = trunc(R2I((.red.int*.272)+(.green.int*.534)+(.blue.int*.131)))
        endmethod
        
        implement AdvColor__ChannelModule
        
    endstruct
    
endlibrary
//! endtextmacro

How to use:
JASS:
/*

    A. Color

        1. Declaring types
        
                        Color < ColorEx < AdvColor
                        
            Means if you declare variable c as Color, c can't use functions
            provided by ColorEx and AdvColor. But if you declare c as AdvColor,
            c may use all functions provided by Color (and ColorEx, depends
            on your configuration).
            
        2. Assigning composition
            
            Color c = Color.new(1, 2, 3, 4)
            
            c.redInt = 255  => change red composition from 2 to 255
            c.redHex = "FF" => change red composition from 2 to 255
            etc...
            
        3. Getting color
            
            Color c = Color.new(1, 2, 3, 4)
            
            c.color     => return "|CFF020304"
            c.mix.hex   => return "|CFF020304"
            c.mixEx.hex => return "|C01020304"
            
            c.red.int   => return 2 (as integer)
            c.red.hex   => return "02" (as string)
            etc...
            
    B. ColorEx
        
        1. Initializing Color transition
        
            Color c = Color.new(255, 255, 0, 0)   => original color is red
            call c.setTargetColor(255, 0, 255, 0, length) => target color is green
            
        2. Applying gradations
        
            (example 1 - Using .next(boolean alpha))
            
                loop
                    exitwhen i == length
                    set result = result + c.next(false) + SubString(msg, i, i+1)
                    set i = i + 1
                endloop
                
            (example 2 - Using .nextEx)
            
                loop
                    exitwhen i == 50
                    set next = c.nextEx()
                    set u = CreateUnit(Player(0), 'hfoo', 64*i, 0, 270)
                    call SetUnitVertexColor(u, next.red.int, next.green.int, next.blue.int, next.alpha.int)
                    call next.destroy()
                    set i = i + 1
                endloop
*/
 

Attachments

  • [Snippet] Color.w3x
    31.4 KB · Views: 65
Last edited:
I think this system has decent potential. But the design needs work.

It is awkward to make both a singular color (e.g. "red") and an RGB pair considered the same entity. If you want rgb components to be objects, they should be a separate struct (e.g. ColorValue) to signify that they are separate objects. Although, even then--it is a bit awkward because then you would limit yourself to 8192/4 = 2048 objects. So instead, I would just add members for "red", "green", "blue", "alpha" and "redHex", "greenHex", "blueHex", and "alphaHex".

And I'm not sure what valueEx and mixed are for. They aren't listed in the documentation. IMO, .hex should just be updated whenever one of the color components is changed, and then the user can just read .hex directly.

Also, I would consider having a separate struct for gradients, in case the user just wants a container. So all in all, I would end up designing it something like this:
JASS:
struct Color
    private integer _red
    private integer _green
    private integer _blue
    private integer _alpha
    private string _redHex
    private string _greenHex
    private string _blueHex
    private string _alphaHex // not really necessary since alphas don't work in strings

    /* operators and functions */
endstruct

struct ColorGradient
    /* relevant members and functions */
endstruct

IMO that is a bit more modular and will simplify some of the usage a bit. Another issue with the current design is that if a user does this:
JASS:
local Color rgb = Color.new(100, 200, 0, 0)
local Color red = rgb.red
local Color green = rgb.green
call BJDebugMsg(I2S(red.int))
call BJDebugMsg(I2S(green.int))

The code will display "200" twice, which is a bit counter-intuitive.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
@PnF:
Ah, at last someone is being positive :D

Well, I will consider your suggestions but I have some points:
So instead, I would just add members for "red", "green", "blue", "alpha" and "redHex", "greenHex", "blueHex", and "alphaHex".
I'm doing this already :)

And I'm not sure what valueEx and mixed are for. They aren't listed in the documentation. IMO, .hex should just be updated whenever one of the color components is changed, and then the user can just read .hex directly.
Yeah, the documentation isn't very completed yet.
valueEx is actually mixedEx, I forgot to rename that one. mixedEx and mixed are actually the same, but mixed returns the color without alpha, while mixedEx returns the color with the alpha included. Maybe I will rename both to "mixture", not sure.

Also, I would consider having a separate struct for gradients, in case the user just wants a container. So all in all, I would end up designing it something like this:
Alrighty!

The code will display "200" twice, which is a bit counter-intuitive.
Because that's not how to do it, I suppose. Should be
JASS:
local Color rgb = Color.new(100, 200, 0, 0)
local Color red = rgb.red // => invalid
local Color green = rgb.green // => invalid
call BJDebugMsg(I2S(red.red.int)) // => getting red composition
call BJDebugMsg(I2S(green.green.int)) // => getting green composition

.red (example) operator doesn't return a Color instance actually, but it preserves the value of "hex" and "int" struct member. So if you really want to get the compositions, here are some example how to do it:

Color c = Color.new(1, 2, 3, 4)

c.alpha.hex => returns "01"
c.alpha.int => returns 1
c.red.hex => returns "02"
c.red.int => returns 2

c.color => returns "020304" (automatically returns hex without alpha)
c.mixed.hex => the same as above
c.mixedEx.hex => returns "01020304"

but, currently
c.mixed.int => returns 0
c.mixedEx.int => returns 0

I'm not sure how the last two should be like.

Thanks again for being positive :) You are rare species. :grin:
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
ARGB exists and is a very widely-used standard. If you like efficiency and modularity, Nestharus has coded something similar.

For a new standard to come about, it needs to have a clearly-defined identity. This looks like a hobby project and not a suitable replacement to the more advanced ARGB. I am scheduling this for graveyard.

For reference, here's Nes' really really good String Colorizer: https://github.com/nestharus/JASS/blob/master/jass/Systems/StringColors/script.j
 
Top