mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2024-11-01 12:04:15 +00:00
134 lines
2.8 KiB
Go
134 lines
2.8 KiB
Go
package roundimage
|
|
|
|
import (
|
|
"math"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/gts/httputil"
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
|
"github.com/gotk3/gotk3/cairo"
|
|
"github.com/gotk3/gotk3/gdk"
|
|
"github.com/gotk3/gotk3/gtk"
|
|
)
|
|
|
|
const (
|
|
pi = math.Pi
|
|
circle = 2 * math.Pi
|
|
)
|
|
|
|
type RadiusSetter interface {
|
|
SetRadius(float64)
|
|
}
|
|
|
|
type Connector interface {
|
|
ConnectHandlers(connector primitives.Connector)
|
|
}
|
|
|
|
type Imager interface {
|
|
gtk.IWidget
|
|
RadiusSetter
|
|
|
|
// Embed setters.
|
|
httputil.ImageContainerSizer
|
|
|
|
GetPixbuf() *gdk.Pixbuf
|
|
GetAnimation() *gdk.PixbufAnimation
|
|
|
|
GetImage() *gtk.Image
|
|
}
|
|
|
|
type Image struct {
|
|
*gtk.Image
|
|
Radius float64
|
|
}
|
|
|
|
var _ Imager = (*Image)(nil)
|
|
|
|
// NewImage creates a new round image. If radius is 0, then it will be half the
|
|
// dimensions. If the radius is less than 0, then nothing is rounded.
|
|
func NewImage(radius float64) (*Image, error) {
|
|
i, err := gtk.ImageNew()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
image := &Image{Image: i, Radius: radius}
|
|
|
|
// Connect to the draw callback and clip the context.
|
|
i.Connect("draw", image.drawer)
|
|
|
|
return image, nil
|
|
}
|
|
|
|
func (i *Image) GetImage() *gtk.Image {
|
|
return i.Image
|
|
}
|
|
|
|
func (i *Image) SetRadius(r float64) {
|
|
i.Radius = r
|
|
}
|
|
|
|
func (i *Image) drawer(widget gtk.IWidget, cc *cairo.Context) bool {
|
|
var w = float64(i.GetAllocatedWidth())
|
|
var h = float64(i.GetAllocatedHeight())
|
|
|
|
var min = w
|
|
// Use the smallest side for radius calculation.
|
|
if h < w {
|
|
min = h
|
|
}
|
|
|
|
// Copy the variables in case we need to change them.
|
|
var r = i.Radius
|
|
|
|
switch {
|
|
// If radius is less than 0, then don't round.
|
|
case r < 0:
|
|
return false
|
|
|
|
// If radius is 0, then we have to calculate our own radius.:This only
|
|
// works if the image is a square.
|
|
case r == 0:
|
|
// Calculate the radius by dividing a side by 2.
|
|
r = (min / 2)
|
|
|
|
// Draw an arc from 0deg to 360deg.
|
|
cc.Arc(w/2, h/2, r, 0, circle)
|
|
|
|
// We have to do this so the arc paint doesn't leave back a black
|
|
// background instead of the usual alpha.
|
|
cc.SetSourceRGBA(255, 255, 255, 0)
|
|
|
|
// Clip the image with the arc we drew.
|
|
cc.Clip()
|
|
|
|
// If radius is more than 0, then we have to calculate the radius from
|
|
// the edges.
|
|
case r > 0:
|
|
// StackOverflow is godly.
|
|
// https://stackoverflow.com/a/6959843.
|
|
|
|
// Radius should be largest a single side divided by 2.
|
|
if max := min / 2; r > max {
|
|
r = max
|
|
}
|
|
|
|
// Draw 4 arcs at 4 corners.
|
|
cc.Arc(0+r, 0+r, r, 2*(pi/2), 3*(pi/2)) // top left
|
|
cc.Arc(w-r, 0+r, r, 3*(pi/2), 4*(pi/2)) // top right
|
|
cc.Arc(w-r, h-r, r, 0*(pi/2), 1*(pi/2)) // bottom right
|
|
cc.Arc(0+r, h-r, r, 1*(pi/2), 2*(pi/2)) // bottom left
|
|
|
|
// Close the created path.
|
|
cc.ClosePath()
|
|
cc.SetSourceRGBA(255, 255, 255, 0)
|
|
|
|
// Clip the image with the arc we drew.
|
|
cc.Clip()
|
|
}
|
|
|
|
// Paint the changes.
|
|
cc.Paint()
|
|
|
|
return false
|
|
}
|