class Eagle::Graphics

Overview

The 2D drawing context: shapes, sprites, text, transforms, cameras and render targets.

You get one as g in App#draw and Node#draw. Every call is batched, so drawing thousands of sprites costs only a few GPU draw calls. Coordinates are in logical points, with (0, 0) at the top-left and y growing downward.

class Game < App
  @ship = Texture.new(Image.circle(32, Color::WHITE))

  def draw(g : Graphics) : Nil
    g.clear(Color.hex("#1d1611"))

    g.rect(20, 20, 200, 120, color: Color::GRAY)              # filled box
    g.rect(20, 20, 200, 120, DrawMode::Line, Color::WHITE)     # outline
    g.circle(Window.center, 40, color: Color::ORANGE)
    g.line(v2(0, 0), Input.mouse, Color::CYAN, width: 3)

    g.draw(@ship, 300, 200, rotation: Clock.elapsed, ox: 16, oy: 16) # spin around its center

    g.push do # everything in here is rotated around (500, 300)
      g.translate(500, 300)
      g.rotate(0.3)
      g.rect(-25, -25, 50, 50)
    end

    g.print("Score 1200", 10, 10, scale: 2)
    g.printf("Centered and wrapped text", 0, 400, Window.width, align: TextAlign::Center)
  end
end

State such as #color, #line_width, #blend, #shader and #font persists until you change it, and resets at the start of each frame. The with_* methods set something for one block and restore it afterwards, which is the tidy way to make temporary changes.

Defined in:

eagle/graphics/graphics.cr

Constant Summary

MAX_VERTICES = 65532

Vertices per batch. A batch is flushed to the GPU when it fills up.

VERTEX_FLOATS = 8

Floats per 2D vertex: position, UV and color.

Constructors

Instance Method Summary

Constructor Detail

def self.new #

Creates a drawing context. The engine makes one for you; see Eagle.graphics.


Instance Method Detail

def apply(t : Transform2D) : Nil #

Multiplies the current transform by t.


def arc(x : Number, y : Number, radius : Number, a0 : Number, a1 : Number, mode : DrawMode = DrawMode::Fill, color : Color = @color) : Nil #

Draws a pie slice (filled) or an arc (line) from angle a0 to a1 in radians. Angle 0 points right and angles grow clockwise, which suits cooldown timers.

cooldown = 0.25
g.arc(50, 50, 20, -Math::PI / 2, -Math::PI / 2 + Mathf::TAU * cooldown, color: Color::WHITE.alpha(0.6))

def begin_frame : Nil #

--- frame --------------------------------------------------------------- :nodoc:


def blend : GPU::BlendMode #

The current blend mode.


def blend=(mode : GPU::BlendMode) #

--- state --------------------------------------------------------------- Sets how new pixels combine with what's already drawn. Additive makes glows and fire, Multiply darkens, and Alpha is the normal default.


def camera : Camera2D | Nil #

The 2D camera in use, or nil for screen space.


def camera=(cam : Camera2D | Nil) #

Draws in world space through cam, or back in screen space with nil. The engine already uses Camera2D.current for the scene tree, so you need this only for manual drawing.


def canvas : Canvas | Nil #

The canvas being drawn into, or nil for the screen.


def circle(x : Number, y : Number, radius : Number, mode : DrawMode = DrawMode::Fill, color : Color = @color, segments : Int32 | Nil = nil) : Nil #

Draws a circle, filled by default.


def circle(center : Vec2, radius : Number, mode : DrawMode = DrawMode::Fill, color : Color = @color) : Nil #

Draws a circle at center.


def circle_line(x : Number, y : Number, radius : Number, color : Color = @color) : Nil #

Draws a circle outline.


def circle_segments : Int32 | Nil #

Fixed segment count for circles. nil picks a count from the radius, so small circles stay cheap and big ones stay round.


def circle_segments=(circle_segments : Int32 | Nil) #

Fixed segment count for circles. nil picks a count from the radius, so small circles stay cheap and big ones stay round.


def clear(color : Color = Color::BLACK) : Nil #

Clears the whole target to color. On the screen this replaces Config#clear_color for this frame.


def color : Color #

Default color for shapes, text and texture tint. Most methods also take a color: argument.


def color=(color : Color) #

Default color for shapes, text and texture tint. Most methods also take a color: argument.


def dispose : Nil #

Frees GPU buffers. The engine calls it on shutdown.


def draw(d : Drawable, x : Number = 0, y : Number = 0, rotation : Number = 0, sx : Number = 1, sy : Number = sx, ox : Number = 0, oy : Number = 0, color : Color = @color) : Nil #

--- sprites ------------------------------------------------------------- Draws a texture or region with its top-left at x, y. rotation is in radians, sx/sy scale it, and ox/oy set the pivot in texture pixels, which is the point that lands on x, y and that rotation turns around.

