implement clipping
This commit is contained in:
parent
cc92081ed6
commit
64d88fe46a
14
README.md
14
README.md
@ -147,6 +147,19 @@ Push()
|
||||
Pop()
|
||||
```
|
||||
|
||||
## Clipping Functions
|
||||
|
||||
Use clipping regions to restrict drawing operations to an area that you
|
||||
defined using paths.
|
||||
|
||||
```go
|
||||
Clip()
|
||||
ClipPreserve()
|
||||
ResetClip()
|
||||
```
|
||||
|
||||
Note: As currently implemented, clipping isn't very fast, but it works.
|
||||
|
||||
## Helper Functions
|
||||
|
||||
Sometimes you just don't want to write these yourself.
|
||||
@ -165,7 +178,6 @@ SavePNG(path string, im image.Image) error
|
||||
If you need any of the features below, I recommend using `cairo` instead. Or
|
||||
even better, implement it and submit a pull request!
|
||||
|
||||
- Clipping Regions
|
||||
- Gradients / Patterns
|
||||
|
||||
## How Do it Do?
|
||||
|
119
context.go
119
context.go
@ -48,6 +48,7 @@ type Context struct {
|
||||
width int
|
||||
height int
|
||||
im *image.RGBA
|
||||
mask *image.Alpha
|
||||
color color.Color
|
||||
strokePath raster.Path
|
||||
fillPath raster.Path
|
||||
@ -338,10 +339,7 @@ func (dc *Context) joiner() raster.Joiner {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StrokePreserve strokes the current path with the current color, line width,
|
||||
// line cap, line join and dash settings. The path is preserved after this
|
||||
// operation.
|
||||
func (dc *Context) StrokePreserve() {
|
||||
func (dc *Context) stroke(painter raster.Painter) {
|
||||
path := dc.strokePath
|
||||
if len(dc.dashes) > 0 {
|
||||
path = dashed(path, dc.dashes)
|
||||
@ -350,14 +348,42 @@ func (dc *Context) StrokePreserve() {
|
||||
// that result in rendering issues
|
||||
path = rasterPath(flattenPath(path))
|
||||
}
|
||||
painter := raster.NewRGBAPainter(dc.im)
|
||||
painter.SetColor(dc.color)
|
||||
r := raster.NewRasterizer(dc.width, dc.height)
|
||||
r.UseNonZeroWinding = true
|
||||
r.AddStroke(path, fix(dc.lineWidth), dc.capper(), dc.joiner())
|
||||
r.Rasterize(painter)
|
||||
}
|
||||
|
||||
func (dc *Context) fill(painter raster.Painter) {
|
||||
path := dc.fillPath
|
||||
if dc.hasCurrent {
|
||||
path = make(raster.Path, len(dc.fillPath))
|
||||
copy(path, dc.fillPath)
|
||||
path.Add1(dc.start.Fixed())
|
||||
}
|
||||
r := raster.NewRasterizer(dc.width, dc.height)
|
||||
r.UseNonZeroWinding = dc.fillRule == FillRuleWinding
|
||||
r.AddPath(path)
|
||||
r.Rasterize(painter)
|
||||
}
|
||||
|
||||
// StrokePreserve strokes the current path with the current color, line width,
|
||||
// line cap, line join and dash settings. The path is preserved after this
|
||||
// operation.
|
||||
func (dc *Context) StrokePreserve() {
|
||||
if dc.mask == nil {
|
||||
painter := raster.NewRGBAPainter(dc.im)
|
||||
painter.SetColor(dc.color)
|
||||
dc.stroke(painter)
|
||||
} else {
|
||||
im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
|
||||
painter := raster.NewRGBAPainter(im)
|
||||
painter.SetColor(dc.color)
|
||||
dc.stroke(painter)
|
||||
draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
|
||||
}
|
||||
}
|
||||
|
||||
// Stroke strokes the current path with the current color, line width,
|
||||
// line cap, line join and dash settings. The path is cleared after this
|
||||
// operation.
|
||||
@ -369,18 +395,17 @@ func (dc *Context) Stroke() {
|
||||
// FillPreserve fills the current path with the current color. Open subpaths
|
||||
// are implicity closed. The path is preserved after this operation.
|
||||
func (dc *Context) FillPreserve() {
|
||||
path := dc.fillPath
|
||||
if dc.hasCurrent {
|
||||
path = make(raster.Path, len(dc.fillPath))
|
||||
copy(path, dc.fillPath)
|
||||
path.Add1(dc.start.Fixed())
|
||||
if dc.mask == nil {
|
||||
painter := raster.NewRGBAPainter(dc.im)
|
||||
painter.SetColor(dc.color)
|
||||
dc.fill(painter)
|
||||
} else {
|
||||
im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
|
||||
painter := raster.NewRGBAPainter(im)
|
||||
painter.SetColor(dc.color)
|
||||
dc.fill(painter)
|
||||
draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
|
||||
}
|
||||
painter := raster.NewRGBAPainter(dc.im)
|
||||
painter.SetColor(dc.color)
|
||||
r := raster.NewRasterizer(dc.width, dc.height)
|
||||
r.UseNonZeroWinding = dc.fillRule == FillRuleWinding
|
||||
r.AddPath(path)
|
||||
r.Rasterize(painter)
|
||||
}
|
||||
|
||||
// Fill fills the current path with the current color. Open subpaths
|
||||
@ -390,6 +415,37 @@ func (dc *Context) Fill() {
|
||||
dc.ClearPath()
|
||||
}
|
||||
|
||||
// ClipPreserve updates the clipping region by intersecting the current
|
||||
// clipping region with the current path as it would be filled by dc.Fill().
|
||||
// The path is preserved after this operation.
|
||||
func (dc *Context) ClipPreserve() {
|
||||
clip := image.NewAlpha(image.Rect(0, 0, dc.width, dc.height))
|
||||
painter := raster.NewAlphaOverPainter(clip)
|
||||
dc.fill(painter)
|
||||
if dc.mask == nil {
|
||||
dc.mask = clip
|
||||
} else {
|
||||
r := image.Rect(0, 0, dc.width, dc.height)
|
||||
mask := image.NewAlpha(r)
|
||||
draw.DrawMask(mask, r, clip, image.ZP, dc.mask, image.ZP, draw.Over)
|
||||
draw.DrawMask(mask, r, dc.mask, image.ZP, clip, image.ZP, draw.Over)
|
||||
dc.mask = mask
|
||||
}
|
||||
}
|
||||
|
||||
// Clip updates the clipping region by intersecting the current
|
||||
// clipping region with the current path as it would be filled by dc.Fill().
|
||||
// The path is cleared after this operation.
|
||||
func (dc *Context) Clip() {
|
||||
dc.ClipPreserve()
|
||||
dc.ClearPath()
|
||||
}
|
||||
|
||||
// ResetClip clears the clipping region.
|
||||
func (dc *Context) ResetClip() {
|
||||
dc.mask = nil
|
||||
}
|
||||
|
||||
// Convenient Drawing Functions
|
||||
|
||||
// Clear fills the entire image with the current color.
|
||||
@ -488,7 +544,11 @@ func (dc *Context) DrawImageAnchored(im image.Image, x, y int, ax, ay float64) {
|
||||
y -= int(ay * float64(s.Y))
|
||||
p := image.Pt(x, y)
|
||||
r := image.Rectangle{p, p.Add(s)}
|
||||
draw.Draw(dc.im, r, im, image.ZP, draw.Over)
|
||||
if dc.mask == nil {
|
||||
draw.Draw(dc.im, r, im, image.ZP, draw.Over)
|
||||
} else {
|
||||
draw.DrawMask(dc.im, r, im, image.ZP, dc.mask, p, draw.Over)
|
||||
}
|
||||
}
|
||||
|
||||
// Text Functions
|
||||
@ -507,6 +567,16 @@ func (dc *Context) LoadFontFace(path string, points float64) {
|
||||
}
|
||||
}
|
||||
|
||||
func (dc *Context) drawString(im *image.RGBA, s string, x, y float64) {
|
||||
d := &font.Drawer{
|
||||
Dst: im,
|
||||
Src: image.NewUniform(dc.color),
|
||||
Face: dc.fontFace,
|
||||
Dot: fixp(x, y),
|
||||
}
|
||||
d.DrawString(s)
|
||||
}
|
||||
|
||||
// DrawString draws the specified text at the specified point.
|
||||
// Currently, rotation and scaling transforms are not supported.
|
||||
func (dc *Context) DrawString(s string, x, y float64) {
|
||||
@ -521,13 +591,13 @@ func (dc *Context) DrawStringAnchored(s string, x, y, ax, ay float64) {
|
||||
x -= ax * w
|
||||
y += ay * h
|
||||
x, y = dc.TransformPoint(x, y)
|
||||
d := &font.Drawer{
|
||||
Dst: dc.im,
|
||||
Src: image.NewUniform(dc.color),
|
||||
Face: dc.fontFace,
|
||||
Dot: fixp(x, y),
|
||||
if dc.mask == nil {
|
||||
dc.drawString(dc.im, s, x, y)
|
||||
} else {
|
||||
im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
|
||||
dc.drawString(im, s, x, y)
|
||||
draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
|
||||
}
|
||||
d.DrawString(s)
|
||||
}
|
||||
|
||||
// DrawStringWrapped word-wraps the specified string to the given max width
|
||||
@ -655,6 +725,7 @@ func (dc *Context) Pop() {
|
||||
s := dc.stack
|
||||
x, s := s[len(s)-1], s[:len(s)-1]
|
||||
*dc = *x
|
||||
dc.mask = before.mask
|
||||
dc.strokePath = before.strokePath
|
||||
dc.fillPath = before.fillPath
|
||||
dc.start = before.start
|
||||
|
15
examples/clip.go
Normal file
15
examples/clip.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "github.com/fogleman/gg"
|
||||
|
||||
func main() {
|
||||
dc := gg.NewContext(1000, 1000)
|
||||
dc.DrawCircle(350, 500, 300)
|
||||
dc.Clip()
|
||||
dc.DrawCircle(650, 500, 300)
|
||||
dc.Clip()
|
||||
dc.DrawRectangle(0, 0, 1000, 1000)
|
||||
dc.SetRGB(0, 0, 0)
|
||||
dc.Fill()
|
||||
dc.SavePNG("out.png")
|
||||
}
|
Loading…
Reference in New Issue
Block a user