Author Topic: Change a sprite's hue, saturation and brightness  (Read 5522 times)

Offline Wampus

  • Prof. Inline
  • *****
  • Posts: 1004
    • View Profile
UPDATE!

The function ShiftColorValue has been improved. The calculations have been optimised and it only does the minimum  work needed to get its result. You can now also saturate and brighten the original sprite more than the original values. All round a better approach. The new routine has been attached to this post.

See my second post below on this thread for an explanation of the ShiftColorValue function

Back to OP:-

---------------------------

Wrote a routine to see how I could use SPRITE2MEM and MEM2SPRITE to create new sprites with different colour schemes. This sort of thing was seen a lot in the 16-bit era. Example screenshot:-



In this demo you can adjust the hue (rainbow bar), the brightness (dark to light bar) and saturation (gray to red bar) of Sagat's flesh, bandages, belt and shorts. Click 'Change' to have a look at the new sprite you've created with your settings and 'Default' to return to default  ;/

Just compile HueShiftExample.gbap to have a look.

[attachment deleted by admin]
« Last Edit: 2011-Jan-29 by Ragaril »

Offline Kitty Hello

  • code monkey
  • Administrator
  • Prof. Inline
  • *******
  • Posts: 10697
  • here on my island the sea says 'hello'
    • View Profile
    • http://www.glbasic.com
Wow, that's handy!

Offline Albert

  • Dr. Type
  • ****
  • Posts: 257
    • View Profile
    • Blog
WoW!
Exactly what I need.

Offline Moru

  • Administrator
  • Prof. Inline
  • *******
  • Posts: 1774
    • View Profile
    • Homepage
Nice, just what we were discussing last year :-)

Offline Hark0

  • Prof. Inline
  • *****
  • Posts: 1020
  • Geek Developer
    • View Profile
    • LitioPixel - Desarrollo de videojuegos con GLBasic | Videogame development with GLBasic
Cool!
 ;)
http://litiopixel.blogspot.com
litiopixel.blogspot.com - Desarrollo videojuegos Indie · Pixel-Art · Retroinformática · Electrónica Development Indie Videogames · Pixel-Art · Retrocomputing · Electronic

Offline Wampus

  • Prof. Inline
  • *****
  • Posts: 1004
    • View Profile
Ok, so clear explanation of ShiftColorValue function

newcolor% = ShiftColorValue ( oldcolor%,  brightness%,  saturation%,  hue% )

The variable newcolor is given a value in RGBA calculated from oldcolor according to settings brightness, saturation and hue.

brightness can be given values from 0 to 200. 0 is totally black. 200 is totally white. 100 is normal brightness.

saturation can be given values from 0 to 200. 0 is totally colourless - black & white. 200 is maximum saturation. 100 is normal saturation. Be careful using values above 100. While this can work well much of the time it can produce odd results in some sprites when brightness is over 100.

hue can be given values from 0 to 359. 0 is no change whatsoever. 180 results in the complete opposite colour.

Code: GLBasic [Select]
FUNCTION ShiftColorValue: color, bri, sat, hue

// color is the original color to change according to the following values:-

// bri = brightness. Can be 0 to 100 to 200. 0 = total darkness. 100 = normal light. over 100 you'll get a bleached look - "over" brightness
// sat = saturation. Can be 0 to 100 to 200. 0 = black & white. 100 = normal saturation. over 100 is "over" saturation. More colourful.
// hue = hue shift. Can be 0 to 359. 0 is no change.

LOCAL hu, cmax%, cmin%, diff, r%, g%, b%, a%, tmin%, tdiff%

a = BAND(ASR(color,24),0xff)

IF a = 0
       
        RETURN color
       
