diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/kern-linux.c | 54 | ||||
-rw-r--r-- | src/kern.h | 31 | ||||
-rw-r--r-- | src/png.c | 106 | ||||
-rw-r--r-- | src/png.h | 46 | ||||
-rw-r--r-- | src/pnm.c | 118 | ||||
-rw-r--r-- | src/pnm.h | 17 | ||||
-rw-r--r-- | src/scrotty.c | 194 |
7 files changed, 323 insertions, 243 deletions
diff --git a/src/kern-linux.c b/src/kern-linux.c index fdae636..00930f1 100644 --- a/src/kern-linux.c +++ b/src/kern-linux.c @@ -19,6 +19,7 @@ #include "common.h" #include "kern.h" #include "pnm.h" +#include "png.h" @@ -128,8 +129,8 @@ measure (int fbno, char *restrict fbpath, long *restrict width, long *restrict h * @return Zero on success, -1 on error. */ int -convert_fb (FILE *restrict file, const char *restrict buf, - size_t n, size_t *restrict adjustment) +convert_fb_to_pnm (FILE *restrict file, const char *restrict buf, + size_t n, size_t *restrict adjustment) { const uint32_t *restrict pixel; int r, g, b; @@ -144,7 +145,7 @@ convert_fb (FILE *restrict file, const char *restrict buf, g = (*pixel >> 8) & 255; b = (*pixel >> 0) & 255; - if (SAVE_PIXEL (file, r, g, b) < 0) + if (SAVE_PNM_PIXEL (file, r, g, b) < 0) goto fail; } @@ -154,3 +155,50 @@ convert_fb (FILE *restrict file, const char *restrict buf, return -1; } + +/** + * Convert read data from a framebuffer to PNG pixel data. + * + * @param file The output image file. + * @param buf Buffer with read data. + * @param n The number of read characters. + * @param width3 The width of the image multipled by 3. + * @param adjustment Set to zero if all bytes were converted + * (a whole number of pixels where available,) + * otherwise, set to the number of bytes a + * pixel is encoded. + * @param state Use this to keep track of where in the you + * stopped. It will be 0 on the first call. + * @return Zero on success, -1 on error. + */ +int convert_fb_to_png (png_struct *pngbuf, png_byte *restrict pixbuf, const char *restrict buf, + size_t n, long width3, size_t *restrict adjustment, long *restrict state) +{ + const uint32_t *restrict pixel; + int r, g, b; + size_t off; + long x3 = *state; + + for (off = 0; off < n; off += 4) + { + /* A pixel in the framebuffer is formatted as `%{blue}%{green}%{red}%{x}` + in big-endian binary, or `%{x}%{red}%{green}%{blue}` in little-endian binary. */ + pixel = (const uint32_t *)(buf + off); + r = (*pixel >> 16) & 255; + g = (*pixel >> 8) & 255; + b = (*pixel >> 0) & 255; + + SAVE_PNG_PIXEL (pixbuf, x3, r, g, b); + x3 += 3; + if (x3 == width3) + { + SAVE_PNG_ROW (pngbuf, pixbuf); + x3 = 0; + } + } + + *adjustment = (off != n ? 4 : 0); + *state = x3; + return 0; +} + @@ -16,6 +16,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpadded" +#endif +#include <png.h> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + /** @@ -65,6 +74,24 @@ int measure (int fbno, char *restrict fbpath, long *restrict width, long *restri * pixel is encoded. * @return Zero on success, -1 on error. */ -int convert_fb (FILE *restrict file, const char *restrict buf, - size_t n, size_t *restrict adjustment); +int convert_fb_to_pnm (FILE *restrict file, const char *restrict buf, + size_t n, size_t *restrict adjustment); + +/** + * Convert read data from a framebuffer to PNG pixel data. + * + * @param file The output image file. + * @param buf Buffer with read data. + * @param n The number of read characters. + * @param width3 The width of the image multipled by 3. + * @param adjustment Set to zero if all bytes were converted + * (a whole number of pixels where available,) + * otherwise, set to the number of bytes a + * pixel is encoded. + * @param state Use this to keep track of where in the you + * stopped. It will be 0 on the first call. + * @return Zero on success, -1 on error. + */ +int convert_fb_to_png (png_struct *pngbuf, png_byte *restrict pixbuf, const char *restrict buf, + size_t n, long width3, size_t *restrict adjustment, long *restrict state); @@ -18,11 +18,7 @@ */ #include "common.h" #include "png.h" - -#ifdef __GNUC__ -# pragma GCC diagnostic ignored "-Wpadded" -#endif -#include <png.h> +#include "kern.h" /* @@ -30,50 +26,37 @@ * * Users want their files in PNG (most often), not PNM. * Calling an external program for conversion poses - * potential security risks, and is slower. + * potential security risks, and is significantly slower. */ /** - * Convert an image from PNM to PNG. - * - * This is a very limited conversion implementation. - * It is not generally reusable. It makes assumptions - * that are true only necessarily for this program. + * Create an PNG file. * - * @param fdin The file descriptor for the image to convert (PNM). - * @param fdout The file descriptor for the output image (PNG). - * @return Zero on success, -1 on error. + * @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 -convert (int fdin, int fdout) +save_png (int fbfd, long width, long height, int imgfd) { -#define SCAN(...) do { if (fscanf(in, __VA_ARGS__) != 1) goto fail; } while (0) - - FILE *in = NULL; - FILE *out = NULL; - long height, width, width3; - long y, x, r, g, b; - png_byte *pixbuf = 0; + char buf[8 << 10]; + FILE *file = NULL; + ssize_t got, off; + size_t adjustment; + png_byte *restrict pixbuf = NULL; png_struct *pngbuf = NULL; png_info *pnginfo = NULL; - int saved_errno = 0, rc, c; + long width3, state = 0; + int rc, saved_errno; - /* Get a FILE * for the input, we want to use fscanf, there is no dscanf. */ - in = fdopen (fdin, "r"); - if (in == NULL) - goto fail; /* Get a FILE * for the output, libpng wants a FILE *, not a file descriptor. */ - out = fdopen (fdout, "w"); - if (out == NULL) + file = fdopen (imgfd, "w"); + if (file == NULL) goto fail; - /* Read head. */ - do c = fgetc(in); while ((c != '\n') && (c != EOF)); - SCAN ("%li ", &width); - SCAN ("%li\n", &height); - do c = fgetc(in); while ((c != '\n') && (c != EOF)); - /* Allocte structures for the PNG. */ width3 = width * 3; pixbuf = malloc ((size_t)width3 * sizeof (png_byte)); @@ -89,28 +72,45 @@ convert (int fdin, int fdout) /* Initialise PNG write, and write head. */ if (setjmp (png_jmpbuf(pngbuf))) /* Failing libpng calls jump here. */ goto fail; - png_init_io (pngbuf, out); + png_init_io (pngbuf, file); png_set_IHDR (pngbuf, pnginfo, (png_uint_32)width, (png_uint_32)height, 8 /* bit depth */, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info (pngbuf, pnginfo); - /* Read and convert body. */ - for (y = 0; y < height; y++) + /* TODO (maybe) The image shall be packed. That is, if 24 bits per pixel is + * unnecessary, less shall be used. 6 bits is often sufficient. */ + + /* Convert raw framebuffer data into a PNG image (body). */ + for (off = 0;;) { - for (x = 0; x < width3; x += 3) + /* 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_png (pngbuf, pixbuf, buf, (size_t)got, + width3, &adjustment, &state) < 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) { - SCAN ("%li\n", &r); - SCAN ("%li\n", &g); - SCAN ("%li\n", &b); - pixbuf[x + 0] = (png_byte)r; - pixbuf[x + 1] = (png_byte)g; - pixbuf[x + 2] = (png_byte)b; + off -= (ssize_t)adjustment; + memcpy (buf, buf + off, (size_t)(got - off) * sizeof (char)); + off = got - off; } - png_write_row (pngbuf, pixbuf); + else + off = 0; } - png_write_end (pngbuf, pnginfo); + /* Done! */ + png_write_end (pngbuf, pnginfo); rc = 0; goto cleanup; fail: @@ -120,11 +120,13 @@ convert (int fdin, int fdout) cleanup: png_destroy_write_struct (&pngbuf, (pnginfo ? &pnginfo : NULL)); - if (in != NULL) - fclose (in); - if (out != NULL) - fclose (out); + if (file != NULL) + { + fflush (file); + fclose (file); + } free (pixbuf); - return errno = saved_errno, rc; + errno = saved_errno; + return rc; } @@ -16,14 +16,50 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpadded" +#endif +#include <png.h> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + + + +/** + * Store a pixel to a PNG row buffer. + * + * @param PIXBUF:png_byte * The pixel buffer for the row. + * @param X3:long The column of the pixel multipled by 3. + * @param R:int The [0, 255]-value on the red subpixel. + * @param G:int The [0, 255]-value on the green subpixel. + * @param B:int The [0, 255]-value on the blue subpixel. + */ +#define SAVE_PNG_PIXEL(PIXBUF, X3, R, G, B) \ + ((PIXBUF)[(X3) + 0] = (png_byte)(R), \ + (PIXBUF)[(X3) + 1] = (png_byte)(G), \ + (PIXBUF)[(X3) + 2] = (png_byte)(B)) + +/** + * Store a row to a PNG image. + * + * @param PNGBUF:png_struct * The PNG image structure. + * @param PIXBUF:png_byte * The pixel buffer for the row. + */ +#define SAVE_PNG_ROW(PNGBUF, PIXBUF) \ + png_write_row (PNGBUF, PIXBUF) /** - * Convert an image from PNM to PNG. + * Create an PNG file. * - * @param fdin The file descriptor for the image to convert (PNM). - * @param fdout The file descriptor for the output image (PNG). - * @return Zero on success, -1 on error. + * @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 convert (int fdin, int fdout); +int +save_png (int fbfd, long width, long height, int imgfd); 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; +} + @@ -16,11 +16,10 @@ * 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 <stdio.h> /** - * Store a pixel to an PNM image. + * Store a pixel to a PNM image. * * A pixel in the PNM image is formatted as `%{red} %{green} %{blue} ` in text. * @@ -30,7 +29,7 @@ * @param B:int The [0, 255]-value on the blue subpixel. * @return Positive on success (not zero!), -1 on error. */ -#define SAVE_PIXEL(F, R, G, B) \ +#define SAVE_PNM_PIXEL(F, R, G, B) \ fprintf (F, "%s%s%s", inttable[R], inttable[G], inttable[B]) @@ -47,3 +46,15 @@ */ extern const char* inttable[]; + +/** + * 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); + diff --git a/src/scrotty.c b/src/scrotty.c index c076fa4..4bc6982 100644 --- a/src/scrotty.c +++ b/src/scrotty.c @@ -42,31 +42,6 @@ #define LIST_DISPLAY_VARS X(DISPLAY) X(MDS_DISPLAY) X(MIR_DISPLAY) X(WAYLAND_DISPLAY) X(PREFERRED_DISPLAY) -#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" - }; - - /** * `argv[0]` from `main`. @@ -89,85 +64,6 @@ static int try_alt_fbpath = 0; /** - * Create an PNM-file that is sent to a conversion process, - * or directly to a file. - * - * @param fbname The framebuffer device. - * @param width The width of the image. - * @param height The height of the image. - * @param fd The file descriptor connected to conversion process's stdin. - * @return Zero on success, -1 on error. - */ -static int -save_pnm (const char *fbpath, long width, long height, int fd) -{ - char buf[8 << 10]; - FILE *file = NULL; - int fbfd = 1; - ssize_t got, off; - int saved_errno; - size_t adjustment; - - /* Open the framebuffer device for reading. */ - fbfd = open (fbpath, O_RDONLY); - if (fbfd == -1) - goto fail; - - /* Create a FILE *, for writing, for the image file. */ - file = fdopen (fd, "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 an 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 (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 files and return successfully. */ - fflush (file); - fclose (file); - close (fbfd); - return 0; - - fail: - saved_errno = errno; - if (file != NULL) - fclose (file); - if (fbfd >= 0) - close (fbfd); - errno = saved_errno; - return -1; -} - - -/** * Create an image of a framebuffer. * * @param fbname The framebuffer device. @@ -180,92 +76,34 @@ save_pnm (const char *fbpath, long width, long height, int fd) static int save (const char *fbpath, const char *imgpath, long width, long height, int raw) { - int pipe_rw[2] = { -1, -1 }; - pid_t pid; - int status; - int fd = -1; + int imgfd = -1, fbfd; int saved_errno; - if (raw) - goto no_convert; - - - /* Create a pipe that for sending data into the conversion process program. */ - if (pipe (pipe_rw) < 0) - goto fail; - - /* Fork the process, the child will become the conversion process. */ - pid = fork (); - if (pid == -1) - goto fail; - - - /* Child process: */ - if (pid == 0) - { - /* Close the write-end of the pipe. */ - close (pipe_rw[1]); - - /* Open file descriptor for the output image. */ - fd = open (imgpath, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (fd == -1) - goto child_fail; - - /* Convert the PNM-image we create to a compressed image, namely in PNG. */ - if (convert (pipe_rw[0], fd) < 0) - goto child_fail; - - _exit(0); - child_fail: - perror(execname); - _exit(1); - } - - - /* Parent process: */ - - /* Close the read-end of the pipe. */ - close (pipe_rw[0]), pipe_rw[0] = -1; - - /* Create a PNM-image of the framebuffer. */ - if (save_pnm (fbpath, width, height, pipe_rw[1]) < 0) - goto fail; - - /* Close the write-end of the pipe. */ - close (pipe_rw[1]), pipe_rw[1] = -1; - - /* Wait for conversion process to exit. */ - if (waitpid (pid, &status, 0) < 0) - goto fail; - - /* Return successfully if and only if conversion did. */ - return status == 0 ? 0 : -1; - - - /* Conversion shall not take place: */ - - no_convert: /* Open output file. */ - if (fd = open (imgpath, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), fd == -1) + imgfd = open (imgpath, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (imgfd == -1) FILE_FAILURE (imgpath); + /* Open the framebuffer device for reading. */ + fbfd = open (fbpath, O_RDONLY); + if (fbfd == -1) + FILE_FAILURE (fbpath); + /* Save image. */ - if (save_pnm (fbpath, width, height, fd) < 0) + if ((raw ? save_pnm : save_png) (fbfd, width, height, imgfd) < 0) goto fail; - close (fd); + close (fbfd); + close (imgfd); return 0; fail: saved_errno = errno; - if (pipe_rw[0] >= 0) - close (pipe_rw[0]); - if (pipe_rw[1] >= 0) - close (pipe_rw[1]); - if (fd >= 0) - close (fd); + if (fbfd >= 0) + close (fbfd); + if (imgfd >= 0) + close (imgfd); errno = saved_errno; return -1; } |