├── COPYING ├── Makefile ├── README.md ├── extra ├── Makefile ├── cows │ └── rmshd.png └── xterm2gimppal.c ├── img2xterm.c └── man6 └── img2xterm.6 /COPYING: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX = /usr/local 2 | INSTALL = install 3 | LN = ln -fs 4 | 5 | ifeq ($(shell sh -c 'which gcc>/dev/null 2>/dev/null && echo y'), y) 6 | CC = gcc 7 | CFLAGS = -O2 -Wall 8 | LDFLAGS = -s 9 | endif 10 | 11 | ifeq ($(shell sh -c 'uname'), Darwin) 12 | CC = clang 13 | CFLAGS = -O2 -Wall 14 | LDFLAGS = 15 | endif 16 | 17 | ifeq ($(shell sh -c 'which ncurses5-config>/dev/null 2>/dev/null && echo y'), y) 18 | DEFS = 19 | CFLAGS += $(shell ncurses5-config --cflags) 20 | LIBS = -lm $(shell ncurses5-config --libs) 21 | else 22 | ifeq ($(shell sh -c 'which ncursesw5-config>/dev/null 2>/dev/null && echo y'), y) 23 | DEFS = 24 | CFLAGS += $(shell ncursesw5-config --cflags) 25 | LIBS = -lm $(shell ncursesw5-config --libs) 26 | else 27 | DEFS = -DNO_CURSES 28 | LIBS = -lm 29 | endif 30 | endif 31 | 32 | ifeq ($(shell sh -c 'which MagickWand-config>/dev/null 2>/dev/null && echo y'), y) 33 | WANDCONFIG = MagickWand-config 34 | else 35 | WANDCONFIG = Wand-config 36 | endif 37 | 38 | CFLAGS := $(CFLAGS) $(shell $(WANDCONFIG) --cflags) 39 | CPPFLAGS := $(CPPFLAGS) $(DEFS) $(shell $(WANDCONFIG) --cppflags) 40 | LDFLAGS := $(LDFLAGS) $(shell $(WANDCONFIG) --ldflags) 41 | LIBS := $(LIBS) $(shell $(WANDCONFIG) --libs) 42 | 43 | all: img2xterm man6/img2xterm.6.gz 44 | 45 | img2xterm: img2xterm.c 46 | $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) 47 | 48 | .PHONY: all install clean 49 | 50 | man6: 51 | mkdir man6 52 | 53 | man6/img2xterm.6.gz: man6/img2xterm.6 | man6 54 | gzip -fc $< > $@ 55 | 56 | man6/img2xterm.6: img2xterm.c | man6 img2xterm 57 | help2man -s 6 -N -m " " --version-string="git" ./img2xterm -o $@ 58 | 59 | install: img2xterm 60 | $(INSTALL) -d $(PREFIX)/bin 61 | $(INSTALL) -m 0755 img2xterm $(PREFIX)/bin/img2xterm 62 | $(LN) $(PREFIX)/bin/img2xterm $(PREFIX)/bin/img2cow 63 | $(INSTALL) -d $(PREFIX)/share/man/man6 64 | $(INSTALL) -m 0644 man6/img2xterm.6.gz $(PREFIX)/share/man/man6/img2xterm.6.gz 65 | $(LN) $(PREFIX)/share/man/man6/img2xterm.6.gz $(PREFIX)/share/man/man6/img2cow.6.gz 66 | 67 | clean: 68 | -$(RM) img2xterm man6/img2xterm.6.gz 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | img2xterm: display images on the terminal 2 | ========================================= 3 | 4 | img2xterm is a program that can display bitmap images on 256-colour terminals 5 | by converting them into Unicode block characters and xterm compatible control 6 | sequences. Based on software by [lachs0r] [1] and Xebec for creating colourful 7 | [cowfiles] [2], img2xterm improves on the colour selection and block printing 8 | logic, providing cleaner output on terminals with nice bitmap fonts. 9 | 10 | This is an example of a cowfile created with img2xterm's `--cow` option: 11 | 12 | ![Example of img2xterm in action.](http://img2xterm.sooaweso.me/demo.png) 13 | 14 | img2xterm uses a modified version of the algorithm used in [xterm256-conv] [3] 15 | in order to have an accurate representation of the upper 240 colours used in 16 | xterm. Modification was needed in order to fix the range of the grey ramp. 17 | 18 | [1]: http://srsfckn.biz/cows/img2cow.c 19 | [2]: http://www.nog.net/~tony/warez/cowsay.shtml 20 | [3]: http://frexx.de/xterm-256-notes 21 | 22 | Dependencies 23 | ------------ 24 | 25 | Before compilation, make sure you have development versions of [ImageMagick] 26 | [4] (for MagickWand) and [Ncurses] [5] (for terminfo support.) 27 | 28 | [4]: http://www.imagemagick.org 29 | [5]: http://www.gnu.org/software/ncurses/ncurses.html 30 | 31 | Getting img2xterm 32 | ----------------- 33 | 34 | The GNU Autotools are not required. To compile and install from source, 35 | simply run: 36 | 37 | $ make 38 | # make install 39 | 40 | A [GIMP] [6] palette containing the upper 240 colours used in xterm is also 41 | available. It can be used for dithering images before conversion. To install, 42 | run: 43 | 44 | $ cd extra/ 45 | $ make 46 | $ cp xterm-256color.gpl ~/.gimp-2.6/palettes/ 47 | 48 | [6]: http://www.gimp.org 49 | 50 | Converting images 51 | ----------------- 52 | 53 | To display an image on a compatible 256-color terminal: 54 | 55 | $ img2xterm image.png 56 | 57 | `img2cow` is a symlink to the `img2xterm` command. When invoked in this way, 58 | the program behaves as if the `--cow` option was used. 59 | 60 | To generate a cowfile: 61 | 62 | $ img2cow image.png image.cow 63 | # cp image.cow /usr/share/cows 64 | 65 | Known issues 66 | ------------ 67 | 68 | * There is something wrong with the implementation of [CIE94 delta-E] [8]. 69 | * ImageMagick can be glitchy. Perhaps using libpng would be a better idea. 70 | 71 | [8]: https://en.wikipedia.org/wiki/Color_difference#CIE94 72 | 73 | Copying 74 | ------- 75 | 76 | To the extent possible under law, the author(s) have dedicated all copyright 77 | and related and neighboring rights to this software to the public domain 78 | worldwide. This software is distributed without any warranty. 79 | 80 | [![CC0](http://i.creativecommons.org/p/zero/1.0/80x15.png)](http://creativecommons.org/publicdomain/zero/1.0/) 81 | -------------------------------------------------------------------------------- /extra/Makefile: -------------------------------------------------------------------------------- 1 | INSTALL = install 2 | COWDIR = /usr/share/cows 3 | 4 | ifeq ($(shell which gcc>/dev/null && echo y),y) 5 | CC = gcc 6 | CFLAGS = -Wall 7 | endif 8 | 9 | ifeq ($(shell uname), Darwin) 10 | CC = clang 11 | CFLAGS = -Wall 12 | endif 13 | 14 | all: xterm-256color.gpl cows 15 | 16 | .PHONY: all cows install-cows clean 17 | 18 | xterm-256color.gpl: xterm2gimppal 19 | ./xterm2gimppal > $@ 20 | 21 | %.cow: %.png ../img2xterm 22 | ../img2xterm -ytcw 1.5 $< $@ 23 | 24 | ../img2xterm: 25 | @echo "error: img2xterm must be compiled before the cowfiles can be made" 26 | @echo " go to the containing directory and run \"make\" first" 27 | @false 28 | 29 | cows: $(patsubst %.png,%.cow,$(wildcard cows/*.png)) 30 | 31 | install-cows: cows 32 | $(INSTALL) -D -m 644 cows/*.cow $(COWDIR) 33 | 34 | clean: 35 | -$(RM) cows/*.cow 36 | -$(RM) xterm-256color.gpl xterm2gimppal 37 | -------------------------------------------------------------------------------- /extra/cows/rmshd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossy/img2xterm/3f852c175a979983e3ee41ee905ad02613c0ceff/extra/cows/rmshd.png -------------------------------------------------------------------------------- /extra/xterm2gimppal.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const unsigned char valuerange[] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF }; 4 | 5 | void xterm2rgb(unsigned char color, unsigned char* rgb) 6 | { 7 | if (color < 232) 8 | { 9 | color -= 16; 10 | rgb[0] = valuerange[(color / 36) % 6]; 11 | rgb[1] = valuerange[(color / 6) % 6]; 12 | rgb[2] = valuerange[color % 6]; 13 | } 14 | else 15 | rgb[0] = rgb[1] = rgb[2] = 8 + (color - 232) * 10; 16 | } 17 | 18 | int main() 19 | { 20 | int i = 16; 21 | unsigned char c[3]; 22 | 23 | puts("GIMP Palette"); 24 | puts("Name: xterm-256color"); 25 | puts("Columns: 12"); 26 | puts("#"); 27 | 28 | for (; i < 256; i ++) 29 | { 30 | xterm2rgb(i, c); 31 | printf("%3d %3d %3d #%d, ", c[0], c[1], c[2], i); 32 | if (i < 232) 33 | printf("color cube (%d, %d, %d)\n", 34 | ((i - 16) / 36) % 6, ((i - 16) / 6) % 6, (i - 16) % 6); 35 | else 36 | printf("grey ramp (%d)\n", i - 232); 37 | } 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /img2xterm.c: -------------------------------------------------------------------------------- 1 | /* img2xterm - convert images to 256 color block elements for use in cowfiles 2 | * 3 | * To the extent possible under law, the author(s) have dedicated all copyright 4 | * and related and neighboring rights to this software to the public domain 5 | * worldwide. This software is distributed without any warranty. 6 | * 7 | * You should have received a copy of the CC0 Public Domain Dedication along 8 | * with this software. If not, see 9 | * . 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #ifndef NO_CURSES 21 | #include 22 | #endif 23 | 24 | #ifndef INFINITY 25 | #include 26 | #define INFINITY DBL_MAX 27 | #endif 28 | 29 | enum { 30 | color_undef, 31 | color_transparent, 32 | }; 33 | 34 | unsigned char* colortable; 35 | double* labtable; 36 | 37 | const unsigned char valuerange[] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff }; 38 | unsigned long oldfg = color_undef; 39 | unsigned long oldbg = color_undef; 40 | int perceptive = 0, cowheader; 41 | double chroma_weight = 1.0; 42 | 43 | #ifndef NO_CURSES 44 | int use_terminfo = 0; 45 | char* ti_setb; 46 | char* ti_setf; 47 | char* ti_op; 48 | #endif 49 | 50 | void srgb2lab(unsigned char red, unsigned char green, unsigned char blue, double* lightness, double* aa, double* bb) 51 | { 52 | double r = (double)red / 255.0; 53 | double g = (double)green / 255.0; 54 | double b = (double)blue / 255.0; 55 | 56 | double rl = r <= 0.4045 ? r / 12.92 : pow((r + 0.055) / 1.055, 2.4); 57 | double gl = g <= 0.4045 ? g / 12.92 : pow((g + 0.055) / 1.055, 2.4); 58 | double bl = b <= 0.4045 ? b / 12.92 : pow((b + 0.055) / 1.055, 2.4); 59 | 60 | double x = 0.4124564 * rl + 0.3575761 * gl + 0.1804375 * bl; 61 | double y = 0.2126729 * rl + 0.7151522 * gl + 0.0721750 * bl; 62 | double z = 0.0193339 * rl + 0.1191920 * gl + 0.9503041 * bl; 63 | 64 | double xn = x / 0.95047; 65 | double yn = y; 66 | double zn = z / 1.08883; 67 | 68 | double fxn = xn > (216.0 / 24389.0) ? pow(xn, 1.0 / 3.0) 69 | : (841.0 / 108.0) * xn + (4.0 / 29.0); 70 | double fyn = yn > (216.0 / 24389.0) ? pow(yn, 1.0 / 3.0) 71 | : (841.0 / 108.0) * yn + (4.0 / 29.0); 72 | double fzn = zn > (216.0 / 24389.0) ? pow(zn, 1.0 / 3.0) 73 | : (841.0 / 108.0) * zn + (4.0 / 29.0); 74 | 75 | *lightness = 116.0 * fyn - 16.0; 76 | *aa = (500.0 * (fxn - fyn)) * chroma_weight; 77 | *bb = (200.0 * (fyn - fzn)) * chroma_weight; 78 | } 79 | 80 | void srgb2yiq(unsigned char red, unsigned char green, unsigned char blue, double* y, double* i, double* q) 81 | { 82 | double r = (double)red / 255.0; 83 | double g = (double)green / 255.0; 84 | double b = (double)blue / 255.0; 85 | 86 | *y = 0.299 * r + 0.587 * g + 0.144 * b; 87 | *i = (0.595716 * r + -0.274453 * g + -0.321263 * b) * chroma_weight; 88 | *q = (0.211456 * r + -0.522591 * g + 0.311135 * b) * chroma_weight; 89 | } 90 | 91 | double cie94(double l1, double a1, double b1, double l2, double a2, double b2) 92 | { 93 | const double kl = 1; 94 | const double k1 = 0.045; 95 | const double k2 = 0.015; 96 | 97 | double c1 = sqrt(a1 * a1 + b1 * b1); 98 | double c2 = sqrt(a2 * a2 + b2 * b2); 99 | double dl = l1 - l2; 100 | double dc = c1 - c2; 101 | double da = a1 - a2; 102 | double db = b1 - b2; 103 | double dh = sqrt(da * da + db * db - dc * dc); 104 | 105 | double t1 = dl / kl; 106 | double t2 = dc / (1 + k1 * c1); 107 | double t3 = dh / (1 + k2 * c1); 108 | 109 | return sqrt(t1 * t1 + t2 * t2 + t3 * t3); 110 | } 111 | 112 | void xterm2rgb(unsigned char color, unsigned char* rgb) 113 | { 114 | if (color < 232) 115 | { 116 | color -= 16; 117 | rgb[0] = valuerange[(color / 36) % 6]; 118 | rgb[1] = valuerange[(color / 6) % 6]; 119 | rgb[2] = valuerange[color % 6]; 120 | } 121 | else 122 | rgb[0] = rgb[1] = rgb[2] = 8 + (color - 232) * 10; 123 | } 124 | 125 | unsigned long rgb2xterm_cie94(unsigned char r, unsigned char g, unsigned char b) 126 | { 127 | unsigned long i = 16, ret = 0; 128 | double d, smallest_distance = INFINITY; 129 | double l, aa, bb; 130 | 131 | srgb2lab(r, g, b, &l, &aa, &bb); 132 | 133 | for (; i < 256; i++) 134 | { 135 | d = cie94(l, aa, bb, labtable[i * 3], labtable[i * 3 + 1], labtable[i * 3 + 2]); 136 | if (d < smallest_distance) 137 | { 138 | smallest_distance = d; 139 | ret = i; 140 | } 141 | } 142 | 143 | return ret; 144 | } 145 | 146 | unsigned long rgb2xterm_yiq(unsigned char r, unsigned char g, unsigned char b) 147 | { 148 | unsigned long i = 16, ret = 0; 149 | double d, smallest_distance = INFINITY; 150 | double y, ii, q; 151 | 152 | srgb2yiq(r, g, b, &y, &ii, &q); 153 | 154 | for (; i < 256; i++) 155 | { 156 | d = (y - labtable[i * 3]) * (y - labtable[i * 3]) + 157 | (ii - labtable[i * 3 + 1]) * (ii - labtable[i * 3 + 1]) + 158 | (q - labtable[i * 3 + 2]) * (q - labtable[i * 3 + 2]); 159 | if (d < smallest_distance) 160 | { 161 | smallest_distance = d; 162 | ret = i; 163 | } 164 | } 165 | 166 | return ret; 167 | } 168 | 169 | unsigned long rgb2xterm(unsigned char r, unsigned char g, unsigned char b) 170 | { 171 | unsigned long i = 16, d, ret = 0, smallest_distance = UINT_MAX; 172 | 173 | for (; i < 256; i++) 174 | { 175 | d = (colortable[i * 3] - r) * (colortable[i * 3] - r) + 176 | (colortable[i * 3 + 1] - g) * (colortable[i * 3 + 1] - g) + 177 | (colortable[i * 3 + 2] - b) * (colortable[i * 3 + 2] - b); 178 | if (d < smallest_distance) 179 | { 180 | smallest_distance = d; 181 | ret = i; 182 | } 183 | } 184 | 185 | return ret; 186 | } 187 | 188 | void bifurcate(FILE* file, unsigned long color1, unsigned long color2, char* bstr) 189 | { 190 | unsigned long fg = oldfg; 191 | unsigned long bg = oldbg; 192 | char* str = cowheader ? "\\N{U+2584}" : "\xe2\x96\x84"; 193 | 194 | if (color1 == color2) 195 | { 196 | bg = color1; 197 | if (bstr && bg == color_transparent) 198 | { 199 | fg = color_undef; 200 | str = bstr; 201 | } 202 | else 203 | str = " "; 204 | } 205 | else 206 | if (color2 == color_transparent) 207 | { 208 | str = cowheader ? "\\N{U+2580}" : "\xe2\x96\x80"; 209 | bg = color2; 210 | fg = color1; 211 | } 212 | else 213 | { 214 | bg = color1; 215 | fg = color2; 216 | } 217 | 218 | #ifndef NO_CURSES 219 | if (use_terminfo) 220 | { 221 | if (bg != oldbg) 222 | { 223 | if (bg == color_transparent) 224 | { 225 | fputs(ti_op, file); 226 | oldfg = color_undef; 227 | } 228 | else 229 | fputs(tparm(ti_setb, bg), file); 230 | } 231 | 232 | if (fg != oldfg) 233 | fputs(tparm(ti_setf, fg), file); 234 | } 235 | else 236 | #endif 237 | if (bg != oldbg) 238 | { 239 | if (bg == color_transparent) 240 | fputs(cowheader ? "\\e[49m" : "\e[49m", file); 241 | else 242 | fprintf(file, cowheader ? "\\e[48;5;%lum" : "\e[48;5;%lum", bg); 243 | } 244 | 245 | if (fg != oldfg) 246 | { 247 | if (fg == color_undef) 248 | fputs(cowheader ? "\\e[39m" : "\e[39m", file); 249 | else 250 | fprintf(file, cowheader ? "\\e[38;5;%lum" : "\e[38;5;%lum", fg); 251 | } 252 | 253 | oldbg = bg; 254 | oldfg = fg; 255 | 256 | fputs(str, file); 257 | } 258 | 259 | unsigned long fillrow(PixelWand** pixels, unsigned long* row, unsigned long width) 260 | { 261 | unsigned long i = 0, lastpx = 0; 262 | 263 | switch (perceptive) 264 | { 265 | case 0: 266 | for (; i < width; i ++) 267 | { 268 | if (PixelGetAlpha(pixels[i]) < 0.5) 269 | row[i] = color_transparent; 270 | else 271 | { 272 | row[i] = rgb2xterm( 273 | (unsigned long)(PixelGetRed(pixels[i]) * 255.0), 274 | (unsigned long)(PixelGetGreen(pixels[i]) * 255.0), 275 | (unsigned long)(PixelGetBlue(pixels[i]) * 255.0)); 276 | lastpx = i; 277 | } 278 | } 279 | break; 280 | case 1: 281 | for (; i < width; i ++) 282 | { 283 | if (PixelGetAlpha(pixels[i]) < 0.5) 284 | row[i] = color_transparent; 285 | else 286 | { 287 | row[i] = rgb2xterm_cie94( 288 | (unsigned long)(PixelGetRed(pixels[i]) * 255.0), 289 | (unsigned long)(PixelGetGreen(pixels[i]) * 255.0), 290 | (unsigned long)(PixelGetBlue(pixels[i]) * 255.0)); 291 | lastpx = i; 292 | } 293 | } 294 | break; 295 | case 2: 296 | for (; i < width; i ++) 297 | { 298 | if (PixelGetAlpha(pixels[i]) < 0.5) 299 | row[i] = color_transparent; 300 | else 301 | { 302 | row[i] = rgb2xterm_yiq( 303 | (unsigned long)(PixelGetRed(pixels[i]) * 255.0), 304 | (unsigned long)(PixelGetGreen(pixels[i]) * 255.0), 305 | (unsigned long)(PixelGetBlue(pixels[i]) * 255.0)); 306 | lastpx = i; 307 | } 308 | } 309 | break; 310 | } 311 | 312 | return lastpx + 1; 313 | } 314 | 315 | void usage(int ret, const char* binname) 316 | { 317 | fprintf(ret ? stderr: stdout, 318 | "\ 319 | Usage: %s [ options ] [ infile ] [ outfile ]\n\n\ 320 | Convert bitmap images to 256 color block elements suitable for display in xterm\n\ 321 | and similar terminal emulators.\n\n\ 322 | Options:\n\ 323 | -c, --cow generate a cowfile header\n\ 324 | (default behavior if invoked as img2cow)\n\ 325 | -h, --help display this message\n" 326 | #ifndef NO_CURSES 327 | "\ 328 | -i, --terminfo use terminfo to set the colors of each block\n\ 329 | " 330 | #endif 331 | "\ 332 | -l, --stem-length [length] length of the speech bubble stem when generating\n\ 333 | cowfiles (default: 4)\n\ 334 | -m, --margin [width] add a margin to the left of the image\n\ 335 | -p, --perceptive use the CIE94 color difference formula for color\n\ 336 | conversion instead of simple RGB linear distance\n\ 337 | -s, --stem-margin [width] margin for the speech bubble stem when generating\n\ 338 | cowfiles (default: 11)\n\ 339 | -t, --stem-continue continue drawing the speech bubble stem into the\n\ 340 | transparent areas of the image\n\ 341 | -w, --chroma-weight [mult] weighting for chroma in --perceptive and --yiq\n\ 342 | modes (default: 1)\n\ 343 | -y, --yiq use linear distance in the YIQ color space\n\ 344 | instead of RGB for color conversion\n\ 345 | \n\ 346 | If the output file name is omitted the image is written to stdout. If both the\n\ 347 | input file name and the output file name are missing, img2xterm will act as a\n\ 348 | filter.\n\ 349 | \n\ 350 | Examples:\n\ 351 | img2xterm -yw 2 image.png display a bitmap image on the terminal using the\n\ 352 | YIQ color space for conversion with weighted\n\ 353 | chroma\n\ 354 | img2xterm banner.png motd save a bitmap image as a text file to display\n\ 355 | later\n\ 356 | img2cow rms.png rms.cow create a cowfile (assuming img2cow is a link to\n\ 357 | img2xterm)\n\ 358 | " 359 | , binname); 360 | exit(ret); 361 | } 362 | 363 | const char* basename2(const char* string) 364 | { 365 | const char* ret = string; 366 | for (; *string; string++) 367 | #if defined(WIN32) || defined(_WIN32) 368 | if (*string == '/' || *string == '\\') 369 | #endif 370 | if (*string == '/') 371 | ret = string + 1; 372 | return ret; 373 | } 374 | 375 | int main(int argc, char** argv) 376 | { 377 | const char stdin_str[] = "-", * infile = stdin_str, * outfile_str = NULL, * binname = *argv; 378 | char c; 379 | FILE* outfile = stdout; 380 | 381 | size_t width1, width2; 382 | unsigned long i, j, * row1, * row2, color1, color2, lastpx1, lastpx2, margin = 0; 383 | 384 | int background = 0; 385 | unsigned long stemlen = 4, stemmargin = 11; 386 | 387 | MagickWand* science; 388 | PixelIterator* iterator; 389 | PixelWand** pixels; 390 | 391 | cowheader = !memcmp(basename2(binname), "img2cow", 7); 392 | 393 | while (*++argv) 394 | if (**argv == '-') 395 | { 396 | while ((c = *++*argv)) 397 | switch (c) 398 | { 399 | case '-': 400 | if (!strcmp("help", ++*argv)) 401 | usage(0, binname); 402 | else if (!strcmp("cow", *argv)) 403 | cowheader = 1; 404 | else if (!strcmp("stem-length", *argv)) 405 | { 406 | if (!*++argv || !sscanf(*argv, "%lu", &stemlen)) 407 | usage(1, binname); 408 | } 409 | else if (!strcmp("perceptive", *argv)) 410 | perceptive = 1; 411 | else if (!strcmp("margin", *argv)) 412 | { 413 | if (!*++argv || !sscanf(*argv, "%lu", &margin)) 414 | usage(1, binname); 415 | } 416 | else if (!strcmp("stem-margin", *argv)) 417 | { 418 | if (!*++argv || !sscanf(*argv, "%lu", &stemmargin)) 419 | usage(1, binname); 420 | } 421 | else if (!strcmp("stem-continue", *argv)) 422 | background = 1; 423 | #ifndef NO_CURSES 424 | else if (!strcmp("terminfo", *argv)) 425 | use_terminfo = 1; 426 | #endif 427 | else if (!strcmp("chroma-weight", *argv)) 428 | { 429 | if (!*++argv || !sscanf(*argv, "%lf", &chroma_weight)) 430 | usage(1, binname); 431 | } 432 | else if (!strcmp("yiq", *argv)) 433 | perceptive = 2; 434 | else 435 | { 436 | fprintf(stderr, "%s: unrecognised long option --%s\n", binname, *argv); 437 | usage(1, binname); 438 | } 439 | goto nextarg; 440 | case 'h': 441 | usage(0, binname); 442 | break; 443 | case 'c': 444 | cowheader = 1; 445 | break; 446 | #ifndef NO_CURSES 447 | case 'i': 448 | use_terminfo = 1; 449 | break; 450 | #endif 451 | case 'l': 452 | if (*++*argv || !*++argv || !sscanf(*argv, "%lu", &stemlen)) 453 | usage(1, binname); 454 | goto nextarg; 455 | case 'm': 456 | if (*++*argv || !*++argv || !sscanf(*argv, "%lu", &margin)) 457 | usage(1, binname); 458 | goto nextarg; 459 | case 'p': 460 | perceptive = 1; 461 | break; 462 | case 's': 463 | if (*++*argv || !*++argv || !sscanf(*argv, "%lu", &stemmargin)) 464 | usage(1, binname); 465 | goto nextarg; 466 | case 't': 467 | background = 1; 468 | break; 469 | case 'w': 470 | if (*++*argv || !*++argv || !sscanf(*argv, "%lf", &chroma_weight)) 471 | usage(1, binname); 472 | goto nextarg; 473 | case 'y': 474 | perceptive = 2; 475 | break; 476 | default: 477 | fprintf(stderr, "%s: unrecognised option -%c", binname, *--*argv); 478 | usage(1, binname); 479 | } 480 | nextarg: 481 | continue; 482 | } 483 | else if (infile == stdin_str) 484 | infile = *argv; 485 | else if (!outfile_str) 486 | outfile_str = *argv; 487 | else 488 | usage(1, binname); 489 | 490 | if (!cowheader && background == 1) 491 | background = 0; 492 | 493 | #ifndef NO_CURSES 494 | if (use_terminfo) 495 | { 496 | if (setupterm(NULL, fileno(stdout), NULL)) 497 | return 5; 498 | if (((ti_op = tigetstr("op")) == (void*)-1 && 499 | (ti_op = tigetstr("sgr0")) == (void*)-1) || 500 | (ti_setb = tigetstr("setb")) == (void*)-1 || 501 | (ti_setf = tigetstr("setf")) == (void*)-1 || 502 | !tparm(ti_setb, 255) || 503 | !tparm(ti_setf, 255)) 504 | { 505 | fprintf(stderr, 506 | "%s: terminal doesn't support required features\n", binname); 507 | return 5; 508 | } 509 | } 510 | #endif 511 | 512 | MagickWandGenesis(); 513 | atexit(MagickWandTerminus); 514 | science = NewMagickWand(); 515 | 516 | if (!MagickReadImage(science, infile)) 517 | { 518 | DestroyMagickWand(science); 519 | fprintf(stderr, "%s: couldn't open input file %s\n", binname, infile == stdin_str ? "" : infile); 520 | return 3; 521 | } 522 | 523 | if (!(iterator = NewPixelIterator(science))) 524 | { 525 | DestroyMagickWand(science); 526 | fprintf(stderr, "%s: out of memory\n", binname); 527 | return 4; 528 | } 529 | 530 | if (outfile_str && !(outfile = fopen(outfile_str, "w"))) 531 | { 532 | fprintf(stderr, "%s: couldn't open output file %s\n", binname, outfile_str); 533 | return 2; 534 | } 535 | 536 | if (perceptive) 537 | { 538 | unsigned char rgb[3]; 539 | double l, a, b; 540 | 541 | labtable = malloc(256 * 3 * sizeof(double)); 542 | for (i = 16; i < 256; i ++) 543 | { 544 | xterm2rgb(i, rgb); 545 | if (perceptive == 1) 546 | srgb2lab(rgb[0], rgb[1], rgb[2], &l, &a, &b); 547 | else 548 | srgb2yiq(rgb[0], rgb[1], rgb[2], &l, &a, &b); 549 | labtable[i * 3] = l; 550 | labtable[i * 3 + 1] = a; 551 | labtable[i * 3 + 2] = b; 552 | } 553 | } 554 | else 555 | { 556 | colortable = malloc(256 * 3 * sizeof(unsigned char)); 557 | for (i = 16; i < 256; i ++) 558 | xterm2rgb(i, colortable + i * 3); 559 | } 560 | if (cowheader) 561 | { 562 | fputs("binmode STDOUT, \":utf8\";\n$the_cow =< lastpx1) 582 | lastpx1 = lastpx2; 583 | } 584 | else 585 | row2 = NULL; 586 | 587 | for (i = 0; i < margin; i ++) 588 | if (background && i == stemmargin) 589 | bifurcate(outfile, color_transparent, color_transparent, "$thoughts"); 590 | else 591 | fputc(' ', outfile); 592 | 593 | if (background == 1 && lastpx1 < stemmargin + 1) 594 | lastpx1 = stemmargin + 1; 595 | 596 | for (i = 0; i < lastpx1; i ++) 597 | { 598 | color1 = i < width1 ? row1[i] : color_transparent; 599 | color2 = i < width2 ? row2 ? row2[i] : color_transparent : color_transparent; 600 | if (background == 1) 601 | { 602 | if (i + margin == stemmargin) 603 | { 604 | if (color1 == color_transparent && color2 == color_transparent) 605 | { 606 | bifurcate(outfile, color1, color2, "$thoughts"); 607 | continue; 608 | } 609 | else 610 | background = 0; 611 | } 612 | else if (i + margin == stemmargin + 1 && (color1 != color_transparent || color2 != color_transparent)) 613 | background = 0; 614 | } 615 | bifurcate(outfile, color1, color2, NULL); 616 | } 617 | 618 | free(row1); 619 | if (row2) 620 | free(row2); 621 | 622 | if ((pixels = PixelGetNextIteratorRow(iterator, &width1))) 623 | #ifndef NO_CURSES 624 | if (use_terminfo) 625 | { 626 | fprintf(outfile, "%s\n", ti_op); 627 | oldbg = color_transparent; 628 | oldfg = color_undef; 629 | } 630 | else 631 | #endif 632 | if (oldbg != color_transparent) 633 | { 634 | fputs(cowheader ? "\\e[49m\n" : "\e[49m\n", outfile); 635 | oldbg = color_transparent; 636 | } 637 | else 638 | fputc('\n', outfile); 639 | #ifndef NO_CURSES 640 | else if (use_terminfo) 641 | fprintf(outfile, "%s\n", ti_op); 642 | #endif 643 | else if (oldbg == color_transparent) 644 | fputs(cowheader ? "\\e[39m\n" : "\e[39m\n", outfile); 645 | else 646 | fputs(cowheader ? "\\e[39;49m\n" : "\e[39;49m\n", outfile); 647 | 648 | stemmargin ++; 649 | } 650 | 651 | if (cowheader) 652 | fputs("\nEOC\n", outfile); 653 | 654 | DestroyPixelIterator(iterator); 655 | DestroyMagickWand(science); 656 | free(colortable); 657 | if (outfile != stdout) 658 | fclose(outfile); 659 | 660 | return 0; 661 | } 662 | -------------------------------------------------------------------------------- /man6/img2xterm.6: -------------------------------------------------------------------------------- 1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4. 2 | .TH IMG2XTERM "6" "September 2011" "img2xterm git" " " 3 | .SH NAME 4 | img2xterm \- manual page for img2xterm git 5 | .SH SYNOPSIS 6 | .B img2xterm 7 | [ \fIoptions \fR] [ \fIinfile \fR] [ \fIoutfile \fR] 8 | .SH DESCRIPTION 9 | Convert bitmap images to 256 color block elements suitable for display in xterm 10 | and similar terminal emulators. 11 | .SH OPTIONS 12 | .TP 13 | \fB\-c\fR, \fB\-\-cow\fR 14 | generate a cowfile header 15 | (default behavior if invoked as img2cow) 16 | .TP 17 | \fB\-h\fR, \fB\-\-help\fR 18 | display this message 19 | .TP 20 | \fB\-i\fR, \fB\-\-terminfo\fR 21 | use terminfo to set the colors of each block 22 | .TP 23 | \fB\-l\fR, \fB\-\-stem\-length\fR [length] 24 | length of the speech bubble stem when generating 25 | cowfiles (default: 4) 26 | .TP 27 | \fB\-m\fR, \fB\-\-margin\fR [width] 28 | add a margin to the left of the image 29 | .TP 30 | \fB\-p\fR, \fB\-\-perceptive\fR 31 | use the CIE94 color difference formula for color 32 | conversion instead of simple RGB linear distance 33 | .TP 34 | \fB\-s\fR, \fB\-\-stem\-margin\fR [width] 35 | margin for the speech bubble stem when generating 36 | cowfiles (default: 11) 37 | .TP 38 | \fB\-t\fR, \fB\-\-stem\-continue\fR 39 | continue drawing the speech bubble stem into the 40 | transparent areas of the image 41 | .TP 42 | \fB\-w\fR, \fB\-\-chroma\-weight\fR [mult] 43 | weighting for chroma in \fB\-\-perceptive\fR and \fB\-\-yiq\fR 44 | modes (default: 1) 45 | .TP 46 | \fB\-y\fR, \fB\-\-yiq\fR 47 | use linear distance in the YIQ color space 48 | instead of RGB for color conversion 49 | .PP 50 | If the output file name is omitted the image is written to stdout. If both the 51 | input file name and the output file name are missing, img2xterm will act as a 52 | filter. 53 | .SH EXAMPLES 54 | .TP 55 | img2xterm \fB\-yw\fR 2 image.png 56 | display a bitmap image on the terminal using the 57 | YIQ color space for conversion with weighted 58 | chroma 59 | .TP 60 | img2xterm banner.png motd 61 | save a bitmap image as a text file to display 62 | later 63 | .TP 64 | img2cow rms.png rms.cow 65 | create a cowfile (assuming img2cow is a link to 66 | img2xterm) 67 | --------------------------------------------------------------------------------