Module: Tea::Primitive

Included in:
Bitmap, Screen
Defined in:
lib/tea/mix_primitive.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 'Primitive'
def primitive_buffer
  @internal_sdl_buffer
end

Constant Summary collapse

BLEND_MIXER =
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 =
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

#[](x, y) ⇒ Object

Get the color (0xRRGGBBAA) at the point (x, y) on the Bitmap.

Raises Tea::Error if (x, y) is outside of the Bitmap’s area.



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

def [](x, y)
  buffer = primitive_buffer
  w = buffer.w
  h = buffer.h
  if x < 0 || x >= w || y < 0 || y >= h
    raise Tea::Error, "can't get color at (#{x}, #{y}), not within #{w}x#{h}", caller
  end
  Color.mix(*buffer.get_rgba(buffer[x, y]))
end

#[]=(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 Bitmap’s area.



45
46
47
48
49
50
51
52
53
# File 'lib/tea/mix_primitive.rb', line 45

def []=(x, y, color)
  buffer = primitive_buffer
  w = buffer.w
  h = 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
  buffer[x, y] = primitive_color(color)
end

#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.



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
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/tea/mix_primitive.rb', line 161

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

  r, g, b, a = Color.split(color)

  if options == nil
    outline = false
    antialias = false
    mix = (a < 0xff) ? :blend : :replace
  else
    if options[:mix] && [:blend, :replace].include?(options[:mix]) == false
      raise Tea::Error, "invalid mix option \"#{mix}\"", caller
    end

    outline = options[:outline] || false
    antialias = options[:antialias] || false
    mix = (a < 0xff) ? (options[:mix] ? options[:mix] : :blend) : :replace
  end

  if primitive_buffer.class == SDL::Screen
    case mix
    when :blend
      if !outline && antialias
        # 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
    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/mix_primitive.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.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/tea/mix_primitive.rb', line 121

def line(x1, y1, x2, y2, color, options=nil)
  r, g, b, a = Color.split(color)

  if options == nil
    antialias = false
    mix = (a < 0xff) ? :blend : :replace
  else
    if options[:mix] && [:blend, :replace].include?(options[:mix]) == false
      raise Tea::Error, "invalid mix option \"#{mix}\"", caller
    end

    antialias = options[:antialias] || false
    mix = (options[:mix] && a < 0xff) ? options[:mix] : :replace
    mix = (a < 0xff) ? (options[:mix] ? options[:mix] : :blend) : :replace
  end

  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

#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.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/tea/mix_primitive.rb', line 80

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

  r, g, b, a = Color.split(color)

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

  case mix
  when :blend
    if 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