Graphviz 14.1.4~dev.20260320.0055
Loading...
Searching...
No Matches
gvplugin_ascii.c
Go to the documentation of this file.
1
6
7#include "config.h"
8
9#include <aalib.h>
10#include <assert.h>
11#include <gvc/gvplugin.h>
12#include <gvc/gvplugin_device.h>
13#include <limits.h>
14#include <stdbool.h>
15#include <stddef.h>
16#include <util/gv_math.h>
17
19static int rgb_to_grayscale(unsigned red, unsigned green, unsigned blue) {
20
23
24 const double r_linear = red / 255.0;
25 const double g_linear = green / 255.0;
26 const double b_linear = blue / 255.0;
27
28 const double y_linear =
29 0.2126 * r_linear + 0.7152 * g_linear + 0.0722 * b_linear;
30 return (int)(y_linear * 255.999);
31}
32
34static bool is_space(const unsigned char *base, size_t size) {
35 assert(base != NULL || size == 0);
36 // essentially the inverse of memchr, `memcchr(base, ' ', size)`
37 for (size_t i = 0; i < size; ++i) {
38 if (base[i] != ' ') {
39 return false;
40 }
41 }
42 return true;
43}
44
45static void process(GVJ_t *job) {
46 assert(job != NULL);
47
48 assert(job->width <= INT_MAX);
49 const int width = (int)job->width;
50 assert(job->height <= INT_MAX);
51 const int height = (int)job->height;
52
53 // initialize an in-memory device of the dimensions of our image
54 // XXX: Reading the AA-lib docs, one might be led to believe we could simply
55 // call `aa_autoinit` and render to the current terminal. However if you
56 // attempt this AA-lib, despite seeming to read and understand the terminal
57 // dimensions, renders something of the wrong dimensions. To work around this,
58 // we render to an in-memory buffer and then strip the surrounding excess
59 // space when doing our own rendering.
60 aa_hardwareparams params = aa_defparams;
61 params.width = width;
62 params.height = height;
63 aa_context *ctx = aa_init(&mem_d, &params, NULL);
64 if (ctx == NULL) {
65 agerrorf("failed to initialized AA-lib\n");
66 return;
67 }
68
69 // draw the image
70 const unsigned char *const data = job->imagedata;
71 for (unsigned y = 0; y < job->height; ++y) {
72 for (unsigned x = 0; x < job->width; ++x) {
73
74 // extract the pixel data
75 const unsigned offset =
76 y * job->width * BYTES_PER_PIXEL + x * BYTES_PER_PIXEL;
77 const unsigned red = data[offset + 2];
78 const unsigned green = data[offset + 1];
79 const unsigned blue = data[offset];
80
81 const int gray = rgb_to_grayscale(red, green, blue);
82
83 assert(gray >= 0 && gray < 256);
84 aa_putpixel(ctx, (int)x, (int)y, gray);
85 }
86 }
87
88 // render the image in memory
89 aa_fastrender(ctx, 0, 0, width, height);
90 aa_flush(ctx);
91
92 // now render it from there to stdout, accounting for the quirk of the wrong
93 // dimensions as discussed above
94 const unsigned char *const text = aa_text(ctx);
95 const size_t size = job->height * job->width;
96 for (size_t y = 0; y < job->height; ++y) {
97 // stop if we are into the lower excess white space
98 if (is_space(&text[y * job->width], size - y * job->width)) {
99 break;
100 }
101 for (size_t x = 0; x < job->width; ++x) {
102 // stop if we are into the right excess white space
103 if (is_space(&text[y * job->width + x], job->width - x)) {
104 break;
105 }
106 printf("%c", (char)text[y * job->width + x]);
107 }
108 printf("\n");
109 }
110
111 aa_close(ctx);
112}
113
115 .format = process,
116};
117
119 .default_dpi = {96, 96},
120};
121
123 {1, "ascii:cairo", 0, &engine, &device_features},
124 {0},
125};
126
127static gvplugin_api_t apis[] = {{API_device, device_types}, {0}};
128
129#ifdef GVDLL
130#define GVPLUGIN_ASCII_API __declspec(dllexport)
131#else
132#define GVPLUGIN_ASCII_API
133#endif
134
node NULL
Definition grammar.y:181
void agerrorf(const char *fmt,...)
Definition agerror.c:167
Arithmetic helper functions.
@ BYTES_PER_PIXEL
Definition gv_math.h:91
static gvplugin_installed_t device_types[]
static gvdevice_features_t device_features
static void process(GVJ_t *job)
GVPLUGIN_ASCII_API gvplugin_library_t gvplugin_ascii_LTX_library
static bool is_space(const unsigned char *base, size_t size)
does the given range only contain space characters?
#define GVPLUGIN_ASCII_API
static gvplugin_api_t apis[]
static gvdevice_engine_t engine
static int rgb_to_grayscale(unsigned red, unsigned green, unsigned blue)
convert an RGB color to grayscale
unsigned char * imagedata
location of imagedata
Definition gvcjob.h:297
unsigned int width
Definition gvcjob.h:327
unsigned int height
Definition gvcjob.h:328
void(* format)(GVJ_t *firstjob)
ingroup plugin_api
Definition gvplugin.h:35