added image rendering and resizing
This commit is contained in:
parent
658aac8704
commit
5588d95957
@ -4,14 +4,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"log"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jroimartin/gocui"
|
"github.com/jroimartin/gocui"
|
||||||
//"github.com/dolmen-go/kittyimg"
|
|
||||||
|
|
||||||
//"whspbrd/pkg/systray"
|
//"whspbrd/pkg/systray"
|
||||||
"whspbrd/pkg/term_image"
|
"whspbrd/pkg/cell_size"
|
||||||
|
"whspbrd/pkg/render_image"
|
||||||
)
|
)
|
||||||
|
|
||||||
var messages []string
|
var messages []string
|
||||||
@ -85,7 +83,7 @@ func updateChatView(v *gocui.View) {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
// Print image directly to terminal (stdout)
|
// 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)
|
//err = kittyimg.Fprintln(os.Stdout, img)
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
// log.Println("Error rendering image:", err)
|
// log.Println("Error rendering image:", err)
|
||||||
@ -196,24 +194,39 @@ func quit(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Print("\033")
|
|
||||||
|
|
||||||
//systray.Systray()
|
//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 {
|
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 (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
@ -6,6 +6,8 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"whspbrd/pkg/resize_image"
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoadImage(filePath string) (image.Image, error) {
|
func LoadImage(filePath string) (image.Image, error) {
|
||||||
@ -18,25 +20,26 @@ func LoadImage(filePath string) (image.Image, error) {
|
|||||||
return img, err
|
return img, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertToRGBA(img image.Image) *image.RGBA {
|
func convertToRGBA(img image.Image) *image.RGBA {
|
||||||
rgba := image.NewRGBA(img.Bounds())
|
rgba := image.NewRGBA(img.Bounds())
|
||||||
draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)
|
draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)
|
||||||
return rgba
|
return rgba
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncodeImageToBase64RGBA(rgba *image.RGBA) string {
|
func encodeImageToBase64RGBA(rgba *image.RGBA) string {
|
||||||
return base64.StdEncoding.EncodeToString(rgba.Pix)
|
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)
|
img, err := LoadImage(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error loading image: %v\n", err)
|
fmt.Printf("Error loading image: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rgba := ConvertToRGBA(img)
|
rgba := convertToRGBA(img)
|
||||||
encoded := EncodeImageToBase64RGBA(rgba)
|
rgba, _ = resize_image.Resize(*rgba, width_, height_)
|
||||||
|
encoded := encodeImageToBase64RGBA(rgba)
|
||||||
|
|
||||||
width := rgba.Rect.Dx()
|
width := rgba.Rect.Dx()
|
||||||
height := rgba.Rect.Dy()
|
height := rgba.Rect.Dy()
|
||||||
@ -64,4 +67,3 @@ func RenderImage(filepath string, row int, col int) {
|
|||||||
}
|
}
|
||||||
fmt.Print("\033[u")
|
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