├── .github └── dependabot.yml ├── LICENSE ├── README.md ├── explosion.go ├── go.mod ├── go.sum └── screenshots └── lenna.png /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: gomod 5 | directory: / 6 | schedule: 7 | interval: daily 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jeppe Toustrup 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Explosion 2 | 3 | Command line image viewer which supports 24-bit (16 million) colors, much based upon the post by [maato](http://softwarebakery.com/maato/image_in_terminal.html). The implementation in that post is however only with 256 colors while a [bunch of terminal emulators](https://gist.github.com/XVilka/8346728) has support for True Colors, so I had an attempt at reimplementing the same but with more colors. 4 | 5 | That's also where the name comes from, as this is more of an explosion of colors, compared to maato's implementation. 6 | 7 | 8 | ## Usage 9 | 10 | ``` 11 | $ explosion --help 12 | Usage: explosion [options] [file | - ...] 13 | 14 | Specify "-" or just nothing to read from stdin. 15 | 16 | Options: 17 | -h uint 18 | Maximum height of output in number of lines (default 110) 19 | -w uint 20 | Maximum width of output in number of columns (default 204) 21 | ``` 22 | 23 | 24 | ## Screenshots 25 | 26 | Taken in iTerm 2.9 on OS X: 27 | 28 | ![Lenna](screenshots/lenna.png) 29 | 30 | 31 | ## Installation 32 | 33 | If you have [Go](https://golang.org/) installed, then you should simply run: 34 | 35 | ``` 36 | go get github.com/Tenzer/explosion 37 | ``` 38 | 39 | and you will get the `explosion` executable inside `$GOPATH/bin`. 40 | 41 | 42 | ## Change log 43 | 44 | ### 1.1.2 - 2021-05-23 45 | * Switch dependencies to use go modules 46 | * Update all dependencies 47 | 48 | ### 1.1.1 - 2018-10-22 49 | * Read from standard input when no input file is provided. 50 | * Vendored dependencies. 51 | 52 | ### 1.1.0 - 2015-10-21 53 | * Fix bug where the outputted RGB values was on a scale from 0-65535 instead of 0-255 which doesn't work everywhere. 54 | * Attempt to decrease output amount if the two colors within the same character is identical. 55 | * Add support for reading from standard input when specifying "-" as the filename. 56 | * Add command line options `-h` and `-w` to overrule the size of the printed image. 57 | * Automatically determine image default output size based upon terminal size. 58 | 59 | ### 1.0.1 - 2015-10-19 60 | * Use "lower half block" instead of "upper half block" for the sub-character resolution. This removes the artifacts from the upper half block not covering the top row of pixels with iTerm on OS X. 61 | 62 | ### 1.0.0 - 2015-10-19 63 | * Initial release 64 | 65 | 66 | ## To do 67 | 68 | * Attempt to implement maato's solution with the higher image resolution, by making use of the extra characters available. 69 | * Possibly add support for 256 color output, for terminals which only support that (with a flag). 70 | * Find out if goroutines makes sense performance wise, ie. per image or per set of rows in the image? 71 | -------------------------------------------------------------------------------- /explosion.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "image" 7 | "os" 8 | 9 | "github.com/kr/pty" 10 | "github.com/nfnt/resize" 11 | 12 | _ "image/gif" 13 | _ "image/jpeg" 14 | _ "image/png" 15 | 16 | _ "golang.org/x/image/bmp" 17 | _ "golang.org/x/image/tiff" 18 | ) 19 | 20 | func printImage(img image.Image, width uint, height uint) { 21 | resized := resize.Thumbnail(width, height, img, resize.NearestNeighbor) 22 | bounds := resized.Bounds() 23 | 24 | for y := bounds.Min.Y; y < bounds.Max.Y; y += 2 { 25 | for x := bounds.Min.X; x < bounds.Max.X; x++ { 26 | upperRed, upperGreen, upperBlue, _ := resized.At(x, y).RGBA() 27 | lowerRed, lowerGreen, lowerBlue, _ := resized.At(x, y+1).RGBA() 28 | 29 | // Only print the background if upper and lower row are same color 30 | if upperRed == lowerRed && upperGreen == lowerGreen && upperBlue == lowerBlue { 31 | fmt.Printf( 32 | "\x1b[48;2;%d;%d;%dm ", 33 | upperRed/256, 34 | upperGreen/256, 35 | upperBlue/256, 36 | ) 37 | } else { 38 | fmt.Printf( 39 | "\x1b[48;2;%d;%d;%dm\x1b[38;2;%d;%d;%dm▄", 40 | upperRed/256, 41 | upperGreen/256, 42 | upperBlue/256, 43 | lowerRed/256, 44 | lowerGreen/256, 45 | lowerBlue/256, 46 | ) 47 | } 48 | } 49 | fmt.Println("\x1b[0m") 50 | } 51 | } 52 | 53 | func main() { 54 | heightInt, widthInt, _ := pty.Getsize(os.Stdout) 55 | 56 | var width uint 57 | var height uint 58 | 59 | // The three subtracted lines is to have room for command, file name and prompt after explosion 60 | flag.UintVar(&width, "w", uint(widthInt), "Maximum width of output in number of columns") 61 | flag.UintVar(&height, "h", uint((heightInt-3)*2), "Maximum height of output in number of half lines") 62 | flag.Usage = func() { 63 | fmt.Fprintf(os.Stderr, "Usage: %s [options] [file | - ...]\n\n", os.Args[0]) 64 | fmt.Fprintln(os.Stderr, " Specify \"-\" or just nothing to read from stdin.") 65 | fmt.Fprintln(os.Stderr) 66 | fmt.Fprintln(os.Stderr, "Options:") 67 | flag.PrintDefaults() 68 | } 69 | flag.Parse() 70 | 71 | filenames := flag.Args() 72 | if len(filenames) == 0 { 73 | fmt.Println("stdin:") 74 | sourceImage, _, err := image.Decode(os.Stdin) 75 | if err != nil { 76 | fmt.Fprintln(os.Stderr, "Error:", err) 77 | } else { 78 | printImage(sourceImage, width, height) 79 | } 80 | } else { 81 | for i, filename := range filenames { 82 | if i > 0 { 83 | fmt.Println() 84 | } 85 | 86 | var file *os.File 87 | var err error 88 | 89 | if filename == "-" { 90 | fmt.Println("stdin:") 91 | file = os.Stdin 92 | } else { 93 | fmt.Printf("%s:\n", filename) 94 | file, err = os.Open(filename) 95 | if err != nil { 96 | fmt.Fprintln(os.Stderr, "Error:", err) 97 | continue 98 | } 99 | } 100 | 101 | sourceImage, _, err := image.Decode(file) 102 | _ = file.Close() 103 | if err != nil { 104 | fmt.Fprintln(os.Stderr, "Error:", err) 105 | continue 106 | } 107 | 108 | printImage(sourceImage, width, height) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Tenzer/explosion 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/creack/pty v1.1.12 // indirect 7 | github.com/kr/pty v1.1.8 8 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 9 | golang.org/x/image v0.27.0 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 2 | github.com/creack/pty v1.1.12 h1:2QLiUCEbsI13vUyWH6026g0o4u7vxgg2hLUc+D7FPFU= 3 | github.com/creack/pty v1.1.12/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 4 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 5 | github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= 6 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 7 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= 8 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 9 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 10 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 11 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 12 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 13 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 14 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 15 | golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= 16 | golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= 17 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 18 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 19 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 20 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 21 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 22 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 23 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 24 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 25 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 26 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 27 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 28 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 29 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 30 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 31 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 32 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 33 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 34 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 35 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 36 | golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 37 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 38 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 39 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 40 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 41 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 42 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 43 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 44 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 45 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 46 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 47 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 48 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 49 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 50 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 51 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 52 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 53 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 54 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 55 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 56 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 57 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 58 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 59 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 60 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 61 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 62 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 63 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 64 | golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= 65 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 66 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 67 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 68 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 69 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 70 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 71 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 72 | -------------------------------------------------------------------------------- /screenshots/lenna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tenzer/explosion/232ce5592d9d29ad53fc0cdbd7931b35e71f7c62/screenshots/lenna.png --------------------------------------------------------------------------------