Graphviz 16.0.0~dev.20260810.2334
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 <stdint.h>
17#include <util/gv_math.h>
18
20static uint8_t rgb_to_grayscale(unsigned red, unsigned green, unsigned blue) {
21
24
25 const double r_linear = red / 255.0;
26 const double g_linear = green / 255.0;
27 const double b_linear = blue / 255.0;
28
29 const double y_linear =
30 0.2126 * r_linear + 0.7152 * g_linear + 0.0722 * b_linear;
31 return (uint8_t)(y_linear * 255.999);
32}
33
35static bool is_space(const unsigned char *base, size_t size) {
36 assert(base != NULL || size == 0);
37 // essentially the inverse of memchr, `memcchr(base, ' ', size)`
38 for (size_t i = 0; i < size; ++i) {
39 if (base[i] != ' ') {
40 return false;
41 }
42 }
43 return true;
44}
45
46static void process(GVJ_t *job) {
47 assert(job != NULL);
48
49 assert(job->width <= INT_MAX);
50 const int width = (int)job->width;
51 assert(job->height <= INT_MAX);
52 const int height = (int)job->height;
53
54 // initialize an in-memory device of the dimensions of our image
55 // XXX: Reading the AA-lib docs, one might be led to believe we could simply
56 // call `aa_autoinit` and render to the current terminal. However if you
57 // attempt this AA-lib, despite seeming to read and understand the terminal
58 // dimensions, renders something of the wrong dimensions. To work around this,
59 // we render to an in-memory buffer and then strip the surrounding excess
60 // space when doing our own rendering.
61 aa_hardwareparams params = aa_defparams;
62 params.width = width;
63 params.height = height;
64 aa_context *ctx = aa_init(&mem_d, &params, NULL);
65 if (ctx == NULL) {
66 agerrorf("failed to initialized AA-lib\n");
67 return;
68 }
69
70 // draw the image
71 const unsigned char *const data = job->imagedata;
72 for (unsigned y = 0; y < job->height; ++y) {
73 for (unsigned x = 0; x < job->width; ++x) {
74
75 // extract the pixel data
76 const unsigned offset =
77 y * job->width * BYTES_PER_PIXEL + x * BYTES_PER_PIXEL;
78 const unsigned red = data[offset + 2];
79 const unsigned green = data[offset + 1];
80 const unsigned blue = data[offset];
81
82 const uint8_t gray = rgb_to_grayscale(red, green, blue);
83
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:94
static gvplugin_installed_t device_types[]
static uint8_t rgb_to_grayscale(unsigned red, unsigned green, unsigned blue)
convert an RGB color to grayscale
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
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