finished sixel support

This commit is contained in:
Liam Galvin 2018-10-28 15:26:12 +00:00
parent 20c3c0cb14
commit 78d67302b4
6 changed files with 67 additions and 89 deletions

View File

@ -10,9 +10,14 @@ The project is experimental at the moment, so you probably won't want to rely on
Ensure you have your latest graphics card drivers installed before use. Ensure you have your latest graphics card drivers installed before use.
Sixels are now supported.
![Example sixel](sixel.png)
## Aims ## Aims
- Full unicode support - Unicode support
- OpenGL rendering - OpenGL rendering
- Full customisation options - Full customisation options
- True colour support - True colour support
@ -23,6 +28,7 @@ Ensure you have your latest graphics card drivers installed before use.
- Resize logic that wraps/unwraps lines _correctly_ - Resize logic that wraps/unwraps lines _correctly_
- Bullshit graphical effects - Bullshit graphical effects
- Multi platform support - Multi platform support
- Sixel support
## What isn't supported? ## What isn't supported?
@ -32,13 +38,6 @@ Ensure you have your latest graphics card drivers installed before use.
<img alt="Overheating" src="https://imgs.xkcd.com/comics/workflow.png"/> <img alt="Overheating" src="https://imgs.xkcd.com/comics/workflow.png"/>
</p> </p>
## Build Dependencies
- Go 1.10.3+
- On macOS, you need Xcode or Command Line Tools for Xcode (`xcode-select --install`) for required headers and libraries.
- On Ubuntu/Debian-like Linux distributions, you need `libgl1-mesa-dev xorg-dev`.
- On CentOS/Fedora-like Linux distributions, you need `libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel mesa-libGL-devel libXi-devel`.
## Platform Support ## Platform Support
| Platform | Supported | | Platform | Supported |
@ -47,25 +46,12 @@ Ensure you have your latest graphics card drivers installed before use.
| MacOSX | ⏳ | | MacOSX | ⏳ |
| Windows | ⏳ | | Windows | ⏳ |
## Planned Features ## Build Dependencies
| Feature | Done | Notes | - Go 1.10.3+
|-----------------------------|------|-------| - On macOS, you need Xcode or Command Line Tools for Xcode (`xcode-select --install`) for required headers and libraries.
| Pty allocation | ✔ | - On Ubuntu/Debian-like Linux distributions, you need `libgl1-mesa-dev xorg-dev`.
| OpenGL rendering | ✔ | - On CentOS/Fedora-like Linux distributions, you need `libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel mesa-libGL-devel libXi-devel`.
| 8-bit (256) colour | ✔ |
| 24-bit (true) colour | ✔ |
| Resizing/content reordering | ✔ |
| ANSI escape codes | ✔ |
| UTF-8 input | ✔ |
| UTF-8 output | ✔ |
| Copy/paste | ✔ |
| Customisable colour schemes | ✔ |
| Config file | ✔ |
| Scrolling | ✔ |
| Mouse interaction | ✔ |
| Clickable URLs | ✔ |
| Sweet render effects | |
## Keyboard Shortcuts ## Keyboard Shortcuts

View File

