diff options
Diffstat (limited to 'src/pnm.c')
-rw-r--r-- | src/pnm.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/src/pnm.c b/src/pnm.c new file mode 100644 index 0000000..da9dd3a --- /dev/null +++ b/src/pnm.c @@ -0,0 +1,118 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "common.h" +#include "pnm.h" +#include "kern.h" + + + +#define LIST_0_9(P) P"0\n", P"1\n", P"2\n", P"3\n", P"4\n", P"5\n", P"6\n", P"7\n", P"8\n", P"9\n" +/** + * [0, 255]-integer-to-text convertion lookup table for faster conversion from + * raw framebuffer data to the PNM format. The values have a whitespace at the + * end for even faster conversion. + * Lines should not be longer than 70 (although most programs will probably + * work even if there are longer lines), therefore the selected whitespace + * is LF (new line). + * + * ASCII has wider support than binary, and is create for version control, + * especifially with one datum per line. + */ +const char* inttable[] = + { + LIST_0_9(""), LIST_0_9("1"), LIST_0_9("2"), LIST_0_9("3"), LIST_0_9("4"), + LIST_0_9("5"), LIST_0_9("6"), LIST_0_9("7"), LIST_0_9("8"), LIST_0_9("9"), + + LIST_0_9("10"), LIST_0_9("11"), LIST_0_9("12"), LIST_0_9("13"), LIST_0_9("14"), + LIST_0_9("15"), LIST_0_9("16"), LIST_0_9("17"), LIST_0_9("18"), LIST_0_9("19"), + + LIST_0_9("20"), LIST_0_9("21"), LIST_0_9("22"), LIST_0_9("23"), LIST_0_9("24"), + "250\n", "251\n", "252\n", "253\n", "254\n", "255\n" + }; + + + +/** + * Create an PNM file. + * + * @param fbfd The file descriptor connected to framebuffer device. + * @param width The width of the image. + * @param height The height of the image. + * @param imgfd The file descriptor connected to conversion process's stdin. + * @return Zero on success, -1 on error. + */ +int +save_pnm (int fbfd, long width, long height, int imgfd) +{ + char buf[8 << 10]; + FILE *file = NULL; + ssize_t got, off; + size_t adjustment; + int saved_errno; + + /* Create a FILE *, for writing, for the image file. */ + file = fdopen (imgfd, "w"); + if (file == NULL) + goto fail; + + /* The PNM image should begin with `P3\n%{width} %{height}\n%{colour max=255}\n`. + ('\n' and ' ' can be exchanged at will.) */ + if (fprintf (file, "P3\n%li %li\n255\n", width, height) < 0) + goto fail; + + /* Convert raw framebuffer data into a PNM image. */ + for (off = 0;;) + { + /* Read data from the framebuffer, we may have up to 3 bytes buffered. */ + got = read (fbfd, buf + off, sizeof (buf) - (size_t)off * sizeof (char)); + if (got < 0) + goto fail; + if (got == 0) + break; + got += off; + + /* Convert read pixels. */ + if (convert_fb_to_pnm (file, buf, (size_t)got, &adjustment) < 0) + goto fail; + + /* If we read a whole number of pixels, reset the buffer, otherwise, + move the unconverted bytes to the beginning of the buffer. */ + if (adjustment) + { + off -= (ssize_t)adjustment; + memcpy (buf, buf + off, (size_t)(got - off) * sizeof (char)); + off = got - off; + } + else + off = 0; + } + + /* Close file and return successfully. */ + fflush (file); + fclose (file); + return 0; + + fail: + saved_errno = errno; + if (file != NULL) + fclose (file); + errno = saved_errno; + return -1; +} + |