aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2023-08-30 23:04:24 -0700
committerRose Hogenson <rosehogenson@posteo.net>2023-08-30 23:04:24 -0700
commitc20c9685aae51deeeef560d16bcf6e6039438b6c (patch)
treeea1aa693976fe01b5be3165d832ce4982d80953b
downloadimgutil-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--.gitignore1
-rw-r--r--flake.lock25
-rw-r--r--flake.nix17
-rw-r--r--go.mod7
-rw-r--r--go.sum4
-rw-r--r--icat.go80
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;
+ };
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..f2b8f47
--- /dev/null
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..6aed7b2
--- /dev/null
+++ b/go.sum
@@ -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=
diff --git a/icat.go b/icat.go
new file mode 100644
index 0000000..2b2a847
--- /dev/null
+++ b/icat.go
@@ -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)
+ }
+}