Copern

Copern's Bitmap Color Functions

Aug 13th, 2012
530
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 14.51 KB | None | 0 0
  1. #==============================================================================
  2. # ** Copern - Bitmap Color Functions
  3. #------------------------------------------------------------------------------
  4. # Version History:
  5. #  1.0 (09/13/2012) - First release
  6. #==============================================================================
  7. # Introduction
  8. #------------------------------------------------------------------------------
  9. # This script offers a few methods for recoloring a bitmap, in whole or using
  10. # a mask, or for modifying the color levels of a bitmap.
  11. #
  12. #==============================================================================
  13. # Requirements
  14. #------------------------------------------------------------------------------
  15. # Requires the copernbmcolors.dll in your project folder.
  16. #
  17. #==============================================================================
  18. # Instructions
  19. #------------------------------------------------------------------------------
  20. #
  21. # * Recoloring
  22. # ------------
  23. # If you wish to recolor a bitmap to a specific color while maintaining the
  24. # brightness of the bitmap you may simply use colorify(color) or
  25. # colorify!(color)
  26. # For example to give a bitmap an orangish color
  27. # bitmap.colorify!(Color.new(200, 100, 0))
  28. #
  29. # * Recoloring From a Mask
  30. # ------------------------
  31. # If you wish to recolor a bitmap to a specific color based on a given mask, use
  32. # colorify_masked(color, mask_bitmap[, repeat]) or
  33. # colorify_masked!(color, mask_bitmap[, repeat])
  34. # The optional boolean parameter, repeat, will repeat the pattern of the mask if
  35. # it is smaller than the target bitmap.
  36. #
  37. # This method takes into account the value and alpha of each pixel on the mask.
  38. # A value of 128 on the mask will maintain the brightness of the original pixel.
  39. # A higher value will make the pixels brighter than the original towards max
  40. # brightness. A lower value will make the pixels darker than the original
  41. # towards minimum brightness (black in this case). A value of 0 will skip
  42. # processing of that pixel. The alpha layer of the mask will do as one expects
  43. # by alpha blending with the original color.
  44. #
  45. # * Setting Color Levels
  46. # ----------------------
  47. # In order to set the color levels of a bitmap, you will need to create an
  48. # instance of Color_Levels and define points for the channels you wish to
  49. # modify. Channels include red, green, blue, alpha, and value. The value channel
  50. # corresponds to brightness and is applied after the RGB channel.
  51. # The X in a point is the color value you wish to change ranging from 0 - 255.
  52. # The Y in a point is the value you want X to become (0 - 255).
  53. #
  54. # Lines are created between each point defined in the channel so you don't
  55. # have to set every point individually. If you want to change only a single
  56. # value remember that by default, x equals y. You could, for example, set a
  57. # point at (124, 124), then (125, 200), and finally (126, 126). This way only
  58. # values of 125 are changed to 200, everything else remains the same.
  59. #
  60. # Usable methods in Color Levels are:
  61. #   set_point(channel, x, y)
  62. #
  63. #   set_points(channel, points)
  64. #     Takes a hash where the keys correlate to X and the values correlate to Y.
  65. #
  66. #   remove_at(channel, x)
  67. #
  68. #   remove_range(channel, x_ary)
  69. #     Takes an array of X values.
  70. #
  71. # Example using all methods:
  72. # levels = Color_Levels.new
  73. # levels.set_point(:red, 60, 150).set_point(:blue, 200, 50)
  74. # greens = {80 => 45, 100 => 50, 120 => 40}
  75. # values = {0 => 100, 50 => 100, 110 => 180, 180 => 230}
  76. # levels.set_points(:green, greens).set_points(:value, values)
  77. # levels.remove_at(:value, 180).remove_range(:green, [80, 120])
  78. #
  79. # After setting the color levels, you can use either bitmap.set_levels(levels)
  80. # or bitmap.set_levels!(levels).
  81. #
  82. #==============================================================================
  83. # Compatibility
  84. #------------------------------------------------------------------------------
  85. #
  86. # * New variables
  87. #   class Bitmap
  88. #     * Class variables
  89. #       @@colorify
  90. #       @@colorify_masked
  91. #       @@set_levels
  92. #
  93. # * New methods
  94. #   class Bitmap
  95. #     def colorify(color)
  96. #     def colorify!(color)
  97. #     def colorify_masked(color, mask, repeat = false)
  98. #     def colorify_masked!(color, mask, repeat = false)
  99. #     def set_levels(levels)
  100. #     def set_levels!(levels)
  101. #     def self.valid_bitmap?(bitmap)
  102. #    
  103. #==============================================================================
  104. # Terms of Use
  105. #------------------------------------------------------------------------------
  106. #
  107. # You must give credit to Copern.
  108. # You may use this script free of charge in FREE and COMMERCIAL games.
  109. # You may NOT redistribute this script. Scripts that require this must link to
  110. # the appropriate forum post(s) created by Copern.
  111. # You may NOT distribute a modified version of this script.
  112. #
  113. #==============================================================================
  114.  
  115. $imported ||= {}
  116. $imported[:copern_color_modifications] = true
  117.  
  118. module Copern
  119.   module Color_Functs
  120.     VERSION = 1.0
  121.     DLL_NAME =
  122.     #==========================================================================
  123.     # Modify this if you want to change the location of the DLL.
  124.     # Ex 'system\copernbmcolors'
  125.     #--------------------------------------------------------------------------
  126.     'copernbmcolors'
  127.     #==========================================================================
  128.   end
  129.  
  130.   module Error
  131.     def self.raise_error(err, obj, msg)
  132.       klass = obj.class.to_s
  133.       stack = caller.slice(1..15)
  134.       stack.each do |i|
  135.         script = i.scan(/\d+/)[0]
  136.         i.sub!(/{\d+}/, $RGSS_SCRIPTS.at(script.to_i)[1]) if script
  137.       end
  138.       raise err, msg % klass << "\n\n#{stack.join("\n")}"
  139.     end
  140.  
  141.     def self.raise_bitmap(bitmap)
  142.       raise_error(RGSSError, bitmap, "%s is a disposed or invalid bitmap!")
  143.     end
  144.    
  145.     def self.raise_color(color)
  146.       raise_error(ArgumentError, color, "%s is not a valid color!")
  147.     end
  148.    
  149.     def self.raise_levels(levels)
  150.       raise_error(ArgumentError, levels, "%s is not a valid color levels instance!")
  151.     end
  152.   end
  153. end
  154.  
  155. #==============================================================================
  156. # ** Color_Levels
  157. #------------------------------------------------------------------------------
  158. #  This class ensures proper formatting of points in a color levels graph. The
  159. # contents are sorted and flattened before being passed to the DLL.
  160. #==============================================================================
  161.  
  162. class Color_Levels
  163.   #--------------------------------------------------------------------------
  164.   # * Class variables
  165.   #--------------------------------------------------------------------------
  166.   @@default = {0 => 0, 255 => 255}.freeze
  167.  
  168.   #--------------------------------------------------------------------------
  169.   # * initialize
  170.   #--------------------------------------------------------------------------  
  171.   def initialize
  172.     #Blue, Green, Red, Alpha, Value
  173.     @channels = Array.new(5) {@@default.dup}
  174.   end
  175.  
  176.   #--------------------------------------------------------------------------
  177.   # * set_point
  178.   #--------------------------------------------------------------------------
  179.   #  Adds or modifies a point in the specified channel.
  180.   #--------------------------------------------------------------------------  
  181.   def set_point(channel, x, y)
  182.     return self unless valid_point?(x, y)
  183.     index = get_channel(channel)
  184.     @channels[index][x] = y;
  185.     self
  186.   end
  187.  
  188.   #--------------------------------------------------------------------------
  189.   # * set_points
  190.   #--------------------------------------------------------------------------
  191.   #  Adds or modifies multiple points in the specified channel. Invalid entries
  192.   # are removed prior to merger.
  193.   #--------------------------------------------------------------------------  
  194.   def set_points(channel, points)
  195.     return self unless points && points.is_a?(Hash)
  196.     index = get_channel(channel)
  197.     new_points = points.reject {|k, v| !valid_point?(k, v)}
  198.     @channels[index].merge!(new_points)
  199.     self
  200.   end
  201.    
  202.   #--------------------------------------------------------------------------
  203.   # * remove_at
  204.   #--------------------------------------------------------------------------
  205.   #  Removes a point in the specified channel.
  206.   #--------------------------------------------------------------------------
  207.   def remove_at(channel, x)
  208.     return self unless x
  209.     index = get_channel(channel)
  210.     if x == 0 || x == 255
  211.       @channels[index][x] = x
  212.     else
  213.       @channels[index].delete(x)
  214.     end
  215.     self
  216.   end
  217.  
  218.   #--------------------------------------------------------------------------
  219.   # * remove_range
  220.   #--------------------------------------------------------------------------
  221.   #  Removes a range of points in the specified channel.
  222.   #--------------------------------------------------------------------------
  223.   def remove_range(channel, x_range)
  224.     return self unless x_range && x_range.respond_to?(:each)
  225.     x_range.each {|i| remove_at(channel, i)}
  226.     self
  227.   end
  228.  
  229.   #--------------------------------------------------------------------------
  230.   # * get_sorted_channels
  231.   #--------------------------------------------------------------------------
  232.   #  Returns a sorted array for use in the DLL.
  233.   #--------------------------------------------------------------------------
  234.   def sorted_channels
  235.     Array.new(5) {|i| @channels[i].sort.flatten}
  236.   end
  237.  
  238.   #--------------------------------------------------------------------------
  239.   # * valid_point?
  240.   #--------------------------------------------------------------------------
  241.   #  Checks if the given point contains valid color values.
  242.   #--------------------------------------------------------------------------
  243.   def valid_point?(x, y)
  244.     return false unless x && y && x.is_a?(Integer) && y.is_a?(Integer)
  245.     return false if x < 0 || x > 255 || y < 0 || y > 255
  246.     return true
  247.   end
  248.  
  249.   #--------------------------------------------------------------------------
  250.   # * get_channel
  251.   #--------------------------------------------------------------------------
  252.   #  Returns a channel index based on the given channel symbol. Defaults to the
  253.   # red channel if the symbol is not found.
  254.   #--------------------------------------------------------------------------  
  255.   def get_channel(channel)
  256.     case channel
  257.       when :blue
  258.         0
  259.       when :green
  260.         1
  261.       when :red
  262.         2
  263.       when :alpha
  264.         3
  265.       when :value
  266.         4
  267.       else
  268.         2
  269.     end
  270.   end
  271. end
  272.  
  273. #==============================================================================
  274. # ** Bitmap
  275. #==============================================================================
  276.  
  277. class Bitmap
  278.   #--------------------------------------------------------------------------
  279.   # * Class variables
  280.   #--------------------------------------------------------------------------
  281.   @@colorify = Win32API.new(Copern::Color_Functs::DLL_NAME,
  282.   'colorify', ['l', 'l'], 'l').freeze
  283.   @@colorify_masked = Win32API.new(Copern::Color_Functs::DLL_NAME,
  284.   'colorifymasked', ['l', 'l', 'l', 'l'], 'l').freeze
  285.   @@set_levels = Win32API.new(Copern::Color_Functs::DLL_NAME,
  286.   'setlevels', ['l', 'l'], 'l').freeze
  287.  
  288.   #--------------------------------------------------------------------------
  289.   # * New method: colorify
  290.   #--------------------------------------------------------------------------
  291.   def colorify(color)
  292.     Copern::Error::raise_bitmap(self) if self.disposed?
  293.     Copern::Error::raise_color(color) unless color && color.instance_of?(Color)
  294.     other = self.dup
  295.     @@colorify.call(other.object_id, color.object_id)
  296.     other
  297.   end
  298.  
  299.   #--------------------------------------------------------------------------
  300.   # * New method: colorify!
  301.   #--------------------------------------------------------------------------
  302.   def colorify!(color)
  303.     Copern::Error::raise_bitmap(self) if self.disposed?
  304.     Copern::Error::raise_color(color) unless color && color.instance_of?(Color)
  305.     @@colorify.call(object_id, color.object_id)
  306.     self
  307.   end
  308.  
  309.   #--------------------------------------------------------------------------
  310.   # * New method: colorify_masked
  311.   #--------------------------------------------------------------------------
  312.   def colorify_masked(color, mask, repeat = false)
  313.     Copern::Error::raise_bitmap(mask) if self.disposed? || !Bitmap::valid_bitmap?(mask)
  314.     Copern::Error::raise_color(color) unless color && color.instance_of?(Color)
  315.     other = self.dup
  316.     @@colorify_masked.call(other.object_id, mask.object_id, color.object_id, repeat ? 1 : 0)
  317.     other
  318.   end
  319.  
  320.   #--------------------------------------------------------------------------
  321.   # * New method: colorify_masked!
  322.   #--------------------------------------------------------------------------
  323.   def colorify_masked!(color, mask, repeat = false)
  324.     Copern::Error::raise_bitmap(mask) if self.disposed? || !Bitmap::valid_bitmap?(mask)
  325.     Copern::Error::raise_color(color) unless color && color.instance_of?(Color)
  326.     @@colorify_masked.call(object_id, mask.object_id, color.object_id, repeat ? 1 : 0)
  327.     self
  328.   end
  329.  
  330.   #--------------------------------------------------------------------------
  331.   # * New method: set_levels
  332.   #--------------------------------------------------------------------------
  333.   def set_levels(levels)
  334.     Copern::Error::raise_bitmap(self) if self.disposed?
  335.     unless levels && levels.instance_of?(Color_Levels)
  336.       Copern::Error::raise_levels(levels)
  337.     end
  338.     channels = levels.sorted_channels
  339.     other = self.dup
  340.     @@set_levels.call(other.object_id, channels.object_id)
  341.     other
  342.   end
  343.  
  344.   #--------------------------------------------------------------------------
  345.   # * New method: set_levels!
  346.   #--------------------------------------------------------------------------
  347.   def set_levels!(levels)
  348.     Copern::Error::raise_bitmap(self) if self.disposed?
  349.     unless levels && levels.instance_of?(Color_Levels)
  350.       Copern::Error::raise_levels(levels)
  351.     end
  352.     channels = levels.sorted_channels
  353.     @@set_levels.call(object_id, channels.object_id)
  354.     self
  355.   end
  356.  
  357.   #--------------------------------------------------------------------------
  358.   # * New method: valid_bitmap?
  359.   #--------------------------------------------------------------------------
  360.   def self.valid_bitmap?(bitmap)
  361.     return false unless bitmap && bitmap.instance_of?(Bitmap)
  362.     return false if bitmap.disposed?
  363.     return true
  364.   end
  365. end
Advertisement
Add Comment
Please, Sign In to add comment