Bitmap Addons
Version: 1.5
Author: modern algebra
Date: August 21, 2009
Version History
- <Version 1.5> 08.21.2009 - added extra options to greyscale, as well as allowing for a "default opacity" and "default greyscale"
- <Version 1.0> 02.19.2009 - Original Release
Planned Future Versions
- <Version 2.0> - polygon and pie features. So outline_polygon, outline_pie, fill_polygon, fill_pie, fill_area
Description
This is a Scripter's Tool, not a standalone script. If you are not a scripter, then you will only want this script if it is used by a script that you do want.
This script adds methods into Bitmap for drawing ellipses, as well as one for drawing a straight line.
Features
- New! Can greyscale a designated Rect object within a bitmap
- New! Can now set a default opacity (and greyscale boolean) for the methods blt and stretch_blt to use
- Can draw outline or fill an ellipse (or circle)
- Can draw rectangles with rounded edges
- Can draw diagonally oriented lines
ScreenshotsInstructions Please see header of the script for instructions
Script
#==============================================================================
# Bitmap Addons
# Version: 1.5
# Author: modern algebra (rmrk.net)
# Date: August 21, 2009
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Description:
# This is a simple scripter's tool with methods added into Bitmap for drawing
# ellipses, as well as one for drawing a straight line. The methods it adds
# are:
# outline_ellipse
# fill_ellipse
# fill_rounded_rect
# draw_line
# greyscale
#
# It also allows you to change opacity and greyscale of blt and stretch_blt
# externally with the public instance variables:
#
# bitmap.ma_default_opacity = [0-255]
# bitmap.ma_default_greyscale = false
#
# These codes allow you to be able to change the opacity or greyscale what a
# method like draw_character draws without overwriting it entirely. You want
# it to draw the character opaque or grey if the character is dead? All you
# need to do is change the default opacity or greyscale before running the
# method.
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Instructions:
# To use this, simply put it above main. For some of these, it is necessary
# to create an ellipse object and pass it as an argument. You can create an
# ellipse with the code:
#
# Ellipse.new (x, y, a, b)
# x : the top left x position
# y : the top left y position
# a : the width of oval from origin to the side
# b : the height of oval from origin. If nil, then a is radius of circle
#
# There are four new methods added to bitmap that can be used:
#
# outline_ellipse (ellipse[, colour, width, steps])
# ellipse : the ellipse object being drawn
# colour : the colour of the outline. Assumed font colour if not specified
# width : the thickness of the line. Assumed 1 if not specified
# steps : can set a number of steps for increased accuracy. If not
# specified, it will estimate the number of steps
#
# fill_ellipse (ellipse[, colour, steps])
# ellipse : the ellipse object being drawn
# colour : the colour of the ellipse. Assumed font colour if not specified
# steps : can set a number of steps for increased accuracy. If not
# specified, it will estimate the number of steps
#
# fill_rounded_rect (ellipse[, colour, w)
# ellipse : the ellipse object being drawn
# colour : the colour of the outline. Assumed font colour if not specified
# w : the number of pixels at which to round the edge.
#
# draw_line
# x0 : the initial x coordinate
# y0 : the initial y coordinate
# x1 : the end x coordinate
# y1 : the end y coordinate
# colour : the colour of the outline. Assumed font colour if not specified
#
# greyscale (rect)
# rect : the Rect object over which that part of the bitmap will be greyed.
#==============================================================================
# ** Ellipse
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Stores an ellipse object.
#==============================================================================
class Ellipse < Rect
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Public Instance Variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
attr_reader :h # The x position of the origin
attr_reader :k # The y position of the origin
alias a width
alias b height
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Object Initialization
# x : the top left x position
# y : the top left y position
# a : the width of oval from origin to the side
# b : the height of oval from origin. If nil, then a is radius of circle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def initialize (x, y, a, b = nil)
b = a if b.nil?
super (x, y, a, b)
@h = x + a
@k = y + b
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Within?
# x : the x coordinate being tested
# y : the y coordinate being tested
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def within? (x, y)
x_square = ((x - @h)*(x - @h)).to_f / (a*a)
y_square = ((y - @k)*(y - @k)).to_f / (b*b)
# If "radius" <= 1, then it must be within the ellipse
return (x_square + y_square) <= 1
end
end
#==============================================================================
# ** Bitmap
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Summary of Changes:
# attr_accessor - ma_default_opacity, ma_default_greyscale
# aliased methods - blt; stretch_blt
# new methods - fill_ellipse; outline_ellipse; fill_rounded_rect; draw_line
#==============================================================================
class Bitmap
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Public Instance Variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
attr_accessor :ma_default_opacity
attr_accessor :ma_default_greyscale
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Object Initialization
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias mdnabr_dfltopac_init_bmpadon_2lh3 initialize unless $@
def initialize (*args)
# Initialize new public instance variable
@ma_default_opacity = 255
@ma_default_greyscale = false
# Run Original Method
mdnabr_dfltopac_init_bmpadon_2lh3 (*args)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * BLT
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias mnalgba_bitmapaddons_opacdflt_blt_2hb5 blt unless $@
def blt (x, y, src_bmp, src_rect, opacity = @ma_default_opacity, grey = @ma_default_greyscale, *args)
if grey
src_bmp = src_bmp.dup
src_bmp.greyscale (src_rect)
end
# Run Original Method
mnalgba_bitmapaddons_opacdflt_blt_2hb5 (x, y, src_bmp, src_rect, opacity, *args)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Stretch BLT
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias modrnalgbra_strtchblt_bmpdons_opc_0lk2 stretch_blt unless $@
def stretch_blt (dest_rect, src_bmp, src_rect, opacity = @ma_default_opacity, grey = @ma_default_greyscale, *args)
if grey
src_bmp = src_bmp.dup
src_bmp.greyscale (src_rect)
end
# Run Original Method
modrnalgbra_strtchblt_bmpdons_opc_0lk2 (dest_rect, src_bmp, src_rect, opacity, *args)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Outline Ellipse
# ellipse : the ellipse being drawn
# width : the width of the bar
# colour : the colour of the outline
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def outline_ellipse (ellipse, colour = font.color, width = 1, steps = 0)
# For neatness, define local variables a and b to the ellipse variables
a, b = ellipse.a, ellipse.b
# Use Ramanujan's approximation of the Circumference of an ellipse
steps = Math::PI*(3*(a + b) - Math.sqrt((3*a + b)*(a + 3*b))) if steps == 0
radian_modifier = (2*Math::PI) / steps
for i in 0...steps
t = (radian_modifier*i) % (2*Math::PI)
# Expressed parametrically:
# x = h + acos(t), y = k + bsin(t) : where t ranges from 0 to 2pi
x = (ellipse.h + (a*Math.cos(t)))
y = (ellipse.k + (b*Math.sin(t)))
set_pixel (x, y, colour)
end
# Thicken the line
if width > 1
ellipse = Ellipse.new (ellipse.x + 1, ellipse.y + 1, ellipse.a - 1, ellipse.b - 1)
outline_ellipse (ellipse, colour, width - 1, steps)
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Fill Ellipse
# ellipse : the ellipse being drawn
# colour : the colour of the outline
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def fill_ellipse (ellipse, colour = font.color, steps = 0)
# For neatness, define local variables a and b to the ellipse variables
a, b = ellipse.a, ellipse.b
# Use Ramanujan's approximation of the Circumference of an ellipse
steps = Math::PI*(3*(a + b) - Math.sqrt((3*a + b)*(a + 3*b))) if steps == 0
radian_modifier = (2*Math::PI) / steps
for i in 0...(steps / 2)
t = (radian_modifier*i)
# Expressed parametrically:
# x = h + acos(t), y = k + bsin(t) : where t ranges from 0 to 2pi
x = ellipse.h + (a*Math.cos(t))
y = ellipse.k - (b*Math.sin(t))
fill_rect (x, y, 1, 2*(ellipse.k - y), colour)
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Fill Rounded Rectangle
# rect : the rectangle being drawn
# colour : the colour of the outline
# w : the number of pixels to cover by rounding
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Used to fill a rectangle with rounded corners
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def fill_rounded_rect (rect, colour = font.color, w = 8)
# Draw Body of the rectangle
fill_rect (rect.x + w, rect.y, rect.width - 2*w, rect.height, colour)
# Draw Left Vertical Rect
fill_rect (rect.x, rect.y + w, w, rect.height - 2*w, colour)
# Draw Right Vertical Rect
x = rect.x + rect.width - w
fill_rect (x, rect.y + w, w, rect.height - 2*w, colour)
# Make a circle
circle = Ellipse.new (0, 0, w)
for i in 0...w
for j in 0...w
# Upper Left Corner
set_pixel (rect.x + i, rect.y + j, colour) if circle.within? (i, j)
# Upper Right Corner
set_pixel (rect.x + rect.width - w + i, rect.y + j, colour) if circle.within? (i + w, j)
# Bottom Left Corner
set_pixel (rect.x + i, rect.y + rect.height - w + j, colour) if circle.within? (i, j + w)
# Bottom Right Corner
set_pixel (rect.x + rect.width - w + i, rect.y + rect.height - w + j, colour) if circle.within? (i + w, j + w)
end
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Draw Line
# x0, y0 : the coordinates of the start of the line.
# x1, y1 : the coordinates of the end of the line.
#``````````````````````````````````````````````````````````````````````````
# This uses Bresenham's algorithm to draw a line
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def draw_line (x0, y0, x1, y1, colour = font.color)
# Set boolean for steep lines
steep = (y1 - y0).abs > (x1 - x0).abs
if steep
# Reflect across y=x
tmp = x0
x0, y0 = y0, tmp
tmp = x1
x1, y1 = y1, tmp
end
# If in negative direction
if x0 > x1
# Swap initial points
tmp = x0
x0, x1 = x1, tmp
tmp = y0
y0, y1 = y1, tmp
end
ystep = y0 < y1 ? 1 : -1
deltax = x1 - x0
deltay = (y1 - y0).abs
error = deltax / 2
y = y0
# Advance by Rows
for x in x0.to_i...x1.to_i
steep ? set_pixel (y, x, colour) : set_pixel (x, y, colour)
error -= deltay
if error < 0
y += ystep
error += deltax
end
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * GreyScale
# rect : the rect to greyscale
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def greyscale (rect = Rect.new (0, 0, self.width, self.height))
# For each pixel in chosen rect of the bitmap
for i in rect.x...rect.x + rect.width
for j in rect.y...rect.y + rect.height
colour = self.get_pixel (i,j)
# Get the weighted average of the pixels for a proper shade of grey
grey_pixel = (colour.red*0.3 + colour.green*0.59 + colour.blue*0.11)
colour.red = colour.green = colour.blue = grey_pixel
# Set the pixel to the new shade of grey
self.set_pixel (i,j,colour)
end
end
end
end
Credit
Thanks
This script uses:
- Ramanujan's approximation of the Circumference of an ellipse
- Bresenham's algorithm to draw a line
Support
Please post here at rmrk.net for swiftest support.
Known Compatibility Issues
No known compatibility issues
Author's Notes
I wrote this a while ago for my Quest Journal, but since I intend to use it for other scripts, I thought it would be useful if it were released as standalone. I realize that these are probably not the most efficient ways to draw these basic geometric shapes, and I welcome any suggestions that would help me improve the algorithms used.
This script by
modern algebra is licensed under a
Creative Commons Attribution-Non-Commercial-Share Alike 2.5 Canada License.