tex = Texture.new(Image.circle(32, Color::WHITE))
g.draw(tex, 100, 100)                                     # top-left at (100, 100)
g.draw(tex, 200, 100, rotation: 0.5, ox: 16, oy: 16)       # rotate around its center
g.draw(tex, 300, 100, sx: -1, ox: 32)                     # mirrored horizontally
g.draw(tex, 400, 100, color: Color::RED.alpha(0.5))       # tinted and translucent

def draw(d : Drawable, position : Vec2, rotation : Number = 0, scale : Vec2 = Vec2::ONE, origin : Vec2 = Vec2::ZERO, color : Color = @color) : Nil #

Draws a texture or region using vectors for position, scale and pivot.


def draw(d : Drawable, dest : Rect, color : Color = @color, rotation : Number = 0) : Nil #

Draws a texture or region stretched to fill dest.


def draw(c : Canvas, x : Number = 0, y : Number = 0, rotation : Number = 0, sx : Number = 1, sy : Number = sx, ox : Number = 0, oy : Number = 0, color : Color = @color) : Nil #

Draws a canvas like a texture.


def draw(c : Canvas, dest : Rect, color : Color = @color) : Nil #

Draws a canvas stretched to fill dest.


def draw_calls : Int32 #

def draw_centered(d : Drawable, x : Number, y : Number, rotation : Number = 0, sx : Number = 1, sy : Number = sx, color : Color = @color) : Nil #

Draws a texture or region centered on x, y.


def draw_centered(d : Drawable, p : Vec2, rotation : Number = 0, sx : Number = 1, sy : Number = sx, color : Color = @color) : Nil #

Draws a texture or region centered on p.


def draw_tiled(tex : Texture, dest : Rect, offset : Vec2 = Vec2::ZERO, scale : Number = 1, color : Color = @color) : Nil #

Fills dest by repeating a texture, for backgrounds and floors. The texture must use GPU::Wrap::Repeat. offset scrolls the pattern, which is an easy parallax effect.

floor = Texture.new(Image.checkerboard(32, 32), wrap: GPU::Wrap::Repeat)
g.draw_tiled(floor, Window.rect, offset: v2(Clock.elapsed * 20, 0))

def ellipse(x : Number, y : Number, rx : Number, ry : Number, mode : DrawMode = DrawMode::Fill, color : Color = @color) : Nil #

Draws an ellipse with radii rx and ry.


def flush : Nil #

Sends pending geometry to the GPU now. Eagle does this when needed; call it yourself only when mixing in raw GPU calls.


def font : Font #

Font used by #print when you don't pass one. Starts as Font.default, the built-in pixel font.


def font=(font : Font) #

Font used by #print when you don't pass one. Starts as Font.default, the built-in pixel font.


def line(x1 : Number, y1 : Number, x2 : Number, y2 : Number, color : Color = @color, width : Number = @line_width) : Nil #

Draws a line of width points.


def line(a : Vec2, b : Vec2, color : Color = @color, width : Number = @line_width) : Nil #

Draws a line between two points.


def line_width : Float32 #

Default thickness, in points, for lines, polylines and outlines.


def line_width=(line_width : Float32) #

Default thickness, in points, for lines, polylines and outlines.


def origin : Nil #

Resets the transform to the camera's view, or to identity without a camera.


def point(x : Number, y : Number, color : Color = @color, size : Number = 1) : Nil #

Draws a square dot of size points.


def points(pts : Array(Vec2), color : Color = @color, size : Number = 1) : Nil #

Draws many dots in one go, such as stars or particles.


def polygon(points : Array(Vec2), mode : DrawMode = DrawMode::Fill, color : Color = @color) : Nil #

Draws a polygon. Filled polygons may be concave, as long as the edges don't cross.


def polyline(points : Array(Vec2), color : Color = @color, width : Number = @line_width, closed : Bool = false) : Nil #

Draws connected line segments with mitred joins. Set closed to join the last point to the first.


def pop : Nil #

Restores the transform saved by the last #push.


def print(text : String, x : Number = 0, y : Number = 0, color : Color = @color, font : Font = @font, scale : Number = 1, align : TextAlign = TextAlign::Left) : Nil #

--- text ---------------------------------------------------------------- Draws text with its top-left corner at x, y. Newlines start new lines. With align: TextAlign::Center, x is the center of each line instead.

g.print("GAME OVER", Window.center.x, 200, Color::RED, scale: 3, align: TextAlign::Center)
g.print("line one\nline two", 10, 10)

def print(text : String, pos : Vec2, color : Color = @color, font : Font = @font, scale : Number = 1, align : TextAlign = TextAlign::Left) : Nil #

Draws text at pos.


def printf(text : String, x : Number, y : Number, width : Number, align : TextAlign = TextAlign::Left, color : Color = @color, font : Font = @font, scale : Number = 1) : Nil #

Draws text word-wrapped to fit width, aligned inside that width.


def push : Nil #

--- transform stack ----------------------------------------------------- Saves the current transform. Pair it with #pop.


def push(&) : Nil #

Saves the transform, runs the block, and restores it. Transforms inside the block stay local to it.


def quad_raw(tex : Texture, p0 : Vec2, p1 : Vec2, p2 : Vec2, p3 : Vec2, uv0 : Vec2, uv1 : Vec2, uv2 : Vec2, uv3 : Vec2, color : Color = @color) : Nil #

--- low level ----------------------------------------------------------- Pushes a textured quad with explicit corners and UVs, for custom effects like skewed sprites.


def rect(x : Number, y : Number, w : Number, h : Number, mode : DrawMode = DrawMode::Fill, color : Color = @color) : Nil #

--- shapes -------------------------------------------------------------- Draws a rectangle, filled by default. Pass DrawMode::Line for an outline.


def rect(r : Rect, mode : DrawMode = DrawMode::Fill, color : Color = @color) : Nil #

Draws a Rect, filled by default.


def rect_line(x : Number, y : Number, w : Number, h : Number, color : Color = @color) : Nil #

Draws a rectangle outline.


def rect_line(r : Rect, color : Color = @color) : Nil #

Draws the outline of a Rect.


def rotate(rad : Number) : Nil #

Rotates subsequent drawing by rad radians around the current origin.


def rounded_rect(x : Number, y : Number, w : Number, h : Number, radius : Number, mode : DrawMode = DrawMode::Fill, color : Color = @color) : Nil #

Draws a rectangle with rounded corners of radius, handy for buttons and panels.


def scale(x : Number, y : Number) : Nil #

Scales subsequent drawing by x and y. A negative value mirrors.


def scale(s : Number) : Nil #

Scales subsequent drawing uniformly.


def scissor=(r : Rect | Nil) #

Clips drawing to a rectangle in target coordinates, for example to keep a scrolling list inside its panel. nil turns clipping off.


def scissor_rect : Rect | Nil #

The current clip rectangle, or nil when clipping is off.


def shader : Shader | Nil #

The custom shader in use, or nil for the default.


def shader=(s : Shader | Nil) #

Uses a custom shader for everything drawn afterwards. nil restores the default. Eagle sets u_projection, u_texture, u_time and u_resolution for you. See Shader.effect.


def stats_draw_calls : Int32 #

Draw calls issued so far this frame. Show it in a debug overlay to check batching.


def stats_vertices : Int32 #

Vertices submitted so far this frame.


def target_size : Vec2 #

Size of what you are drawing into: the window, or the canvas inside #with_canvas.


def text_size(text : String, font : Font = @font, scale : Number = 1) : Vec2 #

The width and height text would take up, for centering or sizing backgrounds.


def transform : Transform2D #

The current transform applied to everything drawn.


def transform=(t : Transform2D) #

Replaces the current transform.


def translate(x : Number, y : Number) : Nil #

Moves the origin by x, y.


def translate(v : Vec2) : Nil #

Moves the origin by v.


def triangle(a : Vec2, b : Vec2, c : Vec2, mode : DrawMode = DrawMode::Fill, color : Color = @color) : Nil #

Draws a triangle.


def triangles(tex : Texture, positions : Array(Vec2), uvs : Array(Vec2), colors : Array(Color), indices : Array(Int32)) : Nil #

Pushes arbitrary textured triangles with per-vertex colors. Use it for custom meshes, trails and deformable sprites.


def with_blend(mode : GPU::BlendMode, &) : Nil #

Uses a blend mode for the block, then restores the previous one.

g.with_blend(GPU::BlendMode::Additive) { g.circle(200, 200, 30, color: Color::ORANGE.alpha(0.5)) }

def with_camera(cam : Camera2D | Nil, &) : Nil #

Draws through a camera for the block, then restores the previous camera and transform.


def with_canvas(canvas : Canvas, clear : Color | Nil = Color::TRANSPARENT, &) : Nil #

Draws into canvas instead of the screen for the duration of the block. The canvas is cleared to clear first; pass nil to keep what it already has. The transform starts fresh inside the block.

canvas = Canvas.new(320, 180)
g.with_canvas(canvas) do
  g.circle(160, 90, 40, color: Color::YELLOW)
end
g.draw(canvas, 0, 0, sx: 4) # upscale the low-res image to fill a 1280x720 window

def with_color(c : Color, &) : Nil #

Uses a default color for the block, then restores the previous one.


def with_scissor(r : Rect | Nil, &) : Nil #

Clips drawing to a rectangle for the block.


def with_shader(s : Shader | Nil, &) : Nil #

Uses a shader for the block, then restores the previous one.


def with_transform(t : Transform2D, &) : Nil #

Applies t for the block.