zntg/allegro5/bitmap.go

210 lines
5.5 KiB
Go
Raw Normal View History

package allegro5
// #include <allegro5/allegro.h>
// #include <stdlib.h>
import "C"
import (
"errors"
"image"
"image/color"
"unsafe"
)
// Bitmap represents an in memory bitmap
type Bitmap struct {
bitmap *C.ALLEGRO_BITMAP
width int
height int
}
type DrawOptions struct {
Center bool
Scale Scale
Tint *Color
Rotation *Rotation
}
type Scale interface {
Horizontal() float32
Vertical() float32
}
type scale struct {
horizontal float32
vertical float32
}
func (s *scale) Horizontal() float32 { return s.horizontal }
func (s *scale) Vertical() float32 { return s.vertical }
func NewScale(horizontal, vertical float32) Scale {
return &scale{horizontal, vertical}
}
func NewUniformScale(s float32) Scale {
return &scale{s, s}
}
type Rotation struct {
Angle float32
Center bool
}
// NewBitmap creates a new bitmap of given width and height
func NewBitmap(width, height int) (*Bitmap, error) {
b := C.al_create_bitmap(C.int(width), C.int(height))
if nil == b {
return nil, errors.New("error creating bitmap")
}
return &Bitmap{b, width, height}, nil
}
type newBmpFlagsCapture struct {
cap C.int
}
func captureNewBmpFlags() *newBmpFlagsCapture {
var cap = C.al_get_new_bitmap_flags()
return &newBmpFlagsCapture{cap}
}
func (c *newBmpFlagsCapture) Mut(mut func(*flagMut)) {
var m = &flagMut{c.cap}
mut(m)
C.al_set_new_bitmap_flags(m.flg)
}
func (c *newBmpFlagsCapture) Revert() {
C.al_set_new_bitmap_flags(c.cap)
}
// NewBitmapFromImage creates a new bitmap starting from a Go native image (image.Image)
func NewBitmapFromImage(im image.Image, video bool) (*Bitmap, error) {
var newBmpFlags = captureNewBmpFlags()
defer newBmpFlags.Revert()
newBmpFlags.Mut(func(m *flagMut) {
m.Unset(C.ALLEGRO_VIDEO_BITMAP)
m.Set(C.ALLEGRO_MEMORY_BITMAP)
m.Set(C.ALLEGRO_MIN_LINEAR)
})
var bnd = im.Bounds()
width, height := bnd.Dx(), bnd.Dy()
var b = C.al_create_bitmap(C.int(width), C.int(height))
if nil == b {
return nil, errors.New("error creating memory bitmap")
}
row := make([]uint8, width*4)
rgn := C.al_lock_bitmap(b, C.ALLEGRO_PIXEL_FORMAT_ABGR_8888, C.ALLEGRO_LOCK_WRITEONLY)
if nil == rgn {
C.al_destroy_bitmap(b)
return nil, errors.New("unable to lock bitmap")
}
data := (*[1 << 30]uint8)(rgn.data)
offset := 0
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
pix := color.RGBAModel.Convert(im.At(x, y)).(color.RGBA)
row[x*4] = pix.R
row[x*4+1] = pix.G
row[x*4+2] = pix.B
row[x*4+3] = pix.A
}
copy(data[offset:], row)
offset += int(rgn.pitch)
}
C.al_unlock_bitmap(b)
if video {
newBmpFlags.Mut(func(m *flagMut) {
m.Unset(C.ALLEGRO_MEMORY_BITMAP)
m.Set(C.ALLEGRO_VIDEO_BITMAP)
m.Set(C.ALLEGRO_MIN_LINEAR)
})
C.al_convert_bitmap(b)
}
return &Bitmap{b, width, height}, nil
}
// LoadBitmap tries to load the image at the specified path as a bitmap
func LoadBitmap(path string) (*Bitmap, error) {
p := C.CString(path)
defer C.free(unsafe.Pointer(p))
b := C.al_load_bitmap(p)
if nil == b {
return nil, errors.New("error loading bitmap")
}
width := int(C.al_get_bitmap_width(b))
height := int(C.al_get_bitmap_height(b))
return &Bitmap{b, width, height}, nil
}
// Draw draws the bitmap at the given location
func (b *Bitmap) Draw(left, top float32) {
C.al_draw_bitmap(b.bitmap, C.float(left), C.float(top), 0)
}
func (b *Bitmap) DrawOptions(left, top float32, options DrawOptions) {
width := float32(b.width)
height := float32(b.height)
scale := nil != options.Scale
if scale {
width *= options.Scale.Horizontal()
height *= options.Scale.Vertical()
}
if options.Center {
left -= width * 0.5
top -= height * 0.5
}
rotated := nil != options.Rotation
var centerX C.float
var centerY C.float
if rotated && options.Rotation.Center {
centerX = C.float(b.width) * 0.5
centerY = C.float(b.height) * 0.5
}
if scale {
if nil == options.Tint { // scaled
if rotated { // scaled & rotated
C.al_draw_scaled_rotated_bitmap(b.bitmap, centerX, centerY, C.float(left), C.float(top), C.float(options.Scale.Horizontal()), C.float(options.Scale.Vertical()), C.float(options.Rotation.Angle), 0)
} else { // scaled
C.al_draw_scaled_bitmap(b.bitmap, 0, 0, C.float(b.width), C.float(b.height), C.float(left), C.float(top), C.float(width), C.float(height), 0)
}
} else { // tinted & scaled
if rotated { // scaled, tinted & rotated
C.al_draw_tinted_scaled_rotated_bitmap(b.bitmap, options.Tint.color, centerX, centerY, C.float(left), C.float(top), C.float(options.Scale.Horizontal()), C.float(options.Scale.Vertical()), C.float(options.Rotation.Angle), 0)
} else { // tinted, scaled
C.al_draw_tinted_scaled_bitmap(b.bitmap, options.Tint.color, 0, 0, C.float(b.width), C.float(b.height), C.float(left), C.float(top), C.float(width), C.float(height), 0)
}
}
} else {
if nil == options.Tint {
if rotated { // rotated
C.al_draw_rotated_bitmap(b.bitmap, centerX, centerY, C.float(left), C.float(top), C.float(options.Rotation.Angle), 0)
} else {
C.al_draw_bitmap(b.bitmap, C.float(left), C.float(top), 0)
}
} else { // tinted
if rotated { // tinted & rotated
C.al_draw_tinted_rotated_bitmap(b.bitmap, options.Tint.color, centerX, centerY, C.float(left), C.float(top), C.float(options.Rotation.Angle), 0)
} else {
C.al_draw_tinted_bitmap(b.bitmap, options.Tint.color, C.float(left), C.float(top), 0)
}
}
}
}
func (b *Bitmap) Width() int {
return b.width
}
func (b *Bitmap) Height() int {
return b.height
}
// Destroy destroys the bitmap
func (b *Bitmap) Destroy() {
C.al_destroy_bitmap(b.bitmap)
}