Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
gvdevice_kitty.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <assert.h>
14#include <stdbool.h>
15#include <stdio.h>
16#include <stdlib.h>
17
18#include <cgraph/alloc.h>
19#include <common/types.h>
20#include <gvc/gvio.h>
21#include <gvc/gvplugin_device.h>
22
23#ifdef HAVE_LIBZ
24#include <zlib.h>
25#endif
26
27static void fix_colors(char *imagedata, size_t imagedata_size) {
28 for (size_t i = 0; i < imagedata_size; i += 4) {
29 char blue = imagedata[i];
30 char red = imagedata[i + 2];
31 imagedata[i] = red;
32 imagedata[i + 2] = blue;
33 }
34}
35
36static size_t div_up(size_t dividend, size_t divisor) {
37 return dividend / divisor + (dividend % divisor != 0);
38}
39
40static const char base64_alphabet[] =
41 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
42
43static size_t base64_encoded_size(size_t original_size) {
44 return div_up(original_size, 3) * 4;
45}
46
47static char *base64_encode(const char *data, size_t size) {
48 size_t buf_i = 0;
49 size_t data_i = 0;
50 char *buf = gv_alloc(base64_encoded_size(size));
51
52 while (data_i < size) {
53 int v;
54
55 unsigned char d0 = data[data_i];
56 v = (d0 & 0xFC) >> 2; // 1111_1100
57 buf[buf_i++] = base64_alphabet[v];
58
59 unsigned char d1 = data_i + 1 < size ? data[data_i + 1] : 0;
60 v = (d0 & 0x03) << 4 // 0000_0011
61 | (d1 & 0xF0) >> 4; // 1111_0000
62 buf[buf_i++] = base64_alphabet[v];
63 if (size <= data_i + 1) {
64 goto end;
65 }
66
67 unsigned char d2 = data_i + 2 < size ? data[data_i + 2] : 0;
68 v = (d1 & 0x0F) << 2 // 0000_1111
69 | (d2 & 0xC0) >> 6; // 1100_0000
70 buf[buf_i++] = base64_alphabet[v];
71 if (size <= data_i + 2) {
72 goto end;
73 }
74
75 v = d2 & 0x3F; // 0011_1111
76 buf[buf_i++] = base64_alphabet[v];
77
78 data_i += 3;
79 }
80
81end:
82 while (buf_i % 4 != 0) {
83 buf[buf_i++] = base64_alphabet[64];
84 }
85
86 return buf;
87}
88
89static void kitty_write(char *data, size_t data_size, int width, int height,
90 bool is_compressed) {
91 const size_t chunk_size = 4096;
92 char *output = base64_encode(data, data_size);
93 size_t offset = 0;
94 size_t size = base64_encoded_size(data_size);
95
96 while (offset < size) {
97 int has_next_chunk = offset + chunk_size <= size;
98 if (offset == 0) {
99 printf("\033_Ga=T,f=32,s=%d,v=%d%s%s;", width, height,
100 chunk_size < size ? ",m=1" : "", is_compressed ? ",o=z" : "");
101 } else {
102 printf("\033_Gm=%d;", has_next_chunk);
103 }
104
105 size_t this_chunk_size = has_next_chunk ? chunk_size : size - offset;
106 fwrite(output + offset, this_chunk_size, 1, stdout);
107 printf("\033\\");
108 offset += chunk_size;
109 }
110 printf("\n");
111
112 free(output);
113}
114
115static void kitty_format(GVJ_t *job) {
116 char *imagedata = job->imagedata;
117 size_t imagedata_size = job->width * job->height * 4;
118 fix_colors(imagedata, imagedata_size);
119
120 kitty_write(imagedata, imagedata_size, job->width, job->height, false);
121}
122
124 GVDEVICE_DOES_TRUECOLOR, /* flags */
125 {0., 0.}, /* default margin - points */
126 {0., 0.}, /* default page width, height - points */
127 {96., 96.}, /* dpi */
128};
129
131
132#ifdef HAVE_LIBZ
133static int zlib_compress(char *source, size_t source_len, char **dest,
134 size_t *dest_len, int level) {
135 int ret;
136
137 z_stream strm;
138 strm.zalloc = Z_NULL;
139 strm.zfree = Z_NULL;
140 strm.opaque = Z_NULL;
141 ret = deflateInit(&strm, level);
142 if (ret != Z_OK) {
143 return ret;
144 }
145
146 size_t dest_cap = deflateBound(&strm, source_len);
147 *dest = gv_alloc(dest_cap);
148
149 strm.avail_in = source_len;
150 strm.next_in = (Bytef *)source;
151 strm.next_out = (Bytef *)*dest;
152 strm.avail_out = dest_cap;
153
154 ret = deflate(&strm, Z_FINISH);
155 assert(strm.avail_in == 0);
156 assert(ret == Z_STREAM_END);
157
158 *dest_len = dest_cap - strm.avail_out;
159
160 (void)deflateEnd(&strm);
161 return Z_OK;
162}
163
164static void zkitty_format(GVJ_t *job) {
165 char *imagedata = job->imagedata;
166 size_t imagedata_size = job->width * job->height * 4;
167 fix_colors(imagedata, imagedata_size);
168
169 char *zbuf;
170 size_t zsize;
171 int ret = zlib_compress(imagedata, imagedata_size, &zbuf, &zsize, -1);
172 assert(ret == Z_OK);
173 (void)ret;
174
175 kitty_write(zbuf, zsize, job->width, job->height, true);
176
177 free(zbuf);
178}
179
180static gvdevice_features_t device_features_zkitty = {
181 GVDEVICE_DOES_TRUECOLOR, /* flags */
182 {0., 0.}, /* default margin - points */
183 {0., 0.}, /* default page width, height - points */
184 {96., 96.}, /* dpi */
185};
186
187static gvdevice_engine_t device_engine_zkitty = {.format = zkitty_format};
188#endif
189
191 {0, "kitty:cairo", 0, &device_engine_kitty, &device_features_kitty},
192#ifdef HAVE_LIBZ
193 {1, "kittyz:cairo", 1, &device_engine_zkitty, &device_features_zkitty},
194#endif
195 {0, NULL, 0, NULL, NULL}};
Memory allocation wrappers that exit on failure.
static void * gv_alloc(size_t size)
Definition alloc.h:47
void free(void *)
node NULL
Definition grammar.y:149
swig_ptr_object_handlers offset
Definition gv_php.cpp:5915
#define GVDEVICE_DOES_TRUECOLOR
Definition gvcjob.h:90
static void kitty_format(GVJ_t *job)
static gvdevice_engine_t device_engine_kitty
static void fix_colors(char *imagedata, size_t imagedata_size)
static size_t base64_encoded_size(size_t original_size)
static size_t div_up(size_t dividend, size_t divisor)
static void kitty_write(char *data, size_t data_size, int width, int height, bool is_compressed)
static const char base64_alphabet[]
gvplugin_installed_t gvdevice_types_kitty[]
static char * base64_encode(const char *data, size_t size)
static gvdevice_features_t device_features_kitty
char * imagedata
Definition gvcjob.h:297
unsigned int width
Definition gvcjob.h:327
unsigned int height
Definition gvcjob.h:328
Definition legal.c:50
void(* format)(GVJ_t *firstjob)
ingroup plugin_api
Definition gvplugin.h:35
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t