Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / Color Module
- - By PhusionDev Date 2012-03-06 14:16
I'm not sure if this is the most efficient way to handle it, but I needed a way to dynamically modify my gosu colors and this is the module I came up with. It works in its current state so feel free to use it if you find it useful :)
If you: Include Colored into your ruby objects, you can then initiate a gosu color to them, and then modify it using these module methods.
I mainly needed it for dynamic alpha, but it wasn't any different to make it work for the color channels either.

Examples:
Object.alpha += 1 # Increases the alpha by 1
Object.alpha = "ff" # Set alpha channel to ff, 255 (Opaque)

Let me know if you find it useful, if there are ways to optimize, if there are any bugs, etc etc. :)

module Colored
  attr_accessor :color
 
  class Position
    Alpha = 0
    Red = 1
    Green = 2
    Blue = 3
  end
 
  def alpha
    return unpack_color[Position::Alpha].to_i(16)
  end
 
  def red
    return unpack_color[Position::Red].to_i(16)
  end
 
  def green
    return unpack_color[Position::Green].to_i(16)
  end
 
  def blue
    return unpack_color[Position::Blue].to_i(16)
  end
 
  def alpha=(value)
    change_value(Position::Alpha, value)
  end
 
  def red=(value)
    change_value(Position::Red, value)
  end
 
  def green=(value)
    change_value(Position::Green, value)
  end
 
  def blue=(value)
    change_value(Position::Blue, value)
  end
 
  private
 
  def change_value(position, value)
    if value.is_a?(String)
      value = value.to_i(16)
    end
    if value.is_a?(Integer)
      if (0..255).include? value
        split_color = unpack_color
        split_color[position] = value.to_s(16)
        update_color(repack_color(split_color))
      end
    end
  end
 
  def unpack_color
    return ("%08x" % @color).scan(/[0-9a-fA-F][0-9a-fA-F]/)
  end
 
  def repack_color(color)
    return "0x" << color[0] << color[1] << color[2] << color[3]
  end
 
  def update_color(color)
    @color = color.to_i(16)
  end
end
Parent - - By RavensKrag Date 2012-03-06 15:45
The first thing I see is that you are passing ff as a string to Gosu::Color#alpha.  Why not just do
Object.alpha = 0xff
instead? Then there's no need to convert the string to an integer on input.  Also, 0xff == 255, so that should simplify things.

Also, why does #to_i take a parameter? At the very least, the parameter should default to 16.  I would assume that most of the time someone would want to call #to_i they would be using #to_i(16).  Just look at how many times you did so yourself.

That's my criticism for the time being.
Parent - By PhusionDev Date 2012-03-06 16:00
I'm not passing "ff" myself, but it's there as a fallback to process a hex string. Personally I pass integers between 0-255 but other people may want to pass hex strings? If you pass 0xff, ruby will automatically turn it into 255 which is an Integer, and gets processed as such. The reason I have this is for versatility depending on how people want to use it in their own application. Ruby's integers default to base 10, hence the to_i(16). It's taking a string of hex code "ff" and it needs to convert it from base 16 to base 10.

irb(main):067:0> "ff".to_i
=> 0

irb(main):070:0> "ff".to_i(16)
=> 255
Parent - By PhusionDev Date 2012-03-06 16:16
The problem I was having initially is that Gosu::Color has a color value of 0xAARRGGBB opposed to individual values for Alpha, Red, Green and Blue. 0xAA, 0xRR, 0xGG, 0xBB. So using what was available to me I process this compounded value as a string, split it, modify it, repack it, and then change the original value.
Parent - By PhusionDev Date 2012-03-06 19:40
I'm in the process of revising this completely without any string manipulation, and will post when done :)
Parent - - By Spooner Date 2012-03-06 21:15
I would just keep it simple using a Gosu::Color object and delegation:

require 'forwardable'
module Colored
  extend Forwardable

  attr_accessor :color

  def_delegators :@color, :red, :blue, :green, :alpha, :red=, :blue=, :green=, :alpha=
end


Job done!
Parent - - By PhusionDev Date 2012-03-07 00:35
I'm not really sure how to implement what you posted -- after looking into forwardable I wasn't sure how to make it meet my needs. I'm trying to keep an objects color value separate from a gosu object -- maybe this isn't the correct approach but my UI_Elements don't know anything about gosu, and I was trying to keep it as abstract as possible. For this reason each UI_Element has a @color ranging from 0x0 to 0xffffffff

After much mucking around today, hairs lost and hours of frustration later -- this is my new version of the module without string manipulation:

