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