adam-gui/vendor/github.com/srwiley/rasterx/scan.go

182 lines
4.9 KiB
Go
Raw Normal View History

2024-04-29 19:13:50 +02:00
// 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
}