module Colored
  class Position
    Alpha = 0
    Red = 1
    Green = 2
    Blue = 3
  end
 
  def color=(value)
    if (0x0..0xffffffff).include? value
      breakdown_color(value)
    end
  end
 
  def alpha=(value)
    change_value(Position::Alpha, value)
  end
 
  def red=(value)
    change_value(Position::Red, value)
  end
 
  def green=(value)
    change_value(Position::Green, value)
  end
 
  def blue=(value)
    change_value(Position::Blue, value)
  end
 
  def color
    return @color
  end
 
  def alpha
    return @color_breakdown[Position::Alpha]
  end
 
  def red
    return @color_breakdown[Position::Red]
  end
 
  def green
    return @color_breakdown[Position::Green]
  end
 
  def blue
    return @color_breakdown[Position::Blue]
  end
 
  private
 
  def breakdown_color(value)
    @color = value
   
    remainder = value
    alpha = 0
    red = 0
    green = 0
    blue = 0
    divmod = Array.new(1)
   
    begin
      case remainder
        when (0x0..0xff)
          blue = remainder
          divmod[1] = 0
        when (0x100..0xffff)
          divmod = remainder.divmod(0x100)
          green = divmod[0]
        when (0x10000..0xffffff)
          divmod = remainder.divmod(0x10000)
          red = divmod[0]
        when (0x1000000..0xffffffff)
          divmod = remainder.divmod(0x1000000)
          alpha = divmod[0]
      end
      remainder = divmod[1]
    end until remainder == 0
    @color_breakdown = Array[alpha, red, green, blue]
  end
 
  def change_value(position, value)
    if (0x0..0xff).include? value
      @color_breakdown[position] = value
      update_color
    end
  end
 
  def update_color
    @color = (self.alpha * 0x1000000) +
             (self.red * 0x10000) +
             (self.green * 0x100) +
             (self.blue)
  end
end
Parent - - By Spooner Date 2012-03-07 00:45
What I gave was literally all you needed to do to have equivalent functionality to what you had written. You'd just need to ensure you did self.color = Color.rgba(123, 123, 13, 0) or something in the constructor and you'd be able to access #color and directly affect #alpha, etc.

That said, I've never seen a reason to use the integer color in any of my games or libraries, since Gosu::Color is just so much more versatile and Rubified than a C-friendly integer (really, most of what Colored does is mimic the already existing Color class).

As an aside, since #divmod is just a way to get #div and #modulo at once, you can use:
red = remainder.div(0x10000)
instead of:
divmod = remainder.divmod(0x10000)
red = divmod[0]
Parent - - By PhusionDev Date 2012-03-07 00:49
I appreciate your replies and will continue to try and understand your point of view on this. Thanks :)
Parent - - By PhusionDev Date 2012-03-07 00:50
I didn't think Gosu::Color had a way to set values on ARGB, rather just read from them so I began working on this. But had I understood what you meant I probably could have saved a lot of trouble!
Parent - - By Spooner Date 2012-03-07 00:56
That would make sense, if it wasn't clear that Color had setters.

I appreciate it is hard to drop 50 lines of code you've worked hard on to use 5 lines of code that do the same thing. I maybe should properly explained how they worked rather than just plonking them down. I still find it hard to let go of code I've written and even after years of working in Ruby I learn something new every week that makes some part of my own work redundant!
Parent - By PhusionDev Date 2012-03-07 02:13
I got it working with the code supplied, although now it's time for me to break everything down and learn its usefulness =) The one thing I was hoping to avoid was making a reference to Gosu from within my child classes, but that might not be possible/feasible. Thanks for your help!
Parent - - By PhusionDev Date 2012-03-07 02:41
with a simple addition to the code you supplied, I can now keep Gosu::Color out of my UI_Image
As before, UI_Image takes a 0xaarrbbgg integer on initialization, sets @color to it, but now also makes a call to init_color from the Colored module. Kind of messy? But it works =x

require 'forwardable'
module Colored
  extend Forwardable

  attr_accessor :color

  def_delegators :@color, :red, :blue, :green, :alpha, :red=, :blue=, :green=, :alpha=
 
  def init_color
    self.color = Gosu::Color.argb(@color)
  end
end
Parent - - By Spooner Date 2012-03-07 02:45
Ah, I thought you meant you wanted to keep Color out of your module, rather than to keep it hidden from the end-user.

Might I suggest:

  attr_reader :color # Getter
 
  def color=(color) # Setter
    @color = color.is_a?(Gosu::Color) ? color.dup : Gosu::Color.argb(color)
  end
Parent - - By PhusionDev Date 2012-03-07 13:54 Edited 2012-03-07 13:59
ah yes just looking at this.. it looks perfect for what I want :) many thanks! I think initially I was trying this but @color = value in my UI_Image wasn't calling Colored#color=(value) but I think it was a matter of scope at the time.

and I just found out why it didn't initially work when I tried it yesterday was because I was using @color = color in my UI_Image but I needed to use self.color = color to trigger off Colored#color=(value)

Well this was a great learning experience either way I look at it :D
Parent - By Spooner Date 2012-03-07 14:14
Yes, needing to use self.value= is a bit annoying, and often trips me up. Glad you learned something new :)
Up Topic Gosu / Gosu Exchange / Color Module

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill