added image rendering and resizing
This commit is contained in:
parent
658aac8704
commit
5588d95957
@ -4,14 +4,12 @@ import (
|
||||
"fmt"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
//"github.com/dolmen-go/kittyimg"
|
||||
|
||||
//"whspbrd/pkg/systray"
|
||||
"whspbrd/pkg/term_image"
|
||||
"whspbrd/pkg/cell_size"
|
||||
"whspbrd/pkg/render_image"
|
||||
)
|
||||
|
||||
var messages []string
|
||||
@ -85,7 +83,7 @@ func updateChatView(v *gocui.View) {
|
||||
//}
|
||||
|
||||
// Print image directly to terminal (stdout)
|
||||
term_image.RenderImage("kogami-rounded.png", i*3+2, 23)
|
||||
render_image.RenderImage("kogami-rounded.png", i*3+2, 23, 50, 50)
|
||||
//err = kittyimg.Fprintln(os.Stdout, img)
|
||||
//if err != nil {
|
||||
// log.Println("Error rendering image:", err)
|
||||
@ -196,24 +194,39 @@ func quit(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Print("\033")
|
||||
|
||||
//systray.Systray()
|
||||
g, err := gocui.NewGui(gocui.OutputNormal)
|
||||
//g, err := gocui.NewGui(gocui.OutputNormal)
|
||||
//if err != nil {
|
||||
// log.Panicln(err)
|
||||
//}
|
||||
//defer g.Close()
|
||||
//
|
||||
//g.SetManagerFunc(layout)
|
||||
//
|
||||
//if err := keybindings(g); err != nil {
|
||||
// log.Panicln(err)
|
||||
//}
|
||||
//
|
||||
//if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
||||
// log.Panicln(err)
|
||||
//}
|
||||
// and here is agrid making a grid of images
|
||||
//for i := 0; i < 10; i++ {
|
||||
// for j := 0; j < 10; j++ {
|
||||
// render_image.RenderImage("kogami-pf-edit.jpg", i*3+2, j*7+23, 64, 64)
|
||||
// }
|
||||
//}
|
||||
|
||||
//render_image.RenderImage("kogami-pf-edit.jpg", 0, 3, 750, 0)
|
||||
|
||||
w, h, err := cell_size.GetTerminalCellSizePixels()
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
fmt.Println("Error getting terminal cell size:", err)
|
||||
return
|
||||
}
|
||||
defer g.Close()
|
||||
width, height := cell_size.GetConsoleSize()
|
||||
|
||||
g.SetManagerFunc(layout)
|
||||
fmt.Println("Terminal cell size in pixels:", w, "x", h)
|
||||
fmt.Println("Console size in characters:", width, "x", height)
|
||||
|
||||
if err := keybindings(g); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
term_image.RenderImage("kogami-rounded.png", 20, 20)
|
||||
}
|
||||
|
||||
48
pkg/cell_size/cell_size_unix.go
Normal file
48
pkg/cell_size/cell_size_unix.go
Normal file
@ -0,0 +1,48 @@
|
||||
//go:build !windows
|
||||
|
||||
package cell_size
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type winsize struct {
|
||||
Rows uint16
|
||||
Cols uint16
|
||||
Xpixels uint16
|
||||
Ypixels uint16
|
||||
}
|
||||
|
||||
func GetTerminalCellSizePixels() (widthPx int, heightPx int, err error) {
|
||||
ws := &winsize{}
|
||||
_, _, errno := syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
os.Stdout.Fd(),
|
||||
uintptr(syscall.TIOCGWINSZ),
|
||||
uintptr(unsafe.Pointer(ws)),
|
||||
)
|
||||
if errno != 0 {
|
||||
return 0, 0, errno
|
||||
}
|
||||
if ws.Cols == 0 || ws.Rows == 0 {
|
||||
return 0, 0, fmt.Errorf("terminal rows or columns is zero")
|
||||
}
|
||||
widthPx = int(ws.Xpixels) / int(ws.Cols)
|
||||
heightPx = int(ws.Ypixels) / int(ws.Rows)
|
||||
return
|
||||
}
|
||||
|
||||
func GetConsoleSize() (int, int) {
|
||||
var sz struct {
|
||||
rows uint16
|
||||
cols uint16
|
||||
xpixels uint16
|
||||
ypixels uint16
|
||||
}
|
||||
_, _, _ = syscall.Syscall(syscall.SYS_IOCTL,
|
||||
uintptr(syscall.Stdout), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz)))
|
||||
return int(sz.cols), int(sz.rows)
|
||||
}
|
||||
107
pkg/cell_size/cell_size_win.go
Normal file
107
pkg/cell_size/cell_size_win.go
Normal file
@ -0,0 +1,107 @@
|
||||
//go:build windows
|
||||
|
||||
package cell_size
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type coord struct {
|
||||
X int16
|
||||
Y int16
|
||||
}
|
||||
|
||||
type consoleFontInfoEx struct {
|
||||
cbSize uint32
|
||||
nFont uint32
|
||||
dwFontSize coord
|
||||
fontFamily uint32
|
||||
fontWeight uint32
|
||||
faceName [32]uint16
|
||||
}
|
||||
|
||||
var (
|
||||
kernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
procGetCurrentConsoleFontEx = kernel32.NewProc("GetCurrentConsoleFontEx")
|
||||
)
|
||||
|
||||
func GetTerminalCellSizePixels() (widthPx int, heightPx int, err error) {
|
||||
var fontInfo consoleFontInfoEx
|
||||
fontInfo.cbSize = uint32(unsafe.Sizeof(fontInfo))
|
||||
|
||||
stdOutHandle := windows.Handle(syscall.Stdout)
|
||||
ret, _, err := procGetCurrentConsoleFontEx.Call(
|
||||
uintptr(stdOutHandle),
|
||||
uintptr(0), // bMaximumWindow = false
|
||||
uintptr(unsafe.Pointer(&fontInfo)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(fontInfo.dwFontSize.X), int(fontInfo.dwFontSize.Y), nil
|
||||
}
|
||||
|
||||
type (
|
||||
short int16
|
||||
word uint16
|
||||
smallRect struct {
|
||||
Left short
|
||||
Top short
|
||||
Right short
|
||||
Bottom short
|
||||
}
|
||||
consoleScreenBufferInfo struct {
|
||||
Size coord
|
||||
CursorPosition coord
|
||||
Attributes word
|
||||
Window smallRect
|
||||
MaximumWindowSize coord
|
||||
}
|
||||
)
|
||||
|
||||
var kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
|
||||
var getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
|
||||
|
||||
// GetConsoleSize returns the current number of columns and rows in the active console window.
|
||||
// The return value of this function is in the order of cols, rows.
|
||||
func GetConsoleSize() (int, int) {
|
||||
stdoutHandle := getStdHandle(syscall.STD_OUTPUT_HANDLE)
|
||||
var info, err = getConsoleScreenBufferInfo(stdoutHandle)
|
||||
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1)
|
||||
}
|
||||
|
||||
func getError(r1, r2 uintptr, lastErr error) error {
|
||||
// If the function fails, the return value is zero.
|
||||
if r1 == 0 {
|
||||
if lastErr != nil {
|
||||
return lastErr
|
||||
}
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getStdHandle(stdhandle int) uintptr {
|
||||
handle, err := syscall.GetStdHandle(stdhandle)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("could not get standard io handle %d", stdhandle))
|
||||
}
|
||||
return uintptr(handle)
|
||||
}
|
||||
|
||||
func getConsoleScreenBufferInfo(handle uintptr) (*consoleScreenBufferInfo, error) {
|
||||
var info consoleScreenBufferInfo
|
||||
if err := getError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package term_image
|
||||
package render_image
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
@ -6,6 +6,8 @@ import (
|
||||
"image"
|
||||
"image/draw"
|
||||
"os"
|
||||
|
||||
"whspbrd/pkg/resize_image"
|
||||
)
|
||||
|
||||
func LoadImage(filePath string) (image.Image, error) {
|
||||
@ -18,25 +20,26 @@ func LoadImage(filePath string) (image.Image, error) {
|
||||
return img, err
|
||||
}
|
||||
|
||||
func ConvertToRGBA(img image.Image) *image.RGBA {
|
||||
func convertToRGBA(img image.Image) *image.RGBA {
|
||||
rgba := image.NewRGBA(img.Bounds())
|
||||
draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)
|
||||
return rgba
|
||||
}
|
||||
|
||||
func EncodeImageToBase64RGBA(rgba *image.RGBA) string {
|
||||
func encodeImageToBase64RGBA(rgba *image.RGBA) string {
|
||||
return base64.StdEncoding.EncodeToString(rgba.Pix)
|
||||
}
|
||||
|
||||
func RenderImage(filepath string, row int, col int) {
|
||||
func RenderImage(filepath string, row int, col int, width_ int, height_ int) {
|
||||
img, err := LoadImage(filepath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error loading image: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
rgba := ConvertToRGBA(img)
|
||||
encoded := EncodeImageToBase64RGBA(rgba)
|
||||
rgba := convertToRGBA(img)
|
||||
rgba, _ = resize_image.Resize(*rgba, width_, height_)
|
||||
encoded := encodeImageToBase64RGBA(rgba)
|
||||
|
||||
width := rgba.Rect.Dx()
|
||||
height := rgba.Rect.Dy()
|
||||
@ -64,4 +67,3 @@ func RenderImage(filepath string, row int, col int) {
|
||||
}
|
||||
fmt.Print("\033[u")
|
||||
}
|
||||
|
||||
33
pkg/resize_image/resize_image.go
Normal file
33
pkg/resize_image/resize_image.go
Normal file
@ -0,0 +1,33 @@
|
||||
package resize_image
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
func Resize(img image.RGBA, width int, height int) (*image.RGBA, error) {
|
||||
if width <= 0 || height <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
newImg := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
scaleX := float64(img.Bounds().Dx()) / float64(width)
|
||||
scaleY := float64(img.Bounds().Dy()) / float64(height)
|
||||
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
srcX := int(float64(x) * scaleX)
|
||||
srcY := int(float64(y) * scaleY)
|
||||
|
||||
r, g, b, a := getPixel(img, srcX, srcY)
|
||||
newImg.Set(x, y, color.RGBA{r, g, b, a})
|
||||
}
|
||||
}
|
||||
|
||||
return newImg, nil
|
||||
}
|
||||
|
||||
func getPixel(img image.RGBA, x int, y int) (uint8, uint8, uint8, uint8) {
|
||||
index := img.PixOffset(x, y)
|
||||
return img.Pix[index], img.Pix[index+1], img.Pix[index+2], img.Pix[index+3]
|
||||
}
|
||||
66
pkg/term_image/term_image.go
Normal file
66
pkg/term_image/term_image.go
Normal file
@ -0,0 +1,66 @@
|
||||
package term_image
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
"os"
|
||||
)
|
||||
|
||||
func LoadImage(filePath string) (image.Image, error) {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
img, _, err := image.Decode(f)
|
||||
return img, err
|
||||
}
|
||||
|
||||
func convertToRGBA(img image.Image) *image.RGBA {
|
||||
rgba := image.NewRGBA(img.Bounds())
|
||||
draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)
|
||||
return rgba
|
||||
}
|
||||
|
||||
func encodeImageToBase64RGBA(rgba *image.RGBA) string {
|
||||
return base64.StdEncoding.EncodeToString(rgba.Pix)
|
||||
}
|
||||
|
||||
func RenderImage(filepath string, row int, col int) {
|
||||
img, err := LoadImage(filepath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error loading image: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
rgba := convertToRGBA(img)
|
||||
encoded := encodeImageToBase64RGBA(rgba)
|
||||
|
||||
width := rgba.Rect.Dx()
|
||||
height := rgba.Rect.Dy()
|
||||
|
||||
fmt.Printf("\033[s\033[%d;%dH", row, col)
|
||||
|
||||
chunk_size := 4096
|
||||
pos := 0
|
||||
first := true
|
||||
for pos < len(encoded) {
|
||||
fmt.Print("\033_G")
|
||||
if first {
|
||||
fmt.Printf("q=2,a=T,f=32,s=%d,v=%d,", width, height)
|
||||
first = false
|
||||
}
|
||||
chunk_len := len(encoded) - pos
|
||||
if chunk_len > chunk_size {
|
||||
chunk_len = chunk_size
|
||||
}
|
||||
if pos+chunk_len < len(encoded) {
|
||||
fmt.Print("m=1")
|
||||
}
|
||||
fmt.Printf(";%s\033\\", encoded[pos:pos+chunk_len])
|
||||
pos += chunk_len
|
||||
}
|
||||
fmt.Print("\033[u")
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user