diff --git a/examples/affine.cr b/examples/affine.cr index ddf3430..c2fe6b5 100644 --- a/examples/affine.cr +++ b/examples/affine.cr @@ -7,6 +7,7 @@ module PF @bricks : Sprite @transform : Transform2d = Transform2d.new @angle = 0.0 + @size = 1.0 def initialize(*args, **kwargs) super @@ -25,6 +26,7 @@ module PF .reset .translate(-(@bricks.size // 2)) .rotate(@angle) + .scale(@size) .translate(viewport // 2) b1, b2 = @transform.bounding_box(@bricks.size.x, @bricks.size.y).map(&.to_i) @@ -35,8 +37,7 @@ module PF b1.x.upto(b2.x) do |x| point = @transform.apply(x, y).to_i if point >= Vector[0, 0] && point < @bricks.size - color = @bricks.sample(point.x, point.y) - draw_point(x.to_i, y.to_i, color) + draw_point(x.to_i, y.to_i, @bricks.peak(point)) end end end diff --git a/examples/snow_scroll.cr b/examples/snow_scroll.cr index 6ae8d35..0d22ce6 100644 --- a/examples/snow_scroll.cr +++ b/examples/snow_scroll.cr @@ -8,7 +8,7 @@ class Snow < PF::Game super @pixels = @screen.pixels - clear(0, 0, 25) + clear(0, 0, 0x25) end def update(dt, event) @@ -23,10 +23,10 @@ class Snow < PF::Game 0.upto(@width - 1) do |x| if rand(0..250) == 0 - shade = rand(0..255) - @pixels[x] = LibSDL.map_rgba(@screen.format, shade, shade, shade, 255) + shade = rand(25_u8..255_u8) + @pixels[x] = PF::Pixel.new(shade, shade, shade).to_u32 else - @pixels[x] = LibSDL.map_rgba(@screen.format, 0, 0, 25, 255) + @pixels[x] = 0x000025FF end end end diff --git a/examples/sprite_example.cr b/examples/sprite_example.cr index a00dee4..e7ac5a3 100644 --- a/examples/sprite_example.cr +++ b/examples/sprite_example.cr @@ -3,11 +3,11 @@ require "../src/sprite" module PF class SpriteExample < Game - @bricks : Sprite + @sprite : Sprite def initialize(*args, **kwargs) super - @bricks = Sprite.new("./assets/pf-font.png") + @sprite = Sprite.new("./assets/pf-font.png") end def update(dt, event) @@ -15,7 +15,7 @@ module PF def draw clear(255, 255, 255) - @bricks.draw_to(@screen, width // 2 - @bricks.width // 2, height // 2 - @bricks.height // 2) + @sprite.draw_to(screen, (viewport // 2) - @sprite.size // 2) end end end diff --git a/spec/pixel_spec.cr b/spec/pixel_spec.cr new file mode 100644 index 0000000..03eb952 --- /dev/null +++ b/spec/pixel_spec.cr @@ -0,0 +1,23 @@ +require "./spec_helper" +require "../src/pixel" + +include PF + +describe Pixel do + describe "#initialize" do + it "breaks out a UInt32 into rgba components" do + p = Pixel.new(0x11223344) + p.r.should eq(0x11_u8) + p.g.should eq(0x22_u8) + p.b.should eq(0x33_u8) + p.a.should eq(0x44_u8) + end + end + + describe "#to_u32" do + it "combines components into a UInt32 value" do + p = Pixel.new(0x11_u8, 0x22_u8, 0x33_u8, 0x44_u8) + p.to_u32.should eq(0x11223344_u32) + end + end +end diff --git a/src/game.cr b/src/game.cr index 984a6a8..0f4d78d 100644 --- a/src/game.cr +++ b/src/game.cr @@ -7,14 +7,16 @@ module PF FPS_INTERVAL = 1.0 SHOW_FPS = true - getter width : Int32 - getter height : Int32 - @viewport : Vector(Int32, 2)? = nil property scale : Int32 property title : String property running = true property screen : Sprite + getter width : Int32 + getter height : Int32 + + @viewport : Vector(Int32, 2)? = nil + delegate :draw_point, :draw_line, :draw_circle, :draw_triangle, :draw_rect, :draw_shape, :fill_triangle, :fill_rect, :fill_shape, to: @screen @@ -30,13 +32,7 @@ module PF @window = SDL::Window.new(@title, @width * @scale, @height * @scale, flags: window_flags) @renderer = SDL::Renderer.new(@window, flags: flags) @renderer.scale = {@scale, @scale} - - surface = SDL::Surface.new(LibSDL.create_rgb_surface( - flags: 0, width: @width, height: @height, depth: 32, - r_mask: 0xFF000000, g_mask: 0x00FF0000, b_mask: 0x0000FF00, a_mask: 0x000000FF - )) - - @screen = Sprite.new(surface) + @screen = Sprite.new(@width, @height) end abstract def update(dt : Float64, event : SDL::Event) diff --git a/src/pixel.cr b/src/pixel.cr index cd2ecf9..8094448 100644 --- a/src/pixel.cr +++ b/src/pixel.cr @@ -39,10 +39,10 @@ module PF property r : UInt8, g : UInt8, b : UInt8, a : UInt8 def initialize(rgba : UInt32) - @r = ((rgba & 0xFF000000_u32) >> (8 * 3)).to_u8 - @g = ((rgba & 0x00FF0000_u32) >> (8 * 2)).to_u8 - @b = ((rgba & 0x0000FF00_u32) >> 8).to_u8 - @a = ((rgba & 0x000000FF_u32)).to_u8 + @r = ((rgba >> 24) & 0xFF).to_u8 + @g = ((rgba >> 16) & 0xFF).to_u8 + @b = ((rgba >> 8) & 0xFF).to_u8 + @a = (rgba & 0xFF).to_u8 end def initialize(@r : UInt8 = 255, @g : UInt8 = 255, @b : UInt8 = 255, @a : UInt8 = 255) @@ -67,5 +67,13 @@ module PF def -(n : Float64) PF::Pixel.new((@r - n).to_u8, (@g - n).to_u8, (@b - n).to_u8, @a) end + + def to_u32 + value = @r.to_u32 << 24 + value |= @g.to_u32 << 16 + value |= @b.to_u32 << 8 + value |= @a.to_u32 + value + end end end diff --git a/src/sprite.cr b/src/sprite.cr index 8d4a5fc..6f56fff 100644 --- a/src/sprite.cr +++ b/src/sprite.cr @@ -15,6 +15,13 @@ module PF @surface = SDL::IMG.load(path) end + def initialize(width : Int, height : Int) + @surface = SDL::Surface.new(LibSDL.create_rgb_surface( + flags: 0, width: width, height: height, depth: 32, + r_mask: 0xFF000000, g_mask: 0x00FF0000, b_mask: 0x0000FF00, a_mask: 0x000000FF + )) + end + def width @surface.width end @@ -27,30 +34,49 @@ module PF Vector[width, height] end + # Convert the color mode of this sprite to another for optimization def convert(other : SDL::Surface) @surface = @surface.convert(other) end + # ditto def convert(other : Sprite) @surface = @surface.convert(other.surface) end - def draw_to(surface : SDL::Surface, x : Int32, y : Int32) + # Draw this sprite to another + def draw_to(surface : SDL::Surface, x : Int, y : Int) @surface.blit(surface, nil, SDL::Rect.new(x, y, width, height)) end - def draw_to(sprite : Sprite, x : Int32, y : Int32) + # ditto + def draw_to(sprite : Sprite, x : Int, y : Int) draw_to(sprite.surface, x, y) end + # ditto + def draw_to(dest : SDL::Surface | Sprite, at : Vector(Int, 2)) + draw_to(dest, at.x, at.y) + end + # Raw access to the pixels as a Slice def pixels Slice.new(@surface.pixels.as(Pointer(UInt32)), width * height) end + # Peak at a raw pixel value at (*x*, *y*) + def peak(x : Int, y : Int) + pixel_pointer(x, y).value + end + + # ditto + def peak(point : Vector(Int, 2)) + pixel_pointer(point.x, point.y).value + end + # Sample a color at an *x* and *y* position def sample(x : Int, y : Int) - raw_pixel = pixel_pointer(x, y).value + raw_pixel = peak(x, y) r = uninitialized UInt8 g = uninitialized UInt8 @@ -78,8 +104,13 @@ module PF Pixel.new(r, g, b, a) end + # ditto + def sample(point : Vector(Int, 2), alpha = true) + sample(point.x, point.y, true) + end + # Get the pointer to a pixel - private def pixel_pointer(x : Int32, y : Int32) + def pixel_pointer(x : Int32, y : Int32) target = @surface.pixels + (y * @surface.pitch) + (x * 4) target.as(Pointer(UInt32)) end diff --git a/src/transform2d.cr b/src/transform2d.cr index 0d3b6b3..4522b94 100644 --- a/src/transform2d.cr +++ b/src/transform2d.cr @@ -46,6 +46,10 @@ module PF self end + def scale(n : Float | Int) + scale(n, n) + end + def rotate(angle : Float | Int) cos = Math.cos(angle) sin = Math.sin(angle)