@ -40,11 +40,11 @@ func (cell *Cell) DrawImage(x, y float32) {
} }
var tex uint32 var tex uint32
gl.Enable(gl.TEXTURE_2D)
gl.GenTextures(1, &tex) gl.GenTextures(1, &tex)
gl.BindTexture(gl.TEXTURE_2D, tex) gl.BindTexture(gl.TEXTURE_2D, tex)
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
@ -60,6 +60,9 @@ func (cell *Cell) DrawImage(x, y float32) {
gl.Ptr(cell.image.Pix), gl.Ptr(cell.image.Pix),
) )
gl.BindTexture(gl.TEXTURE_2D, 0) gl.BindTexture(gl.TEXTURE_2D, 0)
gl.Disable(gl.TEXTURE_2D)
gl.Disable(gl.BLEND)
var w float32 = float32(cell.image.Bounds().Size().X) var w float32 = float32(cell.image.Bounds().Size().X)
var h float32 = float32(cell.image.Bounds().Size().Y) var h float32 = float32(cell.image.Bounds().Size().Y)
@ -67,6 +70,7 @@ func (cell *Cell) DrawImage(x, y float32) {
var readFboId uint32 var readFboId uint32
gl.GenFramebuffers(1, &readFboId) gl.GenFramebuffers(1, &readFboId)
gl.BindFramebuffer(gl.READ_FRAMEBUFFER, readFboId) gl.BindFramebuffer(gl.READ_FRAMEBUFFER, readFboId)
gl.FramebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FramebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, tex, 0) gl.TEXTURE_2D, tex, 0)
gl.BlitFramebuffer(0, 0, int32(w), int32(h), gl.BlitFramebuffer(0, 0, int32(w), int32(h),
@ -74,6 +78,7 @@ func (cell *Cell) DrawImage(x, y float32) {
gl.COLOR_BUFFER_BIT, gl.LINEAR) gl.COLOR_BUFFER_BIT, gl.LINEAR)
gl.BindFramebuffer(gl.READ_FRAMEBUFFER, 0) gl.BindFramebuffer(gl.READ_FRAMEBUFFER, 0)
gl.DeleteFramebuffers(1, &readFboId) gl.DeleteFramebuffers(1, &readFboId)
} }
func (cell *Cell) Attr() CellAttributes { func (cell *Cell) Attr() CellAttributes {

File diff suppressed because one or more lines are too long

BIN
sixel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -6,8 +6,6 @@ import (
"image/color" "image/color"
"strconv" "strconv"
"strings" "strings"
"github.com/go-gl/gl/v2.1/gl"
) )
type Sixel struct { type Sixel struct {
@ -32,7 +30,7 @@ func decompress(data string) string {
inMarker = true inMarker = true
countStr = "" countStr = ""
} else { } else {
output = fmt.Sprintf("%s%c", output, r) output += string(r)
} }
continue continue
} }
@ -41,9 +39,7 @@ func decompress(data string) string {
countStr = fmt.Sprintf("%s%c", countStr, r) countStr = fmt.Sprintf("%s%c", countStr, r)
} else { } else {
count, _ := strconv.Atoi(countStr) count, _ := strconv.Atoi(countStr)
for i := 0; i < count; i++ { output += strings.Repeat(string(r), count)
output = fmt.Sprintf("%s%c", output, r)
}
inMarker = false inMarker = false
} }
} }
@ -85,9 +81,9 @@ func ParseString(data string) (*Sixel, error) {
ratio = 5 ratio = 5
case "2": case "2":
ratio = 3 ratio = 3
case "", "3", "4", "5", "6": case "3", "4", "5", "6":
ratio = 2 ratio = 2
case "7", "8", "9": case "7", "8", "9", "":
ratio = 1 ratio = 1
} }
if len(headers) > 1 { if len(headers) > 1 {
@ -165,37 +161,6 @@ func ParseString(data string) (*Sixel, error) {
return &six, nil return &six, nil
} }
func (six *Sixel) Draw() error {
rgba := six.RGBA()
var handle uint32
gl.GenTextures(1, &handle)
target := uint32(gl.TEXTURE_2D)
internalFmt := int32(gl.SRGB_ALPHA)
format := uint32(gl.RGBA)
width := int32(rgba.Rect.Size().X)
height := int32(rgba.Rect.Size().Y)
pixType := uint32(gl.UNSIGNED_BYTE)
dataPtr := gl.Ptr(rgba.Pix)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(target, handle)
// set the texture wrapping/filtering options (applies to current bound texture obj)
// TODO-cs
//gl.TexParameteri(texture.target, gl.TEXTURE_WRAP_R, wrapR)
//gl.TexParameteri(texture.target, gl.TEXTURE_WRAP_S, wrapS)
gl.TexParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR) // minification filter
gl.TexParameteri(target, gl.TEXTURE_MAG_FILTER, gl.LINEAR) // magnification filter
gl.TexImage2D(target, 0, internalFmt, width, height, 0, format, pixType, dataPtr)
// unbind
gl.BindTexture(target, 0)
return nil
}
func (six *Sixel) setPixel(x, y uint, c colour, vhRatio uint) { func (six *Sixel) setPixel(x, y uint, c colour, vhRatio uint) {
if six.px == nil { if six.px == nil {
@ -227,11 +192,11 @@ func (six *Sixel) RGBA() *image.RGBA {
for x, r := range six.px { for x, r := range six.px {
for y, colour := range r { for y, colour := range r {
rgba.SetRGBA(int(x), int(six.height)-int(y), color.RGBA{ rgba.Set(int(x), int(six.height)-int(y), color.RGBA{
colour[0], R: colour[0],
colour[1], G: colour[1],
colour[2], B: colour[2],
255, A: 255,
}) })
} }
} }

View File

@ -2,9 +2,11 @@ package terminal
import ( import (
"fmt" "fmt"
"image"
"image/draw"
"math" "math"
"strings"
"github.com/go-gl/gl/all-core/gl"
"github.com/liamg/aminal/sixel" "github.com/liamg/aminal/sixel"
) )
@ -18,7 +20,9 @@ func sixelHandler(pty chan rune, terminal *Terminal) error {
_ = <-pty // swallow \ or bell _ = <-pty // swallow \ or bell
break break
} }
data = append(data, b) if b >= 33 {
data = append(data, b)
}
} }
six, err := sixel.ParseString(string(data)) six, err := sixel.ParseString(string(data))
@ -26,21 +30,43 @@ func sixelHandler(pty chan rune, terminal *Terminal) error {
return fmt.Errorf("Failed to parse sixel data: %s", err) return fmt.Errorf("Failed to parse sixel data: %s", err)
} }
originalImage := six.RGBA()
w := originalImage.Bounds().Size().X
h := originalImage.Bounds().Size().Y
x, y := terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine() x, y := terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine()
terminal.ActiveBuffer().Write(' ')
cell := terminal.ActiveBuffer().GetCell(x, y) fromBottom := int(terminal.ActiveBuffer().ViewHeight() - y)
if cell == nil { lines := int(math.Ceil(float64(h) / float64(terminal.charHeight)))
return fmt.Errorf("Missing cell for sixel") if fromBottom < lines+2 {
y -= (uint16(lines+2) - uint16(fromBottom))
} }
for l := 0; l <= int(lines); l++ {
gl.UseProgram(terminal.program) terminal.ActiveBuffer().Write([]rune(strings.Repeat(" ", int(terminal.ActiveBuffer().ViewWidth())))...)
cell.SetImage(six.RGBA())
imageHeight := float64(cell.Image().Bounds().Size().Y)
lines := int(math.Ceil(imageHeight / float64(terminal.charHeight)))
for l := 0; l <= int(lines+1); l++ {
terminal.ActiveBuffer().NewLine() terminal.ActiveBuffer().NewLine()
} }
cols := int(math.Ceil(float64(w) / float64(terminal.charWidth)))
for offsetY := 0; offsetY < lines-1; offsetY++ {
for offsetX := 0; offsetX < cols-1; offsetX++ {
cell := terminal.ActiveBuffer().GetCell(x+uint16(offsetX), y+uint16((lines-2)-offsetY))
if cell == nil {
continue
}
img := originalImage.SubImage(image.Rect(
offsetX*int(terminal.charWidth),
offsetY*int(terminal.charHeight),
(offsetX*int(terminal.charWidth))+int(terminal.charWidth),
(offsetY*int(terminal.charHeight))+int(terminal.charHeight),
))
rgba := image.NewRGBA(image.Rect(0, 0, int(terminal.charWidth), int(terminal.charHeight)))
draw.Draw(rgba, rgba.Bounds(), img, img.Bounds().Min, draw.Src)
cell.SetImage(rgba)
}
}
return nil return nil
} }