File: bgra.go

package info (click to toggle)
golang-github-dkolbly-wl 0.0~git20180220.b06f57e-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 336 kB
  • sloc: makefile: 3
file content (180 lines) | stat: -rw-r--r-- 4,123 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package ui

import (
	"github.com/golang/freetype/raster"
	"image"
	"image/color"
	"image/draw"
)

// BGRA is like RGBA but in wayland's byte order
type BGRA struct {
	Pix    []uint8
	Stride int
	Rect   image.Rectangle
}

// NewBGRA returns a new BGRA image with the given bounds.
func NewBGRA(r image.Rectangle) *BGRA {
	w, h := r.Dx(), r.Dy()
	buf := make([]uint8, 4*w*h)
	return &BGRA{buf, 4 * w, r}
}

func NewBGRAWithData(r image.Rectangle, data []uint8) *BGRA {
	w, h := r.Dx(), r.Dy()
	if len(data) < 4*w*h {
		panic("not enough data supplied")
	}
	return &BGRA{data, 4 * w, r}
}

func (p *BGRA) ColorModel() color.Model { return color.RGBAModel }

func (p *BGRA) Bounds() image.Rectangle { return p.Rect }

func (p *BGRA) At(x, y int) color.Color {
	return p.RGBAAt(x, y)
}

func (p *BGRA) RGBAAt(x, y int) color.RGBA {
	if !(image.Point{x, y}.In(p.Rect)) {
		return color.RGBA{}
	}
	i := p.PixOffset(x, y)
	return color.RGBA{p.Pix[i+2], p.Pix[i+1], p.Pix[i+0], p.Pix[i+3]}
}

// PixOffset returns the index of the first element of Pix that corresponds to
// the pixel at (x, y).
func (p *BGRA) PixOffset(x, y int) int {
	return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
}

func (p *BGRA) Set(x, y int, c color.Color) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	c1 := color.RGBAModel.Convert(c).(color.RGBA)
	p.Pix[i+0] = c1.B
	p.Pix[i+1] = c1.G
	p.Pix[i+2] = c1.R
	p.Pix[i+3] = c1.A
}

func (p *BGRA) SetRGBA(x, y int, c color.RGBA) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	p.Pix[i+0] = c.B
	p.Pix[i+1] = c.G
	p.Pix[i+2] = c.R
	p.Pix[i+3] = c.A
}

// SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image.
func (p *BGRA) SubImage(r image.Rectangle) image.Image {
	r = r.Intersect(p.Rect)
	// If r1 and r2 are Rectangles, r1.Intersect(r2) is not
	// guaranteed to be inside either r1 or r2 if the intersection
	// is empty. Without explicitly checking for this, the Pix[i:]
	// expression below can panic.
	if r.Empty() {
		return &BGRA{}
	}
	i := p.PixOffset(r.Min.X, r.Min.Y)
	return &BGRA{
		Pix:    p.Pix[i:],
		Stride: p.Stride,
		Rect:   r,
	}
}

// Opaque scans the entire image and reports whether it is fully opaque.
func (p *BGRA) Opaque() bool {
	if p.Rect.Empty() {
		return true
	}
	i0 := 0
	i1 := p.Rect.Dx() * 4
	for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
		for i := i0; i < i1; i += 4 {
			if p.Pix[i+3] != 0xff {
				return false
			}
		}
		i0 += p.Stride
		i1 += p.Stride
	}
	return true
}

// a BGRAPainter is used to interface with the freetype renderer and
// raster library
type BGRAPainter struct {
	image      *BGRA
	r, g, b, a uint32
	op         draw.Op
}

func (p *BGRA) Painter() *BGRAPainter {
	return &BGRAPainter{
		image: p,
	}
}

func (p *BGRAPainter) SetColor(c color.Color) {
	p.r, p.g, p.b, p.a = c.RGBA()
}

func (p *BGRAPainter) Paint(spans []raster.Span, done bool) {
	b := p.image.Rect
	pix := p.image.Pix
	for _, s := range spans {
		if s.Y < b.Min.Y {
			continue
		}
		if s.Y >= b.Max.Y {
			// since spans are ordered, we know we're done
			// at this point
			return
		}
		if s.X0 < b.Min.X {
			s.X0 = b.Min.X
		}
		if s.X1 > b.Max.X {
			s.X1 = b.Max.X
		}
		if s.X0 >= s.X1 {
			continue
		}
		// This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go.
		ma := s.Alpha
		const m = 1<<16 - 1
		i0 := (s.Y-b.Min.Y)*p.image.Stride + (s.X0-b.Min.X)*4
		i1 := i0 + (s.X1-s.X0)*4
		if p.op == draw.Over {
			for i := i0; i < i1; i += 4 {
				dr := uint32(pix[i+0])
				dg := uint32(pix[i+1])
				db := uint32(pix[i+2])
				da := uint32(pix[i+3])
				a := (m - (p.a * ma / m)) * 0x101
				pix[i+0] = uint8((dr*a + p.r*ma) / m >> 8)
				pix[i+1] = uint8((dg*a + p.g*ma) / m >> 8)
				pix[i+2] = uint8((db*a + p.b*ma) / m >> 8)
				pix[i+3] = uint8((da*a + p.a*ma) / m >> 8)
			}
		} else {
			for i := i0; i < i1; i += 4 {
				pix[i+0] = uint8(p.r * ma / m >> 8)
				pix[i+1] = uint8(p.g * ma / m >> 8)
				pix[i+2] = uint8(p.b * ma / m >> 8)
				pix[i+3] = uint8(p.a * ma / m >> 8)
			}
		}
	}
}