8 Commits

9 changed files with 125 additions and 31 deletions

2
go.mod
View File

@@ -3,7 +3,7 @@ module git.tebibyte.media/tomo/backend
go 1.20
require (
git.tebibyte.media/tomo/tomo v0.42.0
git.tebibyte.media/tomo/tomo v0.45.0
git.tebibyte.media/tomo/typeset v0.7.1
git.tebibyte.media/tomo/xgbkb v1.0.1
github.com/jezek/xgb v1.1.1

4
go.sum
View File

@@ -1,6 +1,6 @@
git.tebibyte.media/sashakoshka/xgbkb v1.0.0/go.mod h1:pNcE6TRO93vHd6q42SdwLSTTj25L0Yzggz7yLe0JV6Q=
git.tebibyte.media/tomo/tomo v0.42.0 h1:yaEUnURYrvBdMdcajrFhpd83TNzyQyBB+jOxvIyQTkU=
git.tebibyte.media/tomo/tomo v0.42.0/go.mod h1:WrtilgKB1y8O2Yu7X4mYcRiqOlPR8NuUnoA/ynkQWrs=
git.tebibyte.media/tomo/tomo v0.45.0 h1:fQH0WIPidW275hOq9dE6R7p064xG1RGx2QU68Avlr84=
git.tebibyte.media/tomo/tomo v0.45.0/go.mod h1:WrtilgKB1y8O2Yu7X4mYcRiqOlPR8NuUnoA/ynkQWrs=
git.tebibyte.media/tomo/typeset v0.7.1 h1:aZrsHwCG5ZB4f5CruRFsxLv5ezJUCFUFsQJJso2sXQ8=
git.tebibyte.media/tomo/typeset v0.7.1/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g=
git.tebibyte.media/tomo/xgbkb v1.0.1 h1:b3HDUopjdQp1MZrb5Vpil4bOtk3NnNXtfQW27Blw2kE=

View File

@@ -423,6 +423,13 @@ func (this *box) Draw (can canvas.Canvas) {
// centered texture
if textureMode == tomo.TextureModeCenter && texture != nil {
this.centeredTexture(can, texture)
}
}
func (this *box) centeredTexture (can canvas.Canvas, texture canvas.Texture) {
pen := can.Pen()
bounds := this.Bounds()
textureBounds := texture.Bounds()
textureOrigin :=
bounds.Min.
@@ -437,7 +444,6 @@ func (this *box) Draw (can canvas.Canvas) {
pen.Texture(texture)
pen.Rectangle(textureBounds.Sub(textureBounds.Min).Add(textureOrigin))
}
}
func (this *box) drawBorders (can canvas.Canvas) {
if can == nil { return }
@@ -602,7 +608,8 @@ func (this *box) invalidateMinimum () {
}
func (this *box) recursiveReApply () {
if this.getHierarchy() == nil { return }
hierarchy := this.getHierarchy()
if hierarchy == nil { return }
// re-apply styling, icons *if needed*
@@ -614,7 +621,7 @@ func (this *box) recursiveReApply () {
// information about the boxes they're linked to (like all rules
// with a matching role).
this.lastStyleNonce = hierarchyStyleNonce
this.styleApplicator = this.getHierarchy().newStyleApplicator()
this.styleApplicator = hierarchy.newStyleApplicator()
this.invalidateStyle()
this.on.styleChange.Broadcast()
}

View File

@@ -7,6 +7,7 @@ type styleApplicator struct {
style *style.Style
role tomo.Role
rules []style.Rule
currentSet style.AttrSet
}
func (this *styleApplicator) apply (box anyBox) {
@@ -41,7 +42,18 @@ func (this *styleApplicator) apply (box anyBox) {
}
}
// reset an attribute if it is no longer specified
if this.currentSet != nil {
for kind := range this.currentSet {
_, exists := attrs[kind]
if !exists {
box.unsetAttr(kind, false)
}
}
}
// apply that list of attributes
this.currentSet = attrs
for _, attr := range attrs {
box.setAttr(attr, false)
}

View File

@@ -493,9 +493,9 @@ func (this *textBox) scrollToDot () {
func (this *textBox) handleFaceChange () {
hierarchy := this.getHierarchy()
if hierarchy != nil { return }
if hierarchy == nil { return }
faceSet := hierarchy.getFaceSet()
if faceSet != nil { return }
if faceSet == nil { return }
face := faceSet.Face(tomo.Face(this.attrFace.Value()))
this.face.Set(face, face)
@@ -503,3 +503,23 @@ func (this *textBox) handleFaceChange () {
this.invalidateMinimum()
this.invalidateLayout()
}
func (this *textBox) recursiveReApply () {
this.box.recursiveReApply()
hierarchy := this.getHierarchy()
if hierarchy == nil { return }
previousFace := this.face.Value()
if previousFace == nil {
faceSet := hierarchy.getFaceSet()
if faceSet == nil { return }
face := faceSet.Face(tomo.Face(this.attrFace.Value()))
if face != previousFace {
this.face.Set(face, face)
this.drawer.SetFace(face)
this.invalidateMinimum()
this.invalidateLayout()
}
}
}

View File

@@ -4,7 +4,9 @@ import "image"
import "errors"
import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/xgbkb"
import "git.tebibyte.media/tomo/tomo/data"
import "git.tebibyte.media/tomo/tomo/canvas"
import "git.tebibyte.media/tomo/backend/style"
import "git.tebibyte.media/tomo/backend/x/canvas"
import "git.tebibyte.media/tomo/backend/internal/system"
@@ -18,6 +20,9 @@ type Backend struct {
x *xgbutil.XUtil
system *system.System
style *style.Style
iconSet style.IconSet
doChannel chan func()
windows map[xproto.Window] *window
open bool
@@ -126,12 +131,33 @@ func (this *Backend) NewCanvas (bounds image.Rectangle) canvas.CanvasCloser {
return xcanvas.NewCanvas(this.x, bounds)
}
func (this *Backend) SetStyle (style *tomo.Style) {
func (this *Backend) ColorRGBA (id tomo.Color) (r, g, b, a uint32) {
if col, ok := this.style.Colors[id]; ok {
return col.RGBA()
}
return 0xFFFF, 0, 0xFFFF, 0xFFFF // punish bad styles
}
func (this *Backend) IconTexture (id tomo.Icon, size tomo.IconSize) canvas.Texture {
return this.iconSet.Icon(id, size)
}
func (this *Backend) MimeIconTexture (mime data.Mime, size tomo.IconSize) canvas.Texture {
return this.iconSet.MimeIcon(mime, size)
}
func (this *Backend) SetStyle (style *style.Style) {
this.style = style
this.system.SetStyle(style)
}
func (this *Backend) SetIconSet (icons tomo.IconSet) {
this.system.SetIconSet(icons)
func (this *Backend) SetIconSet (iconSet style.IconSet) {
this.iconSet = iconSet
this.system.SetIconSet(iconSet)
}
func (this *Backend) SetFaceSet (faceSet style.FaceSet) {
this.system.SetFaceSet(faceSet)
}
func (this *Backend) assert () {

View File

@@ -132,3 +132,32 @@ func convertColor (c color.Color) xgraphics.BGRA {
A: uint8(a >> 8),
}
}
// For some reason, xgraphics.BGRA does not specify whether or not it uses
// premultiplied alpha, and information regarding this is contradictory.
// Basically:
// - BGRAModel just takes the result of c.RGBA and bit shifts it, without
// un-doing the aplha premultiplication that is required by Color.RGBA,
// suggesting that xgraphics.BGRA stores alpha-premultiplied color.
// - xgraphics.BlendBGRA lerps between dest and src using only the alpha of
// src (temporarily converting the colors to fucking floats for some reason)
// which seems to suggest that xgraphics.BGRA *does not* store alpha-
// premultiplied color.
// There is no issues page on xgbutil so we may never get an answer to this
// question. However, in this package we just use xgraphics.BGRA to store alpha-
// premultiplied color anyway because its way faster, and I would sooner eat
// spaghetti with a spoon than convert to and from float64 to blend pixels.
func blendPremultipliedBGRA (dst, src xgraphics.BGRA) xgraphics.BGRA {
// https://en.wikipedia.org/wiki/Alpha_compositing
return xgraphics.BGRA {
B: blendPremultipliedChannel(dst.B, src.B, src.A),
G: blendPremultipliedChannel(dst.G, src.G, src.A),
R: blendPremultipliedChannel(dst.R, src.R, src.A),
A: blendPremultipliedChannel(dst.A, src.A, src.A),
}
}
func blendPremultipliedChannel (dst, src, a uint8) uint8 {
dst16, src16, a16 := uint16(dst), uint16(src), uint16(a)
return uint8(src16 + ((dst16 * (255 - a16)) >> 8))
}

View File

@@ -46,7 +46,7 @@ func (this *pen) textureRectangleTransparent (bounds image.Rectangle) {
srcPos := pos.Add(offset)
dstIndex := this.image.PixOffset(pos.X, pos.Y)
srcIndex := this.texture.PixOffset(srcPos.X, srcPos.Y)
pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
pixel := blendPremultipliedBGRA(xgraphics.BGRA {
B: dst[dstIndex + 0],
G: dst[dstIndex + 1],
R: dst[dstIndex + 2],
@@ -93,7 +93,7 @@ func (this *pen) fillRectangleTransparent (c xgraphics.BGRA, bounds image.Rectan
for pos.Y = bounds.Min.Y; pos.Y < bounds.Max.Y; pos.Y ++ {
for pos.X = bounds.Min.X; pos.X < bounds.Max.X; pos.X ++ {
index := this.image.PixOffset(pos.X, pos.Y)
pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
pixel := blendPremultipliedBGRA(xgraphics.BGRA {
B: this.image.Pix[index + 0],
G: this.image.Pix[index + 1],
R: this.image.Pix[index + 2],
@@ -256,7 +256,7 @@ func (context *fillingContext) fillPolygonHotTransparent () {
// fill pixels in between
for x := left; x < right; x ++ {
index := context.image.PixOffset(x, context.y)
pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
pixel := blendPremultipliedBGRA(xgraphics.BGRA {
B: context.image.Pix[index + 0],
G: context.image.Pix[index + 1],
R: context.image.Pix[index + 2],

View File

@@ -32,7 +32,7 @@ func (context plottingContext) plot (center image.Point) {
for y := square.Min.Y; y < square.Max.Y; y ++ {
for x := square.Min.X; x < square.Max.X; x ++ {
index := context.image.PixOffset(x, y)
pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
pixel := blendPremultipliedBGRA(xgraphics.BGRA {
B: context.image.Pix[index + 0],
G: context.image.Pix[index + 1],
R: context.image.Pix[index + 2],