Transparency support!!
This commit is contained in:
		
							parent
							
								
									2f0259d913
								
							
						
					
					
						commit
						94db4e8ead
					
				
							
								
								
									
										5
									
								
								box.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								box.go
									
									
									
									
									
								
							@ -3,6 +3,7 @@ package x
 | 
			
		||||
import "image"
 | 
			
		||||
import "image/color"
 | 
			
		||||
import "git.tebibyte.media/tomo/tomo"
 | 
			
		||||
import "git.tebibyte.media/tomo/x/canvas"
 | 
			
		||||
import "git.tebibyte.media/tomo/tomo/data"
 | 
			
		||||
import "git.tebibyte.media/tomo/tomo/input"
 | 
			
		||||
import "git.tebibyte.media/tomo/tomo/event"
 | 
			
		||||
@ -23,7 +24,7 @@ type box struct {
 | 
			
		||||
	padding tomo.Inset
 | 
			
		||||
	border  []tomo.Border
 | 
			
		||||
	color   color.Color
 | 
			
		||||
	texture *texture
 | 
			
		||||
	texture *xcanvas.Texture
 | 
			
		||||
	
 | 
			
		||||
	fillTransparent bool
 | 
			
		||||
 | 
			
		||||
@ -122,7 +123,7 @@ func (this *box) SetColor (c color.Color) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *box) SetTexture (texture tomo.Texture) {
 | 
			
		||||
	this.texture = assertTexture(texture)
 | 
			
		||||
	this.texture = xcanvas.AssertTexture(texture)
 | 
			
		||||
	this.determineFillTransparency()
 | 
			
		||||
	this.invalidateDraw()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import "image"
 | 
			
		||||
import "image/color"
 | 
			
		||||
import "github.com/jezek/xgbutil"
 | 
			
		||||
import "github.com/jezek/xgb/xproto"
 | 
			
		||||
import "git.tebibyte.media/tomo/tomo"
 | 
			
		||||
import "github.com/jezek/xgbutil/xgraphics"
 | 
			
		||||
import "git.tebibyte.media/tomo/tomo/canvas"
 | 
			
		||||
 | 
			
		||||
@ -66,30 +67,41 @@ func (this *Canvas) assert () {
 | 
			
		||||
type pen struct {
 | 
			
		||||
	image *xgraphics.Image
 | 
			
		||||
 | 
			
		||||
	closed bool
 | 
			
		||||
	endCap canvas.Cap
 | 
			
		||||
	joint  canvas.Joint
 | 
			
		||||
	weight int
 | 
			
		||||
	align  canvas.StrokeAlign
 | 
			
		||||
	stroke xgraphics.BGRA
 | 
			
		||||
	fill   xgraphics.BGRA
 | 
			
		||||
	closed  bool
 | 
			
		||||
	endCap  canvas.Cap
 | 
			
		||||
	joint   canvas.Joint
 | 
			
		||||
	weight  int
 | 
			
		||||
	align   canvas.StrokeAlign
 | 
			
		||||
	stroke  xgraphics.BGRA
 | 
			
		||||
	fill    xgraphics.BGRA
 | 
			
		||||
	texture *Texture
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *pen) Rectangle (bounds image.Rectangle) {
 | 
			
		||||
	if this.weight == 0 {
 | 
			
		||||
		this.gfx.fillRectangle(bounds)
 | 
			
		||||
		if this.fill.A > 0 {
 | 
			
		||||
			this.fillRectangle(this.fill, bounds)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		this.gfx.strokeRectangle(bounds)
 | 
			
		||||
		if this.stroke.A > 0 {
 | 
			
		||||
			this.strokeRectangle(this.stroke, bounds)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *pen) Path (points ...image.Point) {
 | 
			
		||||
	if this.weight == 0 {
 | 
			
		||||
		this.fillPolygon(points...)
 | 
			
		||||
		if this.fill.A > 0 {
 | 
			
		||||
			this.fillPolygon(this.fill, points...)
 | 
			
		||||
		}
 | 
			
		||||
	} else if this.closed {
 | 
			
		||||
		this.strokePolygon(points...)
 | 
			
		||||
		if this.stroke.A > 0 {
 | 
			
		||||
			this.strokePolygon(this.stroke, points...)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		this.polyLine(points...)
 | 
			
		||||
		if this.stroke.A > 0 {
 | 
			
		||||
			this.polyLine(this.stroke, points...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -99,8 +111,9 @@ func (this *pen) Joint        (joint  canvas.Joint)      { this.joint  = joint
 | 
			
		||||
func (this *pen) StrokeWeight (weight int)               { this.weight = weight }
 | 
			
		||||
func (this *pen) StrokeAlign  (align canvas.StrokeAlign) { this.align  = align  }
 | 
			
		||||
 | 
			
		||||
func (this *pen) Stroke (stroke color.Color) { this.stroke = convertColor(stroke) }
 | 
			
		||||
func (this *pen) Fill   (fill color.Color)   { this.fill   = convertColor(fill)   }
 | 
			
		||||
func (this *pen) Stroke  (stroke color.Color)   { this.stroke  = convertColor(stroke)   }
 | 
			
		||||
func (this *pen) Fill    (fill color.Color)     { this.fill    = convertColor(fill)     }
 | 
			
		||||
func (this *pen) Texture (texture tomo.Texture) { this.texture = AssertTexture(texture) }
 | 
			
		||||
 | 
			
		||||
func convertColor (c color.Color) xgraphics.BGRA {
 | 
			
		||||
	r, g, b, a := c.RGBA()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										226
									
								
								canvas/draw.go
									
									
									
									
									
								
							
							
						
						
									
										226
									
								
								canvas/draw.go
									
									
									
									
									
								
							@ -1,5 +1,229 @@
 | 
			
		||||
package xcanvas
 | 
			
		||||
 | 
			
		||||
func (this *pen) fillRectangle (bounds image.Rectangle) {
 | 
			
		||||
import "sort"
 | 
			
		||||
import "image"
 | 
			
		||||
import "github.com/jezek/xgbutil/xgraphics"
 | 
			
		||||
 | 
			
		||||
func (this *pen) fillRectangle (c xgraphics.BGRA, bounds image.Rectangle) {
 | 
			
		||||
	if c.A == 255 {
 | 
			
		||||
		this.fillRectangleOpaque(c, bounds)
 | 
			
		||||
	} else {
 | 
			
		||||
		this.fillRectangleTransparent(c, bounds)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *pen) fillRectangleOpaque (c xgraphics.BGRA, bounds image.Rectangle) {
 | 
			
		||||
	bounds = bounds.Intersect(this.image.Bounds())
 | 
			
		||||
	var pos image.Point
 | 
			
		||||
 | 
			
		||||
	for pos.Y = bounds.Min.Y; pos.Y < bounds.Max.Y; pos.Y ++ {
 | 
			
		||||
	for pos.X = bounds.Min.X; pos.X < bounds.Max.X; pos.X ++ {
 | 
			
		||||
		index := this.image.PixOffset(pos.X, pos.Y)
 | 
			
		||||
		this.image.Pix[index + 0] = c.B
 | 
			
		||||
		this.image.Pix[index + 1] = c.G
 | 
			
		||||
		this.image.Pix[index + 2] = c.R
 | 
			
		||||
		this.image.Pix[index + 3] = c.A
 | 
			
		||||
	}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *pen) fillRectangleTransparent (c xgraphics.BGRA, bounds image.Rectangle) {
 | 
			
		||||
	bounds = bounds.Intersect(this.image.Bounds())
 | 
			
		||||
	var pos image.Point
 | 
			
		||||
 | 
			
		||||
	for pos.Y = bounds.Min.Y; pos.Y < bounds.Max.Y; pos.Y ++ {
 | 
			
		||||
	for pos.X = bounds.Min.X; pos.X < bounds.Max.X; pos.X ++ {
 | 
			
		||||
		index := this.image.PixOffset(pos.X, pos.Y)
 | 
			
		||||
		pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
 | 
			
		||||
			B: this.image.Pix[index + 0],
 | 
			
		||||
			G: this.image.Pix[index + 1],
 | 
			
		||||
			R: this.image.Pix[index + 2],
 | 
			
		||||
			A: this.image.Pix[index + 3],
 | 
			
		||||
		}, c)
 | 
			
		||||
		this.image.Pix[index + 0] = pixel.B
 | 
			
		||||
		this.image.Pix[index + 1] = pixel.G
 | 
			
		||||
		this.image.Pix[index + 2] = pixel.R
 | 
			
		||||
		this.image.Pix[index + 3] = pixel.A
 | 
			
		||||
	}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *pen) strokeRectangle (c xgraphics.BGRA, bounds image.Rectangle) {
 | 
			
		||||
	if this.weight > bounds.Dx() / 2 || this.weight > bounds.Dy() / 2 {
 | 
			
		||||
		this.fillRectangle(c, bounds)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	top := image.Rect (
 | 
			
		||||
		bounds.Min.X,
 | 
			
		||||
		bounds.Min.Y,
 | 
			
		||||
		bounds.Max.X,
 | 
			
		||||
		bounds.Min.Y + this.weight)
 | 
			
		||||
	bottom := image.Rect (
 | 
			
		||||
		bounds.Min.X,
 | 
			
		||||
		bounds.Max.Y - this.weight,
 | 
			
		||||
		bounds.Max.X,
 | 
			
		||||
		bounds.Max.Y)
 | 
			
		||||
	left := image.Rect (
 | 
			
		||||
		bounds.Min.X,
 | 
			
		||||
		bounds.Min.Y + this.weight,
 | 
			
		||||
		bounds.Min.X + this.weight,
 | 
			
		||||
		bounds.Max.Y - this.weight)
 | 
			
		||||
	right := image.Rect (
 | 
			
		||||
		bounds.Max.X - this.weight,
 | 
			
		||||
		bounds.Min.Y + this.weight,
 | 
			
		||||
		bounds.Max.X,
 | 
			
		||||
		bounds.Max.Y - this.weight)
 | 
			
		||||
 | 
			
		||||
	this.fillRectangle(c, top,)
 | 
			
		||||
	this.fillRectangle(c, bottom,)
 | 
			
		||||
	this.fillRectangle(c, left,)
 | 
			
		||||
	this.fillRectangle(c, right,)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// the polygon filling algorithm is adapted from:
 | 
			
		||||
// https://www.alienryderflex.com/polygon_fill/
 | 
			
		||||
// (if you write C like that i will disassemble you)
 | 
			
		||||
	
 | 
			
		||||
func (this *pen) fillPolygon (c xgraphics.BGRA, points ...image.Point) {
 | 
			
		||||
	if len(points) < 3 { return }
 | 
			
		||||
 | 
			
		||||
	// figure out the bounds of the polygon so we don't test empty space
 | 
			
		||||
	var area image.Rectangle
 | 
			
		||||
	area.Min = points[0]
 | 
			
		||||
	area.Max = points[0]
 | 
			
		||||
	for _, point := range points[1:] {
 | 
			
		||||
		if point.X < area.Min.X { area.Min.X = point.X }
 | 
			
		||||
		if point.Y < area.Min.Y { area.Min.Y = point.Y }
 | 
			
		||||
		if point.X > area.Max.X { area.Max.X = point.X }
 | 
			
		||||
		if point.Y > area.Max.Y { area.Max.Y = point.Y }
 | 
			
		||||
	}
 | 
			
		||||
	area = this.image.Bounds().Intersect(area)
 | 
			
		||||
	if area.Empty() { return }
 | 
			
		||||
 | 
			
		||||
	context := fillingContext {
 | 
			
		||||
		image:      this.image,
 | 
			
		||||
		color:      this.fill,
 | 
			
		||||
		min:        area.Min.X,
 | 
			
		||||
		max:        area.Max.X,
 | 
			
		||||
		boundaries: make([]int, len(points)),
 | 
			
		||||
		points:     points,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for context.y = area.Min.Y; context.y < area.Max.Y; context.y ++ {
 | 
			
		||||
		// build boundary list
 | 
			
		||||
		boundaryCount := 0
 | 
			
		||||
		prevPoint := points[len(points) - 1]
 | 
			
		||||
		for _, point := range points {
 | 
			
		||||
			fy      := float64(context.y)
 | 
			
		||||
			fPointX := float64(point.X)
 | 
			
		||||
			fPointY := float64(point.Y)
 | 
			
		||||
			fPrevX  := float64(prevPoint.X)
 | 
			
		||||
			fPrevY  := float64(prevPoint.Y)
 | 
			
		||||
			addboundary :=
 | 
			
		||||
				(fPointY < fy && fPrevY  >= fy) ||
 | 
			
		||||
				(fPrevY  < fy && fPointY >= fy)
 | 
			
		||||
			if addboundary {
 | 
			
		||||
				context.boundaries[boundaryCount] = int (
 | 
			
		||||
					fPointX +
 | 
			
		||||
					(fy - fPointY) /
 | 
			
		||||
					(fPrevY - fPointY) *
 | 
			
		||||
					(fPrevX - fPointX))
 | 
			
		||||
				boundaryCount ++
 | 
			
		||||
			}
 | 
			
		||||
			prevPoint = point
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// sort boundary list
 | 
			
		||||
		cutBoundaries := context.boundaries[:boundaryCount]
 | 
			
		||||
		sort.Ints(cutBoundaries)
 | 
			
		||||
 | 
			
		||||
		// fill pixels between boundary pairs
 | 
			
		||||
		if c.A == 255 {
 | 
			
		||||
			context.fillPolygonHotOpaque()
 | 
			
		||||
		} else {
 | 
			
		||||
			context.fillPolygonHotTransparent()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type fillingContext struct {
 | 
			
		||||
	image      *xgraphics.Image
 | 
			
		||||
	color      xgraphics.BGRA
 | 
			
		||||
	min, max   int
 | 
			
		||||
	y          int
 | 
			
		||||
	boundaries []int
 | 
			
		||||
	points     []image.Point
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (context *fillingContext) fillPolygonHotOpaque () {
 | 
			
		||||
	for index := 0; index < len(context.boundaries); index += 2 {
 | 
			
		||||
		left  := context.boundaries[index]
 | 
			
		||||
		right := context.boundaries[index + 1]
 | 
			
		||||
 | 
			
		||||
		// stop if we have exited the polygon
 | 
			
		||||
		if left >= context.max { break }
 | 
			
		||||
		// begin filling if we are within the polygon
 | 
			
		||||
		if right > context.min {
 | 
			
		||||
			// constrain boundaries to image size
 | 
			
		||||
			if left  < context.min { left  = context.min }
 | 
			
		||||
			if right > context.max { right = context.max }
 | 
			
		||||
			
 | 
			
		||||
			// fill pixels in between
 | 
			
		||||
			for x := left; x < right; x ++ {
 | 
			
		||||
				index := context.image.PixOffset(x, context.y)
 | 
			
		||||
				context.image.Pix[index + 0] = context.color.B
 | 
			
		||||
				context.image.Pix[index + 1] = context.color.G
 | 
			
		||||
				context.image.Pix[index + 2] = context.color.R
 | 
			
		||||
				context.image.Pix[index + 3] = context.color.A
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (context *fillingContext) fillPolygonHotTransparent () {
 | 
			
		||||
	for index := 0; index < len(context.boundaries); index += 2 {
 | 
			
		||||
		left  := context.boundaries[index]
 | 
			
		||||
		right := context.boundaries[index + 1]
 | 
			
		||||
 | 
			
		||||
		// stop if we have exited the polygon
 | 
			
		||||
		if left >= context.max { break }
 | 
			
		||||
		// begin filling if we are within the polygon
 | 
			
		||||
		if right > context.min {
 | 
			
		||||
			// constrain boundaries to image size
 | 
			
		||||
			if left  < context.min { left  = context.min }
 | 
			
		||||
			if right > context.max { right = context.max }
 | 
			
		||||
			
 | 
			
		||||
			// fill pixels in between
 | 
			
		||||
			for x := left; x < right; x ++ {
 | 
			
		||||
				index := context.image.PixOffset(x, context.y)
 | 
			
		||||
				pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
 | 
			
		||||
					B: context.image.Pix[index + 0],
 | 
			
		||||
					G: context.image.Pix[index + 1],
 | 
			
		||||
					R: context.image.Pix[index + 2],
 | 
			
		||||
					A: context.image.Pix[index + 3],
 | 
			
		||||
				}, context.color)
 | 
			
		||||
				context.image.Pix[index + 0] = pixel.B
 | 
			
		||||
				context.image.Pix[index + 1] = pixel.G
 | 
			
		||||
				context.image.Pix[index + 2] = pixel.R
 | 
			
		||||
				context.image.Pix[index + 3] = pixel.A
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *pen) strokePolygon (c xgraphics.BGRA, points ...image.Point) {
 | 
			
		||||
	prevPoint := points[len(points) - 1]
 | 
			
		||||
	for _, point := range points {
 | 
			
		||||
		this.line(c, prevPoint, point)
 | 
			
		||||
		prevPoint = point
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *pen) polyLine (c xgraphics.BGRA, points ...image.Point) {
 | 
			
		||||
	if len(points) < 2 { return }
 | 
			
		||||
	prevPoint := points[0]
 | 
			
		||||
	for _, point := range points[1:] {
 | 
			
		||||
		this.line(c, prevPoint, point)
 | 
			
		||||
		prevPoint = point
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										94
									
								
								canvas/line.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								canvas/line.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,94 @@
 | 
			
		||||
package xcanvas
 | 
			
		||||
 | 
			
		||||
import "image"
 | 
			
		||||
import "github.com/jezek/xgbutil/xgraphics"
 | 
			
		||||
 | 
			
		||||
func (this *pen) line  (
 | 
			
		||||
	c      xgraphics.BGRA,
 | 
			
		||||
	min    image.Point,
 | 
			
		||||
	max    image.Point,
 | 
			
		||||
) {
 | 
			
		||||
	context := linePlottingContext {
 | 
			
		||||
		plottingContext: plottingContext {
 | 
			
		||||
			image:  this.image,
 | 
			
		||||
			color:  c,
 | 
			
		||||
			weight: this.weight,
 | 
			
		||||
		},
 | 
			
		||||
		min: min,
 | 
			
		||||
		max: max,
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if abs(max.Y - min.Y) < abs(max.X - min.X) {
 | 
			
		||||
		if max.X < min.X { context.swap() }
 | 
			
		||||
		context.lineLow()
 | 
			
		||||
		
 | 
			
		||||
	} else {
 | 
			
		||||
		if max.Y < min.Y { context.swap() }
 | 
			
		||||
		context.lineHigh()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type linePlottingContext struct {
 | 
			
		||||
	plottingContext
 | 
			
		||||
	min image.Point
 | 
			
		||||
	max image.Point
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (context *linePlottingContext) swap () {
 | 
			
		||||
	temp := context.max
 | 
			
		||||
	context.max = context.min
 | 
			
		||||
	context.min = temp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (context linePlottingContext) lineLow () {
 | 
			
		||||
	deltaX := context.max.X - context.min.X
 | 
			
		||||
	deltaY := context.max.Y - context.min.Y
 | 
			
		||||
	yi     := 1
 | 
			
		||||
 | 
			
		||||
	if deltaY < 0 {
 | 
			
		||||
		yi      = -1
 | 
			
		||||
		deltaY *= -1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	D := (2 * deltaY) - deltaX
 | 
			
		||||
	point := context.min
 | 
			
		||||
 | 
			
		||||
	for ; point.X < context.max.X; point.X ++ {
 | 
			
		||||
		context.plot(point)
 | 
			
		||||
		if D > 0 {
 | 
			
		||||
			D += 2 * (deltaY - deltaX)
 | 
			
		||||
			point.Y += yi
 | 
			
		||||
		} else {
 | 
			
		||||
			D += 2 * deltaY
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (context linePlottingContext) lineHigh () {
 | 
			
		||||
	deltaX := context.max.X - context.min.X
 | 
			
		||||
	deltaY := context.max.Y - context.min.Y
 | 
			
		||||
	xi     := 1
 | 
			
		||||
 | 
			
		||||
	if deltaX < 0 {
 | 
			
		||||
		xi      = -1
 | 
			
		||||
		deltaX *= -1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	D := (2 * deltaX) - deltaY
 | 
			
		||||
	point := context.min
 | 
			
		||||
 | 
			
		||||
	for ; point.Y < context.max.Y; point.Y ++ {
 | 
			
		||||
		context.plot(point)
 | 
			
		||||
		if D > 0 {
 | 
			
		||||
			point.X += xi
 | 
			
		||||
			D += 2 * (deltaX - deltaY)
 | 
			
		||||
		} else {
 | 
			
		||||
			D += 2 * deltaX
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func abs (n int) int {
 | 
			
		||||
	if n < 0 { n *= -1}
 | 
			
		||||
	return n
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								canvas/plot.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								canvas/plot.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
package xcanvas
 | 
			
		||||
 | 
			
		||||
import "image"
 | 
			
		||||
import "github.com/jezek/xgbutil/xgraphics"
 | 
			
		||||
 | 
			
		||||
type plottingContext struct {
 | 
			
		||||
	image  *xgraphics.Image
 | 
			
		||||
	color  xgraphics.BGRA
 | 
			
		||||
	weight int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (context plottingContext) square (center image.Point) (square image.Rectangle) {
 | 
			
		||||
	return image.Rect(0, 0, context.weight, context.weight).
 | 
			
		||||
		Sub(image.Pt(context.weight / 2, context.weight / 2)).
 | 
			
		||||
		Add(center).
 | 
			
		||||
		Intersect(context.image.Bounds())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (context plottingContext) plot (center image.Point) {
 | 
			
		||||
	square := context.square(center)
 | 
			
		||||
 | 
			
		||||
	if context.color.A == 255 {
 | 
			
		||||
		for y := square.Min.Y; y < square.Max.Y; y ++ {
 | 
			
		||||
		for x := square.Min.X; x < square.Max.X; x ++ {
 | 
			
		||||
			index := context.image.PixOffset(x, y)
 | 
			
		||||
			context.image.Pix[index + 0] = context.color.B
 | 
			
		||||
			context.image.Pix[index + 1] = context.color.G
 | 
			
		||||
			context.image.Pix[index + 2] = context.color.R
 | 
			
		||||
			context.image.Pix[index + 3] = context.color.A
 | 
			
		||||
		}}
 | 
			
		||||
	} else {
 | 
			
		||||
		for y := square.Min.Y; y < square.Max.Y; y ++ {
 | 
			
		||||
		for x := square.Min.X; x < square.Max.X; x ++ {
 | 
			
		||||
			index := context.image.PixOffset(x, y)
 | 
			
		||||
			pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
 | 
			
		||||
				B: context.image.Pix[index + 0],
 | 
			
		||||
				G: context.image.Pix[index + 1],
 | 
			
		||||
				R: context.image.Pix[index + 2],
 | 
			
		||||
				A: context.image.Pix[index + 3],
 | 
			
		||||
			}, context.color)
 | 
			
		||||
			context.image.Pix[index + 0] = pixel.B
 | 
			
		||||
			context.image.Pix[index + 1] = pixel.G
 | 
			
		||||
			context.image.Pix[index + 2] = pixel.R
 | 
			
		||||
			context.image.Pix[index + 3] = pixel.A
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package xcanvas
 | 
			
		||||
 | 
			
		||||
import "image"
 | 
			
		||||
import "git.tebibyte.media/tomo/tomo"
 | 
			
		||||
 | 
			
		||||
// Texture is a read-only image texture that can be quickly written to a canvas.
 | 
			
		||||
// It must be closed manually after use.
 | 
			
		||||
@ -52,9 +53,17 @@ func (this *Texture) Close () error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clip returns a subset of this texture that points to the same data.
 | 
			
		||||
func (this *Texture) Clip (bounds image.Rectangle) *Texture {
 | 
			
		||||
func (this *Texture) Clip (bounds image.Rectangle) tomo.Texture {
 | 
			
		||||
	clipped := *this
 | 
			
		||||
	clipped.rect = bounds
 | 
			
		||||
	return &clipped
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AssertTexture checks if a given tomo.Texture is a texture from this package.
 | 
			
		||||
func AssertTexture (unknown tomo.Texture) *Texture {
 | 
			
		||||
	if tx, ok := unknown.(*Texture); ok {
 | 
			
		||||
		return tx
 | 
			
		||||
	} else {
 | 
			
		||||
		panic("foregin texture implementation, i did not make this!")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								texture.go
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								texture.go
									
									
									
									
									
								
							@ -4,26 +4,6 @@ import "image"
 | 
			
		||||
import "git.tebibyte.media/tomo/tomo"
 | 
			
		||||
import "git.tebibyte.media/tomo/x/canvas"
 | 
			
		||||
 | 
			
		||||
type texture struct {
 | 
			
		||||
	*xcanvas.Texture
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (backend Backend) NewTexture (source image.Image) tomo.Texture {
 | 
			
		||||
	return texture {
 | 
			
		||||
		Texture: xcanvas.NewTextureFrom(source),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this texture) Clip (bounds image.Rectangle) tomo.Texture {
 | 
			
		||||
	return texture {
 | 
			
		||||
		Texture: this.Texture.Clip(bounds),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func assertTexture (unknown tomo.Texture) *texture {
 | 
			
		||||
	if tx, ok := unknown.(*texture); ok {
 | 
			
		||||
		return tx
 | 
			
		||||
	} else {
 | 
			
		||||
		panic("foregin texture implementation, i did not make this!")
 | 
			
		||||
	}
 | 
			
		||||
	return xcanvas.NewTextureFrom(source)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user