ELSE

        r = BAND(color,0xff)
        g = BAND(ASR(color,8),0xff)
        b = BAND(ASR(color,16),0xff)
       
        cmax = MAX(b,MAX(r,g))
        cmin = MIN(b,MIN(r,g))
        diff = cmax-cmin
       
        // hue calculations - hu = hue of color%
       
        IF hue <> 0
       
                IF r > g
                        IF g > b        // 7 Between red and yellow
                                hu = ((g-b)/diff)*60
                        ELSEIF b > g
                                IF b = r
                                        hu = 300 // Pure Magnenta
                                ELSE
                                        hu = 300 + ((r-b)/diff)*60      // 12 Between Magnenta and Red
                                ENDIF
                        ELSE // g = b
                                hu = 0          // 1 Pure Red
                        ENDIF
       
                ELSEIF g > b
                        IF b > r        // 9 Between green and cyan
                                hu = 120 + ((b-r)/diff)*60
                        ELSEIF r > b    // 8 Between yellow and green
                                IF r = g        // Pure Yellow
                                        hu = 60
                                ELSE
                                        hu = 60 + ((g-r)/diff)*60       // 8 Between yellow and green
                                ENDIF
                        ELSE // b = r
                                hu = 120 // 3 Pure green
                        ENDIF
       
                ELSE // b > r
                        IF r > g        // 11 Between blue and magnenta
                                hu = 240 + ((r-g)/diff)*60
                        ELSEIF g > r
                                IF g = b        // Pure Cyan
                                        hu = 180
                                ELSE
                                        hu = 180 + ((b-g)/diff)*60      // 10 Between cyan and blue
                                ENDIF
                        ELSE // r = g
                                hu = hu = 240   // 5 Pure blue
                        ENDIF
       
                ENDIF
       
       
                // Adjust hue
       
                hue = hue + hu
       
                IF hue >= 360 THEN DEC hue, 360
       
                IF hue > 0 AND hue < 60 // Between red and yellow
       
                        r = cmax
                        b = cmin
                        g = cmin+(hue*(diff/60))
       
                ELSEIF hue > 60 AND hue < 120           // Between Yellow and green
       
                        g = cmax
                        b = cmin
                        r = cmax-((hue-60)*(diff/60))
       
                ELSEIF hue > 120 AND hue < 180          // Between green and cyan
       
                        g = cmax
                        r = cmin
                        b = cmin+((hue-120)*(diff/60))
       
                ELSEIF hue > 180 AND hue < 240          // Between cyan and blue
       
                        b = cmax
                        r = cmin
                        g = cmax-((hue-180)*(diff/60))
       
                ELSEIF hue > 240 AND hue < 300          // Betweeen blue and magnenta
       
                        b = cmax
                        g = cmin
                        r = cmin+((hue-240)*(diff/60))
       
                ELSEIF hue > 300        // Between mangenta and red
       
                        r = cmax
                        g = cmin
                        b = cmax-((hue-300)*(diff/60))
       
                ELSEIF hue = 0  // Pure Red
       
                        r = cmax
                        g = cmin
                        b = cmin
       
                ELSEIF hue = 60 // Pure Yellow
       
                        r = cmax
                        g = cmax
                        b = cmin
       
                ELSEIF hue = 120        // Pure Green
       
                        r = cmin
                        g = cmax
                        b = cmin
       
                ELSEIF hue = 180        // Pure Cyan
       
                        r = cmin
                        g = cmax
                        b = cmax
       
                ELSEIF hue = 240        // Pure Blue
       
                        r = cmin
                        g = cmin
                        b = cmax
       
                ELSEIF hue = 300        // Pure magnenta
       
                        r = cmax
                        g = cmin
                        b = cmax
       
                ENDIF
       
        ENDIF
       
       
        // Adjust saturation
       
        IF sat <> 100
       
                sat = 100-sat
       
                IF r <> cmax
       
                        tmin = r
                        tdiff = cmax - tmin
       
                        r = tmin + tdiff*(sat/100)
       
                ENDIF
       
                IF g <> cmax
       
                        tmin = g
                        tdiff = cmax - tmin
       
                        g = tmin + tdiff*(sat/100)
       
                ENDIF
       
                IF b <> cmax
       
                        tmin = b
                        tdiff = cmax - tmin
       
                        b = tmin + tdiff*(sat/100)
       
                ENDIF
       
        ENDIF
       
       
        // Adjust brightness
       
        IF bri <> 100
       
                IF bri = 0
       
                        r = 0
                        g = 0
                        b = 0
       
                ELSE
       
                        DEC r, (100-bri)*2.55
       
                        DEC g, (100-bri)*2.55
       
                        DEC b, (100-bri)*2.55
       
                ENDIF
       
        ENDIF
       
       
        IF r < 0 THEN r = 0
        IF r > 255 THEN r = 255
       
        IF g < 0 THEN g = 0
        IF g > 255 THEN g = 255
       
        IF b < 0 THEN b = 0
        IF b > 255 THEN b = 255
       
        RETURN BOR(RGB(r,g,b), ASL(a, 24))
       
ENDIF

ENDFUNCTION
« Last Edit: 2011-Jan-29 by Ragaril »

Offline Quentin

  • Prof. Inline
  • *****
  • Posts: 915
    • View Profile
Cool thing  :good:

I must admit, I've never used MEM2SPRITE until now. Is it fast enough to be used for ingame sprite manipulation? Or should it be used with care?

Offline Marmor

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 908
  • 96A285CC
    • View Profile
    • my youtube channel
its a nice one .  thx

Offline Wampus

  • Prof. Inline
  • *****
  • Posts: 1004
    • View Profile
Cool thing  :good:

I must admit, I've never used MEM2SPRITE until now. Is it fast enough to be used for ingame sprite manipulation? Or should it be used with care?

IMO not a good idea to use it on the fly each frame, i.e. in-game. Even if you increased speed by greatly reducing the number of colours you have to work with by keeping them in a pre-calculated list and skipped alpha channel with 0 value it would still be slow even on relatively fast PCs. Its quick, but not that quick.

The kind of practical thing you can do is re-colour a whole set of sprites on a sprite sheet for use later. In this way its not unlike loading a whole new set of sprites (and about as quick). When actually in use they'd work just the same as the original sprites. For example, in a real-time strategy game you could allow the user to make personalised colour changes to their standard set of units before a game starts. Same with a fighting game like the classic 2D versions of Street Fighter games. Another example would be a character creation screen for an RPG. You could allow the user to customise the colour scheme for their player characters in addition to all the other settings.

Offline r0ber7

  • Prof. Inline
  • *****
  • Posts: 526
    • View Profile
Hey Wampus,

Check this out. :)

<a href="http://www.youtube.com/watch?v=vtVNCJ4pTFw" target="_blank">http://www.youtube.com/watch?v=vtVNCJ4pTFw</a>

Muchas gracias!

Note: low framerate is due to Fraps, game runs smoothly. :P

Offline Wampus

  • Prof. Inline
  • *****
  • Posts: 1004
    • View Profile
Very nice :)

Incidentally, I'm going to use hue shifting amongst other things to make the appearance of wizards in my Chaos remake easy to change too.