• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2012  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Behdad Esfahbod
25  */
26 
27 #ifndef HELPER_CAIRO_ANSI_HH
28 #define HELPER_CAIRO_ANSI_HH
29 
30 #include "hb.hh"
31 
32 #include <cairo.h>
33 
34 #include "ansi-print.hh"
35 
36 #ifdef HAVE_CHAFA
37 # include <chafa.h>
38 
39 /* Similar to ansi-print.cc */
40 # define CELL_W 8
41 # define CELL_H (2 * CELL_W)
42 
43 static void
chafa_print_image_rgb24(const void * data,int width,int height,int stride,int level)44 chafa_print_image_rgb24 (const void *data, int width, int height, int stride, int level)
45 {
46   ChafaTermInfo *term_info;
47   ChafaSymbolMap *symbol_map;
48   ChafaCanvasConfig *config;
49   ChafaCanvas *canvas;
50   GString *gs;
51   unsigned int cols = (width +  CELL_W - 1) / CELL_W;
52   unsigned int rows = (height + CELL_H - 1) / CELL_H;
53   gchar **environ;
54   ChafaCanvasMode mode;
55   ChafaPixelMode pixel_mode;
56 
57   /* Adapt to terminal; use sixels if available, and fall back to symbols
58    * with as many colors as are supported */
59 
60   environ = g_get_environ ();
61   term_info = chafa_term_db_detect (chafa_term_db_get_default (),
62                                     environ);
63 
64   pixel_mode = CHAFA_PIXEL_MODE_SYMBOLS;
65 
66   if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_BEGIN_SIXELS))
67   {
68     pixel_mode = CHAFA_PIXEL_MODE_SIXELS;
69     mode = CHAFA_CANVAS_MODE_TRUECOLOR;
70   }
71 //  else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_DIRECT))
72 //    mode = CHAFA_CANVAS_MODE_TRUECOLOR;
73   else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_256))
74     mode = CHAFA_CANVAS_MODE_INDEXED_240;
75   else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_16))
76     mode = CHAFA_CANVAS_MODE_INDEXED_16;
77   else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_INVERT_COLORS))
78     mode = CHAFA_CANVAS_MODE_FGBG_BGFG;
79   else
80     mode = CHAFA_CANVAS_MODE_FGBG;
81 
82   /* Create the configuration */
83 
84   symbol_map = chafa_symbol_map_new ();
85   chafa_symbol_map_add_by_tags (symbol_map,
86                                 (ChafaSymbolTags) (CHAFA_SYMBOL_TAG_BLOCK
87                                                    | CHAFA_SYMBOL_TAG_SPACE
88 						   | (level >= 2 ? CHAFA_SYMBOL_TAG_WEDGE : 0)
89 						   | (level >= 3 ? CHAFA_SYMBOL_TAG_ALL : 0)
90 				));
91 
92   config = chafa_canvas_config_new ();
93   chafa_canvas_config_set_canvas_mode (config, mode);
94   chafa_canvas_config_set_pixel_mode (config, pixel_mode);
95   chafa_canvas_config_set_cell_geometry (config, CELL_W, CELL_H);
96   chafa_canvas_config_set_geometry (config, cols, rows);
97   chafa_canvas_config_set_symbol_map (config, symbol_map);
98   chafa_canvas_config_set_color_extractor (config, CHAFA_COLOR_EXTRACTOR_MEDIAN);
99   chafa_canvas_config_set_work_factor (config, 1.0f);
100 
101   /* Create canvas, draw to it and render output string */
102 
103   canvas = chafa_canvas_new (config);
104   chafa_canvas_draw_all_pixels (canvas,
105                                 /* Cairo byte order is host native */
106                                 G_BYTE_ORDER == G_LITTLE_ENDIAN
107                                   ? CHAFA_PIXEL_BGRA8_PREMULTIPLIED
108                                   : CHAFA_PIXEL_ARGB8_PREMULTIPLIED,
109                                 (const guint8 *) data,
110                                 width,
111                                 height,
112                                 stride);
113   gs = chafa_canvas_print (canvas, term_info);
114 
115   /* Print the string */
116 
117   fwrite (gs->str, sizeof (char), gs->len, stdout);
118 
119   if (pixel_mode != CHAFA_PIXEL_MODE_SIXELS)
120     fputc ('\n', stdout);
121 
122   /* Free resources */
123 
124   g_string_free (gs, TRUE);
125   chafa_canvas_unref (canvas);
126   chafa_canvas_config_unref (config);
127   chafa_symbol_map_unref (symbol_map);
128   chafa_term_info_unref (term_info);
129   g_strfreev (environ);
130 }
131 
132 #endif /* HAVE_CHAFA */
133 
134 static inline cairo_status_t
helper_cairo_surface_write_to_ansi_stream(cairo_surface_t * surface,cairo_write_func_t write_func,void * closure)135 helper_cairo_surface_write_to_ansi_stream (cairo_surface_t	*surface,
136 					   cairo_write_func_t	write_func,
137 					   void			*closure)
138 {
139   unsigned int width = cairo_image_surface_get_width (surface);
140   unsigned int height = cairo_image_surface_get_height (surface);
141   if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) {
142     cairo_surface_t *new_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
143     cairo_t *cr = cairo_create (new_surface);
144     if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_A8) {
145       cairo_set_source_rgb (cr, 0., 0., 0.);
146       cairo_paint (cr);
147       cairo_set_source_rgb (cr, 1., 1., 1.);
148       cairo_mask_surface (cr, surface, 0, 0);
149     } else {
150       cairo_set_source_rgb (cr, 1., 1., 1.);
151       cairo_paint (cr);
152       cairo_set_source_surface (cr, surface, 0, 0);
153       cairo_paint (cr);
154     }
155     cairo_destroy (cr);
156     surface = new_surface;
157   } else
158     cairo_surface_reference (surface);
159 
160   unsigned int stride = cairo_image_surface_get_stride (surface);
161   const uint32_t *data = (uint32_t *) (void *) cairo_image_surface_get_data (surface);
162 
163   /* We don't have rows to spare on the terminal window...
164    * Find the tight image top/bottom and only print in between. */
165 
166   /* Use corner color as background color. */
167   uint32_t bg_color = data ? * (uint32_t *) data : 0;
168 
169   /* Drop first row while empty */
170   auto orig_data = data;
171   while (height)
172   {
173     unsigned int i;
174     for (i = 0; i < width; i++)
175       if (data[i] != bg_color)
176 	break;
177     if (i < width)
178       break;
179     data += stride / 4;
180     height--;
181   }
182   if (orig_data < data)
183   {
184     data -= stride / 4;
185     height++; /* Add one first blank row for padding. */
186   }
187 
188   /* Drop last row while empty */
189   auto orig_height = height;
190   while (height)
191   {
192     const uint32_t *row = data + (height - 1) * stride / 4;
193     unsigned int i;
194     for (i = 0; i < width; i++)
195       if (row[i] != bg_color)
196 	break;
197     if (i < width)
198       break;
199     height--;
200   }
201   if (height < orig_height)
202     height++; /* Add one last blank row for padding. */
203 
204   if (width && height)
205   {
206 #ifdef HAVE_CHAFA
207     const char *env = getenv ("HB_CHAFA");
208     int chafa_level = 1;
209     if (env)
210       chafa_level = atoi (env);
211     if (chafa_level)
212       chafa_print_image_rgb24 (data, width, height, stride, chafa_level);
213     else
214 #endif
215       ansi_print_image_rgb24 (data, width, height, stride / 4);
216   }
217 
218   cairo_surface_destroy (surface);
219   return CAIRO_STATUS_SUCCESS;
220 }
221 
222 
223 #endif
224