Skip to content

Commit

Permalink
Making go-sdl2 a bit more compatible with Go standard libraries (#369)
Browse files Browse the repository at this point in the history
* Using image/color package for SDL_Color

* Implementing basic image.Image and draw.Image interfaces

* Introducing a truetype-compatible rasterizer

* Added some doc

* Added some doc

* Fixes

* Minor changes in test code

* Fixed houndci-bot notes

* Fixed houndci-bot notes

* Fixed houndci-bot notes
  • Loading branch information
akiross authored and veeableful committed Dec 10, 2018
1 parent b4c40fb commit 49a231d
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 26 deletions.
68 changes: 68 additions & 0 deletions raster/painter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// This code is inspired to golang/freetype/raster

// Package raster implements a Painter interface for rasterizing paths over
// a generic Image, using its ColorModel to convert from a generic raster color
// to the correct color model.
package raster

import (
"github.com/golang/freetype/raster"
"image/color"
"image/draw"
)

// ImagePainter operates on a generic Image (not only sdl.Surfaces) and allows
// to rasterize a path using a specific color.
type ImagePainter struct {
Image draw.Image
c color.Color
}

// Paint a batch of Spans using the current ImagePainter image and color.
// Image's Color model will be used to convert the color.
func (p *ImagePainter) Paint(ss []raster.Span, done bool) {
// Convert color to RGBA
dr, dg, db, da := p.c.RGBA() // 16 bit values
// Alpha mask
const m = 1<<16 - 1
// Draw spans
b := p.Image.Bounds()
for _, s := range ss {
if s.Y < b.Min.Y || s.Y >= b.Max.Y {
continue
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
for x := s.X0; x < s.X1; x++ {
y := s.Y - b.Min.Y
var ma uint32 = s.Alpha // 16 bit value
// Get destination pixel color in RGBA64
sr, sg, sb, sa := p.Image.At(x, y).RGBA() // 16 bit values
// Compute destination color in RGBA64
var a = (m - (da * ma / m))
rr := uint16((dr*ma + sr*a) / m)
gg := uint16((dg*ma + sg*a) / m)
bb := uint16((db*ma + sb*a) / m)
aa := uint16((da*ma + sa*a) / m)
// Use image model to convert
p.Image.Set(x, y, color.RGBA64{rr, gg, bb, aa})
}
}
}

// SetColor set the color to use when rasterizing
func (p *ImagePainter) SetColor(c color.Color) {
p.c = c
}

// NewImagePainter builds a Painter for a generic Image
func NewImagePainter(m draw.Image) *ImagePainter {
return &ImagePainter{Image: m}
}
41 changes: 41 additions & 0 deletions raster/painter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package raster

import (
"testing"

"image"
"image/color"
"image/png"
"os"

"github.com/golang/freetype/raster"
)

func TestImagePainter(t *testing.T) {
// Spans to render
ss := []raster.Span{
raster.Span{0, 4, 6, 0xffff},
raster.Span{1, 3, 7, 0xf0f0},
raster.Span{2, 2, 8, 0x8f8f},
raster.Span{3, 1, 9, 0x8080},
raster.Span{4, 0, 10, 0x0f0f},
}

img := image.NewRGBA(image.Rect(0, 0, 10, 10))
pt := NewImagePainter(img)
pt.SetColor(color.RGBA{0xff, 0x00, 0x00, 0xff})
pt.Paint(ss, false)

// Write to PNG
f, err := os.Create("output.png")
if err != nil {
t.Error(err)
}
if err := png.Encode(f, img); err != nil {
f.Close()
t.Error(err)
}
if err := f.Close(); err != nil {
t.Error(err)
}
}
29 changes: 3 additions & 26 deletions sdl/pixels.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sdl
// #include "sdl_wrapper.h"
import "C"
import "unsafe"
import "image/color"

// PixelFormat contains pixel format information.
// (https://wiki.libsdl.org/SDL_PixelFormat)
Expand Down Expand Up @@ -39,14 +40,9 @@ type Palette struct {
}
type cPalette C.SDL_Palette

// Color represents a color.
// Color represents a color. This implements image/color.Color interface.
// (https://wiki.libsdl.org/SDL_Color)
type Color struct {
R uint8 // the red component in the range 0-255
G uint8 // the green component in the range 0-255
B uint8 // the blue component in the range 0-255
A uint8 // the alpha component in the range 0-255
}
type Color color.RGBA

// Uint32 return uint32 representation of RGBA color.
func (c Color) Uint32() uint32 {
Expand All @@ -58,25 +54,6 @@ func (c Color) Uint32() uint32 {
return v
}

// RGBA returns the alpha-premultiplied red, green, blue and alpha values
// for the color. Each value ranges within [0, 0xffff], but is represented
// by a uint32 so that multiplying by a blend factor up to 0xffff will not
// overflow.
//
// An alpha-premultiplied color component c has been scaled by alpha (a),
// so has valid values 0 <= c <= a.
func (c Color) RGBA() (r, g, b, a uint32) {
r = uint32(c.R)
r |= r << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
a = uint32(c.A)
a |= a << 8
return
}

// Pixel types.
const (
PIXELTYPE_UNKNOWN = C.SDL_PIXELTYPE_UNKNOWN
Expand Down
72 changes: 72 additions & 0 deletions sdl/surface.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ static inline SDL_Surface* SDL_CreateRGBSurfaceWithFormatFrom(void* pixels, int
import "C"
import "unsafe"
import "reflect"
import "image"
import "image/color"

// Surface flags (internal use)
const (
Expand Down Expand Up @@ -521,3 +523,73 @@ func (surface *Surface) Duplicate() (newSurface *Surface, err error) {
newSurface = (*Surface)(unsafe.Pointer(_newSurface))
return
}

// ColorModel returns the color model used by this Surface.
func (surface *Surface) ColorModel() color.Model {
switch surface.Format.Format {
case PIXELFORMAT_ARGB8888, PIXELFORMAT_ABGR8888:
return color.RGBAModel
case PIXELFORMAT_RGB888:
return color.RGBAModel
default:
panic("Not implemented yet")
}
}

// Bounds return the bounds of this surface. Currently, it always starts at
// (0,0), but this is not guaranteed in the future so don't rely on it.
func (surface *Surface) Bounds() image.Rectangle {
return image.Rect(0, 0, int(surface.W), int(surface.H))
}

// At returns the pixel color at (x, y)
func (surface *Surface) At(x, y int) color.Color {
pix := surface.Pixels()
i := int32(y)*surface.Pitch + int32(x)*int32(surface.Format.BytesPerPixel)
switch surface.Format.Format {
/*
case PIXELFORMAT_ARGB8888:
return color.RGBA{pix[i+3], pix[i], pix[i+1], pix[i+2]}
case PIXELFORMAT_ABGR8888:
return color.RGBA{pix[i], pix[i+3], pix[i+2], pix[i+1]}
*/
case PIXELFORMAT_RGB888:
return color.RGBA{pix[i], pix[i+1], pix[i+2], 0xff}
default:
panic("Not implemented yet")
}
}

// Set the color of the pixel at (x, y) using this surface's color model to
// convert c to the appropriate color. This method is required for the
// draw.Image interface. The surface may require locking before calling Set.
func (surface *Surface) Set(x, y int, c color.Color) {
pix := surface.Pixels()
i := int32(y)*surface.Pitch + int32(x)*int32(surface.Format.BytesPerPixel)
switch surface.Format.Format {
case PIXELFORMAT_ARGB8888:
col := surface.ColorModel().Convert(c).(color.RGBA)
pix[i+0] = col.R
pix[i+1] = col.G
pix[i+2] = col.B
pix[i+3] = col.A
case PIXELFORMAT_ABGR8888:
col := surface.ColorModel().Convert(c).(color.RGBA)
pix[i+3] = col.R
pix[i+2] = col.G
pix[i+1] = col.B
pix[i+0] = col.A
case PIXELFORMAT_RGB24, PIXELFORMAT_RGB888:
col := surface.ColorModel().Convert(c).(color.RGBA)
pix[i+0] = col.B
pix[i+1] = col.G
pix[i+2] = col.R
case PIXELFORMAT_BGR24, PIXELFORMAT_BGR888:
col := surface.ColorModel().Convert(c).(color.RGBA)
pix[i+2] = col.R
pix[i+1] = col.G
pix[i+0] = col.B
default:
panic("Unknown pixel format!")
}
}

0 comments on commit 49a231d

Please sign in to comment.