Module: Tea::PrimitiveDrawing

Included in:
Bitmap, Screen
Defined in:
lib/tea/m_primitive_drawing.rb

Overview

The PrimitiveDrawing mixin enables primitive shapes to be drawn to classes with an internal SDL::Surface.

To use this mixin, include it and implement/alias a primitive_buffer method that gets the object’s SDL::Surface.

include 'PrimitiveDrawing'
def primitive_buffer
  @internal_sdl_buffer
end

Constant Summary collapse

BLEND_MIXER =

Mixer for alpha blend mix strategy.

lambda do |src_r, src_g, src_b, src_a, dest_r, dest_g, dest_b, dest_a, intensity|
  ai = src_a * intensity
  ratio = dest_a > 0 ? ai / dest_a.to_f : 1
  ratio = 1 if ratio > 1
  final_r = dest_r + (src_r - dest_r) * ratio
  final_g = dest_g + (src_g - dest_g) * ratio
  final_b = dest_b + (src_b - dest_b) * ratio
  final_a = (dest_a + ai < 255) ? (dest_a + ai) : 255
  [final_r, final_g, final_b, final_a]
end
REPLACE_MIXER =

Mixer for replace mix strategy.

lambda do |src_r, src_g, src_b, src_a, dest_r, dest_g, dest_b, dest_a, intensity|
  [src_r, src_g, src_b, src_a * intensity]
end

Instance Method Summary collapse

Instance Method Details

#circle(x, y, radius, color, options = nil) ⇒ Object

Draw a circle centred at (x, y) with the given radius and color (0xRRGGBBAA). Optional hash arguments:

:outline

If true, do not fill the circle, just draw an outline.

:antialias

If true, smooth the edges of the circle with antialiasing.

:mix

:blend averages the RGB parts of the circle and destination colours by the colour’s alpha (default). :replace writes over the RGBA parts of the circle’s destination pixels.

Raises Tea::Error if the radius is less than 0, or :mix is given an unrecognised symbol.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/tea/m_primitive_drawing.rb', line 149

def circle(x, y, radius, color, options=nil)
  if radius < 0
    raise Tea::Error, "can't draw circle with radius #{radius}", caller
  end

  if options == nil
    outline = false
    antialias = false
    mix = :blend
  else
    outline = options[:outline] || false
    antialias = options[:antialias] || false
    mix = options[:mix] || :blend

    unless [:blend, :replace].include?(mix)
      raise Tea::Error, "invalid mix option \"#{mix}\"", caller
    end
  end

  if primitive_buffer.class == SDL::Screen
    case mix
    when :blend
      r, g, b, a = primitive_hex_to_rgba(color)
      if !outline && antialias && a < 0xff
        # rubysdl can't draw filled antialiased alpha circles for some reason.
        # Big endian because the SGE-powered circle antialiasing apparently
        # doesn't like it any other way.
        ts = SDL::Surface.new(SDL::SWSURFACE, (radius + 1) * 2, (radius + 1) * 2, 32,
                              0xff000000,
                              0x00ff0000,
                              0x0000ff00,
                              0x000000ff)
        ts.draw_circle radius + 1, radius + 1, radius, ts.map_rgba(r, g, b, a), true, true
        SDL::Surface.blit ts, 0, 0, ts.w, ts.h, primitive_buffer, x - radius - 1, y - radius - 1
      else
        primitive_buffer.draw_circle x, y, radius, primitive_rgba_to_color(r, g, b, 255),
                                     !outline, antialias, (a == 255 ? nil : a)
      end
    when :replace
      primitive_buffer.draw_circle x, y, radius, primitive_color(color), !outline, antialias
    end
  else
    # SGE and alpha mixing don't... mix.  Gotta do it ourselves.
    mixer = (mix == :blend) ? BLEND_MIXER : REPLACE_MIXER
    r, g, b, a = primitive_hex_to_rgba(color)
    primitive_circle x, y, radius, !outline, antialias, r, g, b, a, mixer
  end
end

#clearObject

Clear the drawing buffer. This is the same as drawing a completely black rectangle over the whole buffer.



24
25
26
27
# File 'lib/tea/m_primitive_drawing.rb', line 24

def clear
  primitive_buffer.fill_rect 0, 0, primitive_buffer.w, primitive_buffer.h,
                             primitive_color(0x000000ff)
end

#line(x1, y1, x2, y2, color, options = nil) ⇒ Object

Draw a line from (x1, y1) to (x2, y2) with the given color (0xRRGGBBAA). Optional hash arguments:

:antialias

If true, smooth the line with antialiasing.

:mix

:blend averages the RGB parts of the line and destination colours by the line alpha (default). :replace writes over the RGBA parts of the line destination pixels.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/tea/m_primitive_drawing.rb', line 111

def line(x1, y1, x2, y2, color, options=nil)
  if options == nil
    antialias = false
    mix = :blend
  else
    antialias = options[:antialias] || false
    mix = options[:mix] || :blend

    unless [:blend, :replace].include?(mix)
      raise Tea::Error, "invalid mix option \"#{mix}\"", caller
    end
  end

  r, g, b, a = primitive_hex_to_rgba(color)
  if primitive_buffer.class == SDL::Screen
    primitive_buffer.draw_line x1, y1, x2, y2, primitive_rgba_to_color(r, g, b, (mix == :replace ? a : 255)), antialias, (mix == :blend ? a : nil)
  else
    if antialias
      primitive_aa_line x1, y1, x2, y2, r, g, b, a, (mix == :blend ? BLEND_MIXER : REPLACE_MIXER)
    else
      primitive_line x1, y1, x2, y2, r, g, b, a, (mix == :blend ? BLEND_MIXER : REPLACE_MIXER)
    end
  end
end

#point(x, y, color) ⇒ Object

Plot a point at (x, y) with the given color (0xRRGGBBAA) on the Bitmap.

Raises Tea::Error if (x, y) is outside of the drawing buffer.



32
33
34
35
36
37
38
39
# File 'lib/tea/m_primitive_drawing.rb', line 32

def point(x, y, color)
  w = primitive_buffer.w
  h = primitive_buffer.h
  if x < 0 || x > w || y < 0 || y > h
    raise Tea::Error, "can't plot point (#{x}, #{y}), not within #{w}x#{h}", caller
  end
  primitive_buffer[x, y] = primitive_color(color)
end

#rect(x, y, w, h, color, options = nil) ⇒ Object

Draw a rectangle of size w * h with the top-left corner at (x, y) with the given color (0xRRGGBBAA). Hash arguments that can be used:

:mix

:blend averages the RGB parts the rectangle and destination colours according to the colour’s alpha (default). :replace writes over the full RGBA parts of the rectangle area’s pixels.

Raises Tea::Error if w or h are less than 0, or if :mix is given an unrecognised symbol.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/tea/m_primitive_drawing.rb', line 68

def rect(x, y, w, h, color, options=nil)
  if w < 0 || h < 0 || (options && options[:mix] == :blend && (w < 1 || h < 1))
    raise Tea::Error, "can't draw rectangle of size #{w}x#{h}", caller
  end

  if options == nil || options[:mix] == nil
    mix = :blend
  else
    unless [:blend, :replace].include?(options[:mix])
      raise Tea::Error, "invalid mix option \"#{options[:mix]}\"", caller
    end
    mix = options[:mix]
  end

  r, g, b, a = primitive_hex_to_rgba(color)
  case mix
  when :blend
    if a == 0xff
      # Same as for mix == :replace
      primitive_buffer.fill_rect x, y, w, h, primitive_rgba_to_color(r, g, b, a)
    elsif primitive_buffer.class == SDL::Screen
      # SGE's broken alpha blending doesn't matter on the screen, so
      # optimise for it.  rubysdl's draw_rect is off-by-one for width and
      # height, so compensate for that.
      primitive_buffer.draw_rect x, y, w - 1, h - 1, primitive_rgba_to_color(r, g, b, 255), true, a
    else
      # CAUTION: This is _really_ slow, almost unusably so.  Perhaps I
      # should consider not making :blend the default mix mode.
      primitive_rect x, y, w, h, r, g, b, a, BLEND_MIXER
    end
  when :replace
    primitive_buffer.fill_rect x, y, w, h, primitive_rgba_to_color(r, g, b, a)
  end
end