diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2023-08-30 23:04:24 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2023-08-30 23:04:24 -0700 |
| commit | c20c9685aae51deeeef560d16bcf6e6039438b6c (patch) | |
| tree | ea1aa693976fe01b5be3165d832ce4982d80953b | |
| download | imgutil-c20c9685aae51deeeef560d16bcf6e6039438b6c.tar.zst | |
icat: a program to print images in the terminal.
Thanks to WizardCoder for implementing the first version of this code. I
had to clean it up quite a bit though.
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | flake.lock | 25 | ||||
| -rw-r--r-- | flake.nix | 17 | ||||
| -rw-r--r-- | go.mod | 7 | ||||
| -rw-r--r-- | go.sum | 4 | ||||
| -rw-r--r-- | icat.go | 80 |
6 files changed, 134 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4a847d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/result diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..775df95 --- /dev/null +++ b/flake.lock @@ -0,0 +1,25 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1693355128, + "narHash": "sha256-+ZoAny3ZxLcfMaUoLVgL9Ywb/57wP+EtsdNGuXUJrwg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a63a64b593dcf2fe05f7c5d666eb395950f36bc9", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..81b7d36 --- /dev/null +++ b/flake.nix @@ -0,0 +1,17 @@ +{ + outputs = { self, nixpkgs }: + let pkgs = nixpkgs.legacyPackages.x86_64-linux; in + rec { + + packages.x86_64-linux.icat = pkgs.buildGoModule { + pname = "icat"; + version = "1.0"; + + src = self; + + vendorHash = "sha256-WiEK8nq13mdTFnyxDiMRWM1tb60mlZY0fVmtlH5ZWgw="; + }; + + defaultPackage.x86_64-linux = packages.x86_64-linux.icat; + }; +} @@ -0,0 +1,7 @@ +module icat + +go 1.20 + +require golang.org/x/term v0.11.0 + +require golang.org/x/sys v0.11.0 // indirect @@ -0,0 +1,4 @@ +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= @@ -0,0 +1,80 @@ +package main + +import ( + "fmt" + "golang.org/x/term" + "image" + _ "image/jpeg" + _ "image/png" + "os" +) + +func load(filename string) (image.Image, error) { + file := os.Stdin + if filename != "-" { + var err error + file, err = os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + } + + img, _, err := image.Decode(file) + if err != nil { + return nil, fmt.Errorf("decode %q: %s", filename, err) + } + + return img, nil +} + +func printImg(filename string) error { + img, err := load(filename) + if err != nil { + return err + } + + cols, lines, err := term.GetSize(1) + if err != nil { + return fmt.Errorf("terminal size: %s", err) + } + lines-- // Leave a line for the status bar. + + // Try not to stretch the image. + if img.Bounds().Dy()*cols <= img.Bounds().Dx()*lines*5/2 { + lines = img.Bounds().Dy() * cols / (img.Bounds().Dx() * 5 / 2) + } else { + cols = img.Bounds().Dx() * lines * 5 / 2 / img.Bounds().Dy() + } + + // nearest-neighbor interpolation + for y := 0; y < lines; y++ { + for x := 0; x < cols; x++ { + sx := x*img.Bounds().Dx()/cols + img.Bounds().Min.X + sy := y*img.Bounds().Dy()/lines + img.Bounds().Min.Y + r, g, b, _ := img.At(sx, sy).RGBA() + fmt.Printf("\033[48;2;%d;%d;%dm ", r>>8, g>>8, b>>8) + } + fmt.Println("\033[49m") + } + return nil +} + +func icat(args []string) error { + if len(args) == 0 { + args = []string{"-"} + } + for _, f := range args { + if err := printImg(f); err != nil { + return err + } + } + return nil +} + +func main() { + if err := icat(os.Args[1:]); err != nil { + fmt.Fprintf(os.Stderr, "Failed: %s\n", err) + os.Exit(1) + } +} |
