182 lines
4.9 KiB
Go
182 lines
4.9 KiB
Go
// Package rasterx implements a rasterizer in go.
|
|
// By default rasterx uses ScannerGV to render images
|
|
// which uses the rasterizer in the golang.org/x/image/vector package.
|
|
// The freetype rasterizer under the GNU license can also be used, by
|
|
// downloading the scanFT package.
|
|
//
|
|
// Copyright 2018 All rights reserved.
|
|
// Created: 5/12/2018 by S.R.Wiley
|
|
package rasterx
|
|
|
|
import (
|
|
"image"
|
|
"math"
|
|
|
|
"image/color"
|
|
"image/draw"
|
|
|
|
"golang.org/x/image/math/fixed"
|
|
"golang.org/x/image/vector"
|
|
)
|
|
|
|
// At returns the color at the point x,y
|
|
func (c *ColorFuncImage) At(x, y int) color.Color {
|
|
return c.colorFunc(x, y)
|
|
}
|
|
|
|
type (
|
|
// ColorFuncImage implements and image
|
|
// using the provided colorFunc
|
|
ColorFuncImage struct {
|
|
image.Uniform
|
|
colorFunc ColorFunc
|
|
}
|
|
|
|
// ScannerGV uses the google vector rasterizer
|
|
ScannerGV struct {
|
|
r vector.Rasterizer
|
|
//a, first fixed.Point26_6
|
|
Dest draw.Image
|
|
Targ image.Rectangle
|
|
clipImage *ClipImage
|
|
Source image.Image
|
|
Offset image.Point
|
|
minX, minY, maxX, maxY fixed.Int26_6 // keep track of bounds
|
|
}
|
|
)
|
|
|
|
// ClipImage is a clipable ColorFuncImage
|
|
type ClipImage struct {
|
|
ColorFuncImage
|
|
clip image.Rectangle
|
|
}
|
|
|
|
var noApha = color.RGBA{0, 0, 0, 0}
|
|
|
|
// GetPathExtent returns the extent of the path
|
|
func (s *ScannerGV) GetPathExtent() fixed.Rectangle26_6 {
|
|
return fixed.Rectangle26_6{Min: fixed.Point26_6{X: s.minX, Y: s.minY}, Max: fixed.Point26_6{X: s.maxX, Y: s.maxY}}
|
|
}
|
|
|
|
// At returns the color of the ClipImage at the point x,y
|
|
func (c *ClipImage) At(x, y int) color.Color {
|
|
p := image.Point{x, y}
|
|
if p.In(c.clip) {
|
|
return c.ColorFuncImage.At(x, y)
|
|
}
|
|
return noApha
|
|
}
|
|
|
|
// SetWinding set the winding rule for the scanner
|
|
func (s *ScannerGV) SetWinding(useNonZeroWinding bool) {
|
|
// no-op as scanner gv does not support even-odd winding
|
|
}
|
|
|
|
// SetColor set the color type for the scanner
|
|
func (s *ScannerGV) SetColor(clr interface{}) {
|
|
switch c := clr.(type) {
|
|
case color.Color:
|
|
s.clipImage.ColorFuncImage.Uniform.C = c
|
|
if s.clipImage.clip == image.ZR {
|
|
s.Source = &s.clipImage.ColorFuncImage.Uniform
|
|
} else {
|
|
s.clipImage.ColorFuncImage.colorFunc = func(x, y int) color.Color {
|
|
return c
|
|
}
|
|
s.Source = s.clipImage
|
|
}
|
|
case ColorFunc:
|
|
s.clipImage.ColorFuncImage.colorFunc = c
|
|
if s.clipImage.clip == image.ZR {
|
|
s.Source = &s.clipImage.ColorFuncImage
|
|
} else {
|
|
s.Source = s.clipImage
|
|
}
|
|
}
|
|
}
|
|
|
|
// SetClip sets an optional clipping rectangle to restrict rendering only to
|
|
// that region -- if size is 0 then ignored (set to image.ZR to clear)
|
|
func (s *ScannerGV) SetClip(rect image.Rectangle) {
|
|
s.clipImage.clip = rect
|
|
if s.Source == &s.clipImage.ColorFuncImage.Uniform {
|
|
s.SetColor(s.clipImage.ColorFuncImage.Uniform.C)
|
|
} else {
|
|
s.SetColor(s.clipImage.ColorFuncImage.colorFunc)
|
|
}
|
|
}
|
|
|
|
func (s *ScannerGV) set(a fixed.Point26_6) {
|
|
if s.maxX < a.X {
|
|
s.maxX = a.X
|
|
}
|
|
if s.maxY < a.Y {
|
|
s.maxY = a.Y
|
|
}
|
|
if s.minX > a.X {
|
|
s.minX = a.X
|
|
}
|
|
if s.minY > a.Y {
|
|
s.minY = a.Y
|
|
}
|
|
}
|
|
|
|
// Start starts a new path at the given point.
|
|
func (s *ScannerGV) Start(a fixed.Point26_6) {
|
|
s.set(a)
|
|
s.r.MoveTo(float32(a.X)/64, float32(a.Y)/64)
|
|
}
|
|
|
|
// Line adds a linear segment to the current curve.
|
|
func (s *ScannerGV) Line(b fixed.Point26_6) {
|
|
s.set(b)
|
|
s.r.LineTo(float32(b.X)/64, float32(b.Y)/64)
|
|
}
|
|
|
|
// Draw renders the accumulate scan to the desination
|
|
func (s *ScannerGV) Draw() {
|
|
// This draws the entire bounds of the image, because
|
|
// at this point the alpha mask does not shift with the
|
|
// placement of the target rectangle in the vector rasterizer
|
|
s.r.Draw(s.Dest, s.Dest.Bounds(), s.Source, s.Offset)
|
|
|
|
// Remove the line above and uncomment the lines below if you
|
|
// are using a version of the vector rasterizer that shifts the alpha
|
|
// mask with the placement of the target
|
|
|
|
// s.Targ.Min.X = int(s.minX >> 6)
|
|
// s.Targ.Min.Y = int(s.minY >> 6)
|
|
// s.Targ.Max.X = int(s.maxX>>6) + 1
|
|
// s.Targ.Max.Y = int(s.maxY>>6) + 1
|
|
// s.Targ = s.Targ.Intersect(s.Dest.Bounds()) // This check should be done by the rasterizer?
|
|
// s.r.Draw(s.Dest, s.Targ, s.Source, s.Offset)
|
|
}
|
|
|
|
// Clear cancels any previous accumulated scans
|
|
func (s *ScannerGV) Clear() {
|
|
p := s.r.Size()
|
|
s.r.Reset(p.X, p.Y)
|
|
const mxfi = fixed.Int26_6(math.MaxInt32)
|
|
s.minX, s.minY, s.maxX, s.maxY = mxfi, mxfi, -mxfi, -mxfi
|
|
}
|
|
|
|
// SetBounds sets the maximum width and height of the rasterized image and
|
|
// calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
|
|
func (s *ScannerGV) SetBounds(width, height int) {
|
|
s.r.Reset(width, height)
|
|
}
|
|
|
|
// NewScannerGV creates a new Scanner with the given bounds.
|
|
func NewScannerGV(width, height int, dest draw.Image,
|
|
targ image.Rectangle) *ScannerGV {
|
|
s := new(ScannerGV)
|
|
s.SetBounds(width, height)
|
|
s.Dest = dest
|
|
s.Targ = targ
|
|
s.clipImage = &ClipImage{}
|
|
s.clipImage.ColorFuncImage.Uniform.C = &color.RGBA{255, 0, 0, 255}
|
|
s.Source = &s.clipImage.ColorFuncImage.Uniform
|
|
s.Offset = image.Point{0, 0}
|
|
return s
|
|
}
|