diff --git a/theme/assets/wintergreen-icons-small.png b/theme/assets/wintergreen-icons-small.png index cd65704..a6ebc93 100644 Binary files a/theme/assets/wintergreen-icons-small.png and b/theme/assets/wintergreen-icons-small.png differ diff --git a/theme/default.go b/theme/default.go index 304b77e..8c25519 100644 --- a/theme/default.go +++ b/theme/default.go @@ -16,6 +16,9 @@ import "git.tebibyte.media/sashakoshka/tomo/artist/patterns" var defaultAtlasBytes []byte var defaultAtlas canvas.Canvas var defaultTextures [14][9]artist.Pattern +//go:embed assets/wintergreen-icons-small.png +var defaultIconsSmallAtlasBytes []byte +var defaultIconsSmall [640]binaryIcon func atlasCell (col, row int, border artist.Inset) { bounds := image.Rect(0, 0, 16, 16).Add(image.Pt(col, row).Mul(16)) @@ -31,6 +34,51 @@ func atlasCol (col int, border artist.Inset) { } } +type binaryIcon struct { + data []bool + stride int +} + +func (icon binaryIcon) Draw (destination canvas.Canvas, color color.RGBA, at image.Point) { + bounds := icon.Bounds().Add(at).Intersect(destination.Bounds()) + point := image.Point { } + data, stride := destination.Buffer() + + for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ { + for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ { + srcPoint := point.Sub(at) + srcIndex := srcPoint.X + srcPoint.Y * icon.stride + dstIndex := point.X + point.Y * stride + if icon.data[srcIndex] { + data[dstIndex] = color + } + }} +} + +func (icon binaryIcon) Bounds () image.Rectangle { + return image.Rect(0, 0, icon.stride, len(icon.data) / icon.stride) +} + +func binaryIconFrom (source image.Image, clip image.Rectangle) (icon binaryIcon) { + bounds := source.Bounds().Intersect(clip) + if bounds.Empty() { return } + + icon.stride = bounds.Dx() + icon.data = make([]bool, bounds.Dx() * bounds.Dy()) + + point := image.Point { } + dstIndex := 0 + for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ { + for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ { + r, g, b, a := source.At(point.X, point.Y).RGBA() + if a > 0x8000 && (r + g + b) / 3 < 0x8000 { + icon.data[dstIndex] = true + } + dstIndex ++ + }} + return +} + func init () { defaultAtlasImage, _, _ := image.Decode(bytes.NewReader(defaultAtlasBytes)) defaultAtlas = canvas.FromImage(defaultAtlasImage) @@ -64,6 +112,20 @@ func init () { atlasCol(11, artist.Inset { 3, 3, 5, 3 }) // PatternRaised: fun.sharpKey atlasCol(12, artist.Inset { 3, 3, 4, 3 }) + + // set up small icons + defaultIconsSmallAtlasImage, _, _ := image.Decode ( + bytes.NewReader(defaultIconsSmallAtlasBytes)) + bounds := defaultIconsSmallAtlasImage.Bounds() + point := image.Point { } + iconIndex := 0 + for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y += 16 { + for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X += 16 { + defaultIconsSmall[iconIndex] = binaryIconFrom ( + defaultIconsSmallAtlasImage, + image.Rect(0, 0, 16, 16).Add(point)) + iconIndex ++ + }} } // Default is the default theme. @@ -84,9 +146,17 @@ func (Default) FontFace (style FontStyle, size FontSize, c Case) font.Face { } // Icon returns an icon from the default set corresponding to the given name. -func (Default) Icon (string, IconSize, Case) artist.Icon { - // TODO - return nil +func (Default) Icon (id Icon, size IconSize, c Case) artist.Icon { + if size == IconSizeLarge { + // TODO + return nil + } else { + if id < 0 || int(id) >= len(defaultIconsSmall) { + return nil + } else { + return defaultIconsSmall[id] + } + } } // MimeIcon returns an icon from the default set corresponding to the given mime. diff --git a/theme/theme.go b/theme/theme.go index f3341ab..2db0276 100644 --- a/theme/theme.go +++ b/theme/theme.go @@ -78,10 +78,11 @@ type Icon int; const ( IconLibraries IconDownloads IconRepositories - IconSettings + IconSettings) +const ( // Object icons - IconFile = iota + 0x80 + IconFile Icon = iota + 0x80 IconDirectory IconPopulatedDirectory @@ -127,8 +128,9 @@ type Icon int; const ( IconParallelPort IconSerialPort IconPS2Port - IconMonitorPort + IconMonitorPort) +const ( // Action icons IconOpen = iota + 0x100 IconSave @@ -166,17 +168,18 @@ type Icon int; const ( IconHistory IconYes - IconNo + IconNo) +const ( // Status icons IconInformation = iota + 0x180 IconQuestion IconWarning - IconError + IconError) +const ( // Tool icons - -) + ) // Hints specifies rendering hints for a particular pattern. Elements can take // these into account in order to gain extra performance. @@ -197,7 +200,7 @@ type Theme interface { FontFace (FontStyle, FontSize, Case) font.Face // Icon returns an appropriate icon given an icon name, size, and case. - Icon (string, IconSize, Case) artist.Icon + Icon (Icon, IconSize, Case) artist.Icon // Icon returns an appropriate icon given a file mime type, size, and, // case. diff --git a/theme/wrapped.go b/theme/wrapped.go index a2c8706..f535dcb 100644 --- a/theme/wrapped.go +++ b/theme/wrapped.go @@ -21,9 +21,9 @@ func (wrapped Wrapped) FontFace (style FontStyle, size FontSize) font.Face { } // Icon returns an appropriate icon given an icon name. -func (wrapped Wrapped) Icon (name string, size IconSize) artist.Icon { +func (wrapped Wrapped) Icon (id Icon, size IconSize) artist.Icon { real := wrapped.ensure() - return real.Icon(name, size, wrapped.Case) + return real.Icon(id, size, wrapped.Case) } // MimeIcon returns an appropriate icon given file mime type.