1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 /* Copyright (C) 2018-2021 Hans Petter Jansson
4  *
5  * This file is part of Chafa, a program that turns images into character art.
6  *
7  * Chafa is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published
9  * by the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Chafa is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with Chafa.  If not, see <http://www.gnu.org/licenses/>. */
19 
20 #include "config.h"
21 
22 #include "chafa.h"
23 #include "internal/chafa-private.h"
24 
25 /**
26  * SECTION:chafa-util
27  * @title: Miscellaneous
28  * @short_description: Potentially useful functions
29  *
30  * Miscellaneous functions that may be useful to Chafa users.
31  **/
32 
33 /**
34  * chafa_calc_canvas_geometry:
35  * @src_width: Width of source
36  * @src_height: Height of source
37  * @dest_width_inout: Inout location for width of destination
38  * @dest_height_inout: Inout location for height of destination
39  * @font_ratio: Target font's width to height ratio
40  * @zoom: %TRUE to upscale image to fit maximum dimensions, %FALSE otherwise
41  * @stretch: %TRUE to ignore aspect of source, %FALSE otherwise
42  *
43  * Calculates an optimal geometry for a #ChafaCanvas given the width and
44  * height of an input image, maximum width and height of the canvas, font
45  * ratio, zoom and stretch preferences.
46  *
47  * @src_width and @src_height must both be zero or greater.
48  *
49  * @dest_width_inout and @dest_height_inout must point to integers
50  * containing the maximum dimensions of the canvas in character cells.
51  * These will be replaced by the calculated values, which may be zero if
52  * one of the input dimensions is zero. If one or both of the input
53  * parameters is negative, they will be treated as unspecified and
54  * calculated based on the remaining parameters and aspect ratio.
55  *
56  * @font_ratio is the font's width divided by its height. 0.5 is a typical
57  * value.
58  **/
59 void
chafa_calc_canvas_geometry(gint src_width,gint src_height,gint * dest_width_inout,gint * dest_height_inout,gfloat font_ratio,gboolean zoom,gboolean stretch)60 chafa_calc_canvas_geometry (gint src_width,
61                             gint src_height,
62                             gint *dest_width_inout,
63                             gint *dest_height_inout,
64                             gfloat font_ratio,
65                             gboolean zoom,
66                             gboolean stretch)
67 {
68     gint dest_width = -1, dest_height = -1;
69 
70     g_return_if_fail (src_width >= 0);
71     g_return_if_fail (src_height >= 0);
72     g_return_if_fail (font_ratio > 0.0);
73 
74     if (dest_width_inout)
75         dest_width = *dest_width_inout;
76     if (dest_height_inout)
77         dest_height = *dest_height_inout;
78 
79     /* If any dimension is explicitly set to zero, width and height will
80      * both be zero. */
81     if (src_width == 0 || src_height == 0
82         || dest_width == 0 || dest_height == 0)
83     {
84         if (dest_width_inout)
85             *dest_width_inout = 0;
86         if (dest_height_inout)
87             *dest_height_inout = 0;
88         return;
89     }
90 
91     /* If both output dimensions are unspecified, make them 1/8 of their
92      * corresponding input dimensions, rounding up and accounting
93      * for font ratio. Both dimensions will be >= 1. */
94     if (dest_width < 0 && dest_height < 0)
95     {
96         if (dest_width_inout)
97         {
98             *dest_width_inout = (src_width + 7 / 8);
99             *dest_width_inout = MAX (*dest_width_inout, 1);
100         }
101 
102         if (dest_height_inout)
103         {
104             *dest_height_inout = (src_height + 7 / 8) * font_ratio + 0.5;
105             *dest_height_inout = MAX (*dest_height_inout, 1);
106         }
107 
108         return;
109     }
110 
111     if (!zoom)
112     {
113         dest_width = MIN (dest_width, src_width);
114         dest_height = MIN (dest_height, src_height);
115     }
116 
117     if (!stretch || dest_width < 0 || dest_height < 0)
118     {
119         gdouble src_aspect;
120         gdouble dest_aspect;
121 
122         src_aspect = src_width / (gdouble) src_height;
123         dest_aspect = (dest_width / (gdouble) dest_height) * font_ratio;
124 
125         if (dest_width < 1)
126         {
127             dest_width = dest_height * (src_aspect / font_ratio) + 0.5;
128         }
129         else if (dest_height < 1)
130         {
131             dest_height = (dest_width / src_aspect) * font_ratio + 0.5;
132         }
133         else if (src_aspect > dest_aspect)
134         {
135             dest_height = dest_width * (font_ratio / src_aspect);
136         }
137         else
138         {
139             dest_width = dest_height * (src_aspect / font_ratio);
140         }
141     }
142 
143     dest_width = MAX (dest_width, 1);
144     dest_height = MAX (dest_height, 1);
145 
146     if (dest_width_inout)
147         *dest_width_inout = dest_width;
148     if (dest_height_inout)
149         *dest_height_inout = dest_height;
150 }
151 
152 /* --- Internal; not part of public API --- */
153 
154 static void
fill_matrix_r(gint * matrix,gint matrix_size,gint sub_size,gint x,gint y,gint value,gint step)155 fill_matrix_r (gint *matrix, gint matrix_size, gint sub_size, gint x, gint y, gint value, gint step)
156 {
157     gint half;
158 
159     if (sub_size == 1)
160     {
161         matrix [x + y * matrix_size] = value;
162         return;
163     }
164 
165     half = sub_size / 2;
166 
167     fill_matrix_r (matrix, matrix_size, half, x,        y,        value,            step * 4);
168     fill_matrix_r (matrix, matrix_size, half, x + half, y + half, value + step,     step * 4);
169     fill_matrix_r (matrix, matrix_size, half, x + half, y,        value + step * 2, step * 4);
170     fill_matrix_r (matrix, matrix_size, half, x,        y + half, value + step * 3, step * 4);
171 }
172 
173 static void
fill_matrix(gint * matrix,gint matrix_size,gdouble magnitude)174 fill_matrix (gint *matrix, gint matrix_size, gdouble magnitude)
175 {
176     gint maxval = matrix_size * matrix_size;
177     gdouble maxval_d = maxval;
178     gint i;
179 
180     fill_matrix_r (matrix, matrix_size, matrix_size, 0, 0, 0, 1);
181 
182     /* Recenter around 0 and scale so magnitude == 1.0 => -128..127 */
183 
184     for (i = 0; i < matrix_size * matrix_size; i++)
185     {
186         matrix [i] = ((gdouble) matrix [i] - maxval_d / 2.0) * (256.0 / maxval_d) * magnitude + 0.5;
187     }
188 }
189 
190 gint *
chafa_gen_bayer_matrix(gint matrix_size,gdouble magnitude)191 chafa_gen_bayer_matrix (gint matrix_size, gdouble magnitude)
192 {
193     gint *matrix;
194 
195     g_assert (matrix_size == 2 || matrix_size == 4 || matrix_size == 8 || matrix_size == 16);
196 
197     matrix = g_malloc (matrix_size * matrix_size * sizeof (gint));
198     fill_matrix (matrix, matrix_size, magnitude);
199 
200     return matrix;
201 }
202