paint.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package paint
  3. import (
  4. "encoding/binary"
  5. "image"
  6. "image/color"
  7. "image/draw"
  8. "math"
  9. "gioui.org/f32"
  10. "gioui.org/internal/ops"
  11. "gioui.org/op"
  12. "gioui.org/op/clip"
  13. )
  14. // ImageFilter is the scaling filter for images.
  15. type ImageFilter byte
  16. const (
  17. // FilterLinear uses linear interpolation for scaling.
  18. FilterLinear ImageFilter = iota
  19. // FilterNearest uses nearest neighbor interpolation for scaling.
  20. FilterNearest
  21. )
  22. // ImageOp sets the brush to an image.
  23. type ImageOp struct {
  24. Filter ImageFilter
  25. uniform bool
  26. color color.NRGBA
  27. src *image.RGBA
  28. // handle is a key to uniquely identify this ImageOp
  29. // in a map of cached textures.
  30. handle interface{}
  31. }
  32. // ColorOp sets the brush to a constant color.
  33. type ColorOp struct {
  34. Color color.NRGBA
  35. }
  36. // LinearGradientOp sets the brush to a gradient starting at stop1 with color1 and
  37. // ending at stop2 with color2.
  38. type LinearGradientOp struct {
  39. Stop1 f32.Point
  40. Color1 color.NRGBA
  41. Stop2 f32.Point
  42. Color2 color.NRGBA
  43. }
  44. // PaintOp fills the current clip area with the current brush.
  45. type PaintOp struct {
  46. }
  47. // OpacityStack represents an opacity applied to all painting operations
  48. // until Pop is called.
  49. type OpacityStack struct {
  50. id ops.StackID
  51. macroID uint32
  52. ops *ops.Ops
  53. }
  54. // NewImageOp creates an ImageOp backed by src.
  55. //
  56. // NewImageOp assumes the backing image is immutable, and may cache a
  57. // copy of its contents in a GPU-friendly way. Create new ImageOps to
  58. // ensure that changes to an image is reflected in the display of
  59. // it.
  60. func NewImageOp(src image.Image) ImageOp {
  61. switch src := src.(type) {
  62. case *image.Uniform:
  63. col := color.NRGBAModel.Convert(src.C).(color.NRGBA)
  64. return ImageOp{
  65. uniform: true,
  66. color: col,
  67. }
  68. case *image.RGBA:
  69. return ImageOp{
  70. src: src,
  71. handle: new(int),
  72. }
  73. }
  74. sz := src.Bounds().Size()
  75. // Copy the image into a GPU friendly format.
  76. dst := image.NewRGBA(image.Rectangle{
  77. Max: sz,
  78. })
  79. draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)
  80. return ImageOp{
  81. src: dst,
  82. handle: new(int),
  83. }
  84. }
  85. func (i ImageOp) Size() image.Point {
  86. if i.src == nil {
  87. return image.Point{}
  88. }
  89. return i.src.Bounds().Size()
  90. }
  91. func (i ImageOp) Add(o *op.Ops) {
  92. if i.uniform {
  93. ColorOp{
  94. Color: i.color,
  95. }.Add(o)
  96. return
  97. } else if i.src == nil || i.src.Bounds().Empty() {
  98. return
  99. }
  100. data := ops.Write2(&o.Internal, ops.TypeImageLen, i.src, i.handle)
  101. data[0] = byte(ops.TypeImage)
  102. data[1] = byte(i.Filter)
  103. }
  104. func (c ColorOp) Add(o *op.Ops) {
  105. data := ops.Write(&o.Internal, ops.TypeColorLen)
  106. data[0] = byte(ops.TypeColor)
  107. data[1] = c.Color.R
  108. data[2] = c.Color.G
  109. data[3] = c.Color.B
  110. data[4] = c.Color.A
  111. }
  112. func (c LinearGradientOp) Add(o *op.Ops) {
  113. data := ops.Write(&o.Internal, ops.TypeLinearGradientLen)
  114. data[0] = byte(ops.TypeLinearGradient)
  115. bo := binary.LittleEndian
  116. bo.PutUint32(data[1:], math.Float32bits(c.Stop1.X))
  117. bo.PutUint32(data[5:], math.Float32bits(c.Stop1.Y))
  118. bo.PutUint32(data[9:], math.Float32bits(c.Stop2.X))
  119. bo.PutUint32(data[13:], math.Float32bits(c.Stop2.Y))
  120. data[17+0] = c.Color1.R
  121. data[17+1] = c.Color1.G
  122. data[17+2] = c.Color1.B
  123. data[17+3] = c.Color1.A
  124. data[21+0] = c.Color2.R
  125. data[21+1] = c.Color2.G
  126. data[21+2] = c.Color2.B
  127. data[21+3] = c.Color2.A
  128. }
  129. func (d PaintOp) Add(o *op.Ops) {
  130. data := ops.Write(&o.Internal, ops.TypePaintLen)
  131. data[0] = byte(ops.TypePaint)
  132. }
  133. // FillShape fills the clip shape with a color.
  134. func FillShape(ops *op.Ops, c color.NRGBA, shape clip.Op) {
  135. defer shape.Push(ops).Pop()
  136. Fill(ops, c)
  137. }
  138. // Fill paints an infinitely large plane with the provided color. It
  139. // is intended to be used with a clip.Op already in place to limit
  140. // the painted area. Use FillShape unless you need to paint several
  141. // times within the same clip.Op.
  142. func Fill(ops *op.Ops, c color.NRGBA) {
  143. ColorOp{Color: c}.Add(ops)
  144. PaintOp{}.Add(ops)
  145. }
  146. // PushOpacity creates a drawing layer with an opacity in the range [0;1].
  147. // The layer includes every subsequent drawing operation until [OpacityStack.Pop]
  148. // is called.
  149. //
  150. // The layer is drawn in two steps. First, the layer operations are
  151. // drawn to a separate image. Then, the image is blended on top of
  152. // the frame, with the opacity used as the blending factor.
  153. func PushOpacity(o *op.Ops, opacity float32) OpacityStack {
  154. if opacity > 1 {
  155. opacity = 1
  156. }
  157. if opacity < 0 {
  158. opacity = 0
  159. }
  160. id, macroID := ops.PushOp(&o.Internal, ops.OpacityStack)
  161. data := ops.Write(&o.Internal, ops.TypePushOpacityLen)
  162. bo := binary.LittleEndian
  163. data[0] = byte(ops.TypePushOpacity)
  164. bo.PutUint32(data[1:], math.Float32bits(opacity))
  165. return OpacityStack{ops: &o.Internal, id: id, macroID: macroID}
  166. }
  167. func (t OpacityStack) Pop() {
  168. ops.PopOp(t.ops, ops.OpacityStack, t.id, t.macroID)
  169. data := ops.Write(t.ops, ops.TypePopOpacityLen)
  170. data[0] = byte(ops.TypePopOpacity)
  171. }