123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- // SPDX-License-Identifier: Unlicense OR MIT
- package paint
- import (
- "encoding/binary"
- "image"
- "image/color"
- "image/draw"
- "math"
- "gioui.org/f32"
- "gioui.org/internal/ops"
- "gioui.org/op"
- "gioui.org/op/clip"
- )
- // ImageFilter is the scaling filter for images.
- type ImageFilter byte
- const (
- // FilterLinear uses linear interpolation for scaling.
- FilterLinear ImageFilter = iota
- // FilterNearest uses nearest neighbor interpolation for scaling.
- FilterNearest
- )
- // ImageOp sets the brush to an image.
- type ImageOp struct {
- Filter ImageFilter
- uniform bool
- color color.NRGBA
- src *image.RGBA
- // handle is a key to uniquely identify this ImageOp
- // in a map of cached textures.
- handle interface{}
- }
- // ColorOp sets the brush to a constant color.
- type ColorOp struct {
- Color color.NRGBA
- }
- // LinearGradientOp sets the brush to a gradient starting at stop1 with color1 and
- // ending at stop2 with color2.
- type LinearGradientOp struct {
- Stop1 f32.Point
- Color1 color.NRGBA
- Stop2 f32.Point
- Color2 color.NRGBA
- }
- // PaintOp fills the current clip area with the current brush.
- type PaintOp struct {
- }
- // OpacityStack represents an opacity applied to all painting operations
- // until Pop is called.
- type OpacityStack struct {
- id ops.StackID
- macroID uint32
- ops *ops.Ops
- }
- // NewImageOp creates an ImageOp backed by src.
- //
- // NewImageOp assumes the backing image is immutable, and may cache a
- // copy of its contents in a GPU-friendly way. Create new ImageOps to
- // ensure that changes to an image is reflected in the display of
- // it.
- func NewImageOp(src image.Image) ImageOp {
- switch src := src.(type) {
- case *image.Uniform:
- col := color.NRGBAModel.Convert(src.C).(color.NRGBA)
- return ImageOp{
- uniform: true,
- color: col,
- }
- case *image.RGBA:
- return ImageOp{
- src: src,
- handle: new(int),
- }
- }
- sz := src.Bounds().Size()
- // Copy the image into a GPU friendly format.
- dst := image.NewRGBA(image.Rectangle{
- Max: sz,
- })
- draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)
- return ImageOp{
- src: dst,
- handle: new(int),
- }
- }
- func (i ImageOp) Size() image.Point {
- if i.src == nil {
- return image.Point{}
- }
- return i.src.Bounds().Size()
- }
- func (i ImageOp) Add(o *op.Ops) {
- if i.uniform {
- ColorOp{
- Color: i.color,
- }.Add(o)
- return
- } else if i.src == nil || i.src.Bounds().Empty() {
- return
- }
- data := ops.Write2(&o.Internal, ops.TypeImageLen, i.src, i.handle)
- data[0] = byte(ops.TypeImage)
- data[1] = byte(i.Filter)
- }
- func (c ColorOp) Add(o *op.Ops) {
- data := ops.Write(&o.Internal, ops.TypeColorLen)
- data[0] = byte(ops.TypeColor)
- data[1] = c.Color.R
- data[2] = c.Color.G
- data[3] = c.Color.B
- data[4] = c.Color.A
- }
- func (c LinearGradientOp) Add(o *op.Ops) {
- data := ops.Write(&o.Internal, ops.TypeLinearGradientLen)
- data[0] = byte(ops.TypeLinearGradient)
- bo := binary.LittleEndian
- bo.PutUint32(data[1:], math.Float32bits(c.Stop1.X))
- bo.PutUint32(data[5:], math.Float32bits(c.Stop1.Y))
- bo.PutUint32(data[9:], math.Float32bits(c.Stop2.X))
- bo.PutUint32(data[13:], math.Float32bits(c.Stop2.Y))
- data[17+0] = c.Color1.R
- data[17+1] = c.Color1.G
- data[17+2] = c.Color1.B
- data[17+3] = c.Color1.A
- data[21+0] = c.Color2.R
- data[21+1] = c.Color2.G
- data[21+2] = c.Color2.B
- data[21+3] = c.Color2.A
- }
- func (d PaintOp) Add(o *op.Ops) {
- data := ops.Write(&o.Internal, ops.TypePaintLen)
- data[0] = byte(ops.TypePaint)
- }
- // FillShape fills the clip shape with a color.
- func FillShape(ops *op.Ops, c color.NRGBA, shape clip.Op) {
- defer shape.Push(ops).Pop()
- Fill(ops, c)
- }
- // Fill paints an infinitely large plane with the provided color. It
- // is intended to be used with a clip.Op already in place to limit
- // the painted area. Use FillShape unless you need to paint several
- // times within the same clip.Op.
- func Fill(ops *op.Ops, c color.NRGBA) {
- ColorOp{Color: c}.Add(ops)
- PaintOp{}.Add(ops)
- }
- // PushOpacity creates a drawing layer with an opacity in the range [0;1].
- // The layer includes every subsequent drawing operation until [OpacityStack.Pop]
- // is called.
- //
- // The layer is drawn in two steps. First, the layer operations are
- // drawn to a separate image. Then, the image is blended on top of
- // the frame, with the opacity used as the blending factor.
- func PushOpacity(o *op.Ops, opacity float32) OpacityStack {
- if opacity > 1 {
- opacity = 1
- }
- if opacity < 0 {
- opacity = 0
- }
- id, macroID := ops.PushOp(&o.Internal, ops.OpacityStack)
- data := ops.Write(&o.Internal, ops.TypePushOpacityLen)
- bo := binary.LittleEndian
- data[0] = byte(ops.TypePushOpacity)
- bo.PutUint32(data[1:], math.Float32bits(opacity))
- return OpacityStack{ops: &o.Internal, id: id, macroID: macroID}
- }
- func (t OpacityStack) Pop() {
- ops.PopOp(t.ops, ops.OpacityStack, t.id, t.macroID)
- data := ops.Write(t.ops, ops.TypePopOpacityLen)
- data[0] = byte(ops.TypePopOpacity)
- }
|