Blender V5.0
image_gen.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cmath>
11#include <cstdlib>
12
13#include "BLI_math_base.h"
14#include "BLI_math_color.h"
15#include "BLI_math_vector.h"
16#include "BLI_task.hh"
17
18#include "BKE_image.hh"
19
21#include "IMB_imbuf.hh"
22#include "IMB_imbuf_types.hh"
23
24#include "BLF_api.hh"
25
27 uchar *rect_byte, float *rect_float, int width, int height, const float color[4])
28{
29 using namespace blender;
31 IndexRange(int64_t(width) * height), 64 * 1024, [&](const IndexRange i_range) {
32 if (rect_float != nullptr) {
33 float *dst = rect_float + i_range.first() * 4;
34 for ([[maybe_unused]] const int64_t i : i_range) {
35 copy_v4_v4(dst, color);
36 dst += 4;
37 }
38 }
39 if (rect_byte != nullptr) {
40 uchar ccol[4];
42 uchar *dst = rect_byte + i_range.first() * 4;
43 for ([[maybe_unused]] const int64_t i : i_range) {
44 dst[0] = ccol[0];
45 dst[1] = ccol[1];
46 dst[2] = ccol[2];
47 dst[3] = ccol[3];
48 dst += 4;
49 }
50 }
51 });
52}
53
55 uchar *rect, float *rect_float, int width, int height, int offset)
56{
57 /* these two passes could be combined into one, but it's more readable and
58 * easy to tweak like this, speed isn't really that much of an issue in this situation... */
59 const int checker_size = 32;
60 const int checker_size_half = checker_size / 2;
61
62 uchar *rect_orig = rect;
63 float *rect_float_orig = rect_float;
64
65 float hsv[3] = {0.0f, 0.9f, 0.9f};
66 float rgb[3];
67
68 float dark_linear_color = 0.0f, bright_linear_color = 0.0f;
69 if (rect_float != nullptr) {
70 dark_linear_color = srgb_to_linearrgb(0.25f);
71 bright_linear_color = srgb_to_linearrgb(0.58f);
72 }
73
74 /* checkers */
75 for (int y = offset; y < height + offset; y++) {
76 int dark = powf(-1.0f, floorf(y / checker_size));
77
78 for (int x = 0; x < width; x++) {
79 if (x % checker_size == 0) {
80 dark = -dark;
81 }
82
83 if (rect_float) {
84 if (dark > 0) {
85 rect_float[0] = rect_float[1] = rect_float[2] = dark_linear_color;
86 rect_float[3] = 1.0f;
87 }
88 else {
89 rect_float[0] = rect_float[1] = rect_float[2] = bright_linear_color;
90 rect_float[3] = 1.0f;
91 }
92 rect_float += 4;
93 }
94 else {
95 if (dark > 0) {
96 rect[0] = rect[1] = rect[2] = 64;
97 rect[3] = 255;
98 }
99 else {
100 rect[0] = rect[1] = rect[2] = 150;
101 rect[3] = 255;
102 }
103 rect += 4;
104 }
105 }
106 }
107
108 rect = rect_orig;
109 rect_float = rect_float_orig;
110
111 /* 2nd pass, colored `+`. */
112 for (int y = offset; y < height + offset; y++) {
113 float hoffs = 0.125f * floorf(y / checker_size);
114
115 for (int x = 0; x < width; x++) {
116 float h = 0.125f * floorf(x / checker_size);
117 int test_x, test_y;
118 /* Note that this `+` is not exactly centered since it's a 1px wide line being
119 * drawn inside an even sized square, keep as-is since solving requires either
120 * using odd sized checkers or double-width lines, see #112653. */
121 if (((test_x = abs((x % checker_size) - checker_size_half)) < 4) &&
122 ((test_y = abs((y % checker_size) - checker_size_half)) < 4) &&
123 ((test_x < 1) || (test_y < 1)))
124 {
125 hsv[0] = fmodf(fabsf(h - hoffs), 1.0f);
126 hsv_to_rgb_v(hsv, rgb);
127
128 if (rect) {
129 rect[0] = char(rgb[0] * 255.0f);
130 rect[1] = char(rgb[1] * 255.0f);
131 rect[2] = char(rgb[2] * 255.0f);
132 rect[3] = 255;
133 }
134
135 if (rect_float) {
137 rect_float[3] = 1.0f;
138 }
139 }
140
141 if (rect_float) {
142 rect_float += 4;
143 }
144 if (rect) {
145 rect += 4;
146 }
147 }
148 }
149}
150
151void BKE_image_buf_fill_checker(uchar *rect, float *rect_float, int width, int height)
152{
153 using namespace blender;
154 threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
155 int64_t offset = y_range.first() * width * 4;
156 uchar *dst_byte = (rect != nullptr) ? (rect + offset) : nullptr;
157 float *dst_float = (rect_float != nullptr) ? (rect_float + offset) : nullptr;
158 image_buf_fill_checker_slice(dst_byte, dst_float, width, y_range.size(), y_range.first());
159 });
160}
161
162/* Utility functions for BKE_image_buf_fill_checker_color */
163
164#define BLEND_FLOAT(real, add) (real + add <= 1.0f) ? (real + add) : 1.0f
165#define BLEND_CHAR(real, add) \
166 ((real + char(add * 255.0f)) <= 255) ? (real + char(add * 255.0f)) : 255
167
169 uchar *rect, float *rect_float, int width, int height, int offset, int total_height)
170{
171 int hue_step, y, x;
172 float hsv[3], rgb[3];
173
174 hsv[1] = 1.0;
175
176 hue_step = power_of_2_max_i(width / 8);
177 hue_step = std::max(hue_step, 8);
178
179 for (y = offset; y < height + offset; y++) {
180 /* Use a number lower than 1.0 else its too bright. */
181 hsv[2] = 0.1 + (y * (0.4 / total_height));
182
183 for (x = 0; x < width; x++) {
184 hsv[0] = float(double(x / hue_step) * 1.0 / width * hue_step);
185 hsv_to_rgb_v(hsv, rgb);
186
187 if (rect) {
188 rect[0] = char(rgb[0] * 255.0f);
189 rect[1] = char(rgb[1] * 255.0f);
190 rect[2] = char(rgb[2] * 255.0f);
191 rect[3] = 255;
192
193 rect += 4;
194 }
195
196 if (rect_float) {
198 rect_float[3] = 1.0f;
199
200 rect_float += 4;
201 }
202 }
203 }
204}
205
207 uchar *rect, float *rect_float, int width, int height, int size, float blend, int offset)
208{
209 int x, y;
210 float blend_half = blend * 0.5f;
211
212 for (y = offset; y < height + offset; y++) {
213 for (x = 0; x < width; x++) {
214 if (((y / size) % 2 == 1 && (x / size) % 2 == 1) ||
215 ((y / size) % 2 == 0 && (x / size) % 2 == 0))
216 {
217 if (rect) {
218 rect[0] = char(BLEND_CHAR(rect[0], blend));
219 rect[1] = char(BLEND_CHAR(rect[1], blend));
220 rect[2] = char(BLEND_CHAR(rect[2], blend));
221 rect[3] = 255;
222
223 rect += 4;
224 }
225 if (rect_float) {
226 rect_float[0] = BLEND_FLOAT(rect_float[0], blend);
227 rect_float[1] = BLEND_FLOAT(rect_float[1], blend);
228 rect_float[2] = BLEND_FLOAT(rect_float[2], blend);
229 rect_float[3] = 1.0f;
230
231 rect_float += 4;
232 }
233 }
234 else {
235 if (rect) {
236 rect[0] = char(BLEND_CHAR(rect[0], blend_half));
237 rect[1] = char(BLEND_CHAR(rect[1], blend_half));
238 rect[2] = char(BLEND_CHAR(rect[2], blend_half));
239 rect[3] = 255;
240
241 rect += 4;
242 }
243 if (rect_float) {
244 rect_float[0] = BLEND_FLOAT(rect_float[0], blend_half);
245 rect_float[1] = BLEND_FLOAT(rect_float[1], blend_half);
246 rect_float[2] = BLEND_FLOAT(rect_float[2], blend_half);
247 rect_float[3] = 1.0f;
248
249 rect_float += 4;
250 }
251 }
252 }
253 }
254}
255
257 uchar *rect, float *rect_float, int width, int height, float blend, int offset)
258{
259 int x, y;
260 for (y = offset; y < height + offset; y++) {
261 for (x = 0; x < width; x++) {
262 if (((y % 32) == 0) || ((x % 32) == 0) || x == 0) {
263 if (rect) {
264 rect[0] = BLEND_CHAR(rect[0], blend);
265 rect[1] = BLEND_CHAR(rect[1], blend);
266 rect[2] = BLEND_CHAR(rect[2], blend);
267 rect[3] = 255;
268
269 rect += 4;
270 }
271 if (rect_float) {
272 rect_float[0] = BLEND_FLOAT(rect_float[0], blend);
273 rect_float[1] = BLEND_FLOAT(rect_float[1], blend);
274 rect_float[2] = BLEND_FLOAT(rect_float[2], blend);
275 rect_float[3] = 1.0f;
276
277 rect_float += 4;
278 }
279 }
280 else {
281 if (rect_float) {
282 rect_float += 4;
283 }
284 if (rect) {
285 rect += 4;
286 }
287 }
288 }
289 }
290}
291
292/* Defined in `image.cc`. */
293
295 uchar *rect, float *rect_float, int width, int height, int step, int outline)
296{
297 int x, y;
298 int pen_x, pen_y;
299 char text[3] = {'A', '1', '\0'};
300 const int mono = blf_mono_font_render;
301
302 BLF_size(mono, 54.0f); /* hard coded size! */
303
304 /* Using nullptr will assume the byte buffer has sRGB colorspace, which currently
305 * matches the default colorspace of new images. */
306 BLF_buffer(mono, rect_float, rect, width, height, nullptr);
307
308 const float text_color[4] = {0.0, 0.0, 0.0, 1.0};
309 const float text_outline[4] = {1.0, 1.0, 1.0, 1.0};
310
311 const char char_array[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
312 /* Subtract one because of null termination. */
313 const int char_num = sizeof(char_array) - 1;
314
315 int first_char_index = 0;
316 for (y = 0; y < height; y += step) {
317 text[0] = char_array[first_char_index];
318
319 int second_char_index = 27;
320 for (x = 0; x < width; x += step) {
321 text[1] = char_array[second_char_index];
322
323 /* hard coded offset */
324 pen_x = x + 33;
325 pen_y = y + 44;
326
327 /* terribly crappy outline font! */
328 BLF_buffer_col(mono, text_outline);
329
330 BLF_position(mono, pen_x - outline, pen_y, 0.0);
331 BLF_draw_buffer(mono, text, 2);
332 BLF_position(mono, pen_x + outline, pen_y, 0.0);
333 BLF_draw_buffer(mono, text, 2);
334 BLF_position(mono, pen_x, pen_y - outline, 0.0);
335 BLF_draw_buffer(mono, text, 2);
336 BLF_position(mono, pen_x, pen_y + outline, 0.0);
337 BLF_draw_buffer(mono, text, 2);
338
339 BLF_position(mono, pen_x - outline, pen_y - outline, 0.0);
340 BLF_draw_buffer(mono, text, 2);
341 BLF_position(mono, pen_x + outline, pen_y + outline, 0.0);
342 BLF_draw_buffer(mono, text, 2);
343 BLF_position(mono, pen_x - outline, pen_y + outline, 0.0);
344 BLF_draw_buffer(mono, text, 2);
345 BLF_position(mono, pen_x + outline, pen_y - outline, 0.0);
346 BLF_draw_buffer(mono, text, 2);
347
348 BLF_buffer_col(mono, text_color);
349 BLF_position(mono, pen_x, pen_y, 0.0);
350 BLF_draw_buffer(mono, text, 2);
351
352 second_char_index = (second_char_index + 1) % char_num;
353 }
354 first_char_index = (first_char_index + 1) % char_num;
355 }
356
357 /* cleanup the buffer. */
358 BLF_buffer(mono, nullptr, nullptr, 0, 0, nullptr);
359}
360
362 uchar *rect, float *rect_float, int width, int height, int offset, int total_height)
363{
364 checker_board_color_fill(rect, rect_float, width, height, offset, total_height);
365 checker_board_color_tint(rect, rect_float, width, height, 1, 0.03f, offset);
366 checker_board_color_tint(rect, rect_float, width, height, 4, 0.05f, offset);
367 checker_board_color_tint(rect, rect_float, width, height, 32, 0.07f, offset);
368 checker_board_color_tint(rect, rect_float, width, height, 128, 0.15f, offset);
369 checker_board_grid_fill(rect, rect_float, width, height, 1.0f / 4.0f, offset);
370}
371
372void BKE_image_buf_fill_checker_color(uchar *rect, float *rect_float, int width, int height)
373{
374 using namespace blender;
375 threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
376 int64_t offset = y_range.first() * width * 4;
377 uchar *dst_byte = (rect != nullptr) ? (rect + offset) : nullptr;
378 float *dst_float = (rect_float != nullptr) ? (rect_float + offset) : nullptr;
380 dst_byte, dst_float, width, y_range.size(), y_range.first(), height);
381 });
382
383 checker_board_text(rect, rect_float, width, height, 128, 2);
384
385 if (rect_float != nullptr) {
386 /* TODO(sergey): Currently it's easier to fill in form buffer and
387 * linearize it afterwards. This could be optimized with some smart
388 * trickery around blending factors and such.
389 */
391 rect_float,
392 4,
395 true,
396 width,
397 height,
398 width,
399 width);
400 }
401}
void BLF_size(int fontid, float size)
Definition blf.cc:443
void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, const ColorSpace *colorspace)
Definition blf.cc:956
void BLF_draw_buffer(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:1045
int blf_mono_font_render
Definition blf.cc:49
void BLF_buffer_col(int fontid, const float srgb_color[4]) ATTR_NONNULL(2)
Definition blf.cc:1015
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:388
MINLINE int power_of_2_max_i(int n)
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition math_color.cc:57
MINLINE void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4])
float srgb_to_linearrgb(float c)
MINLINE void copy_v4_v4(float r[4], const float a[4])
unsigned char uchar
BLI_INLINE void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3], const float srgb[3])
BLI_INLINE void IMB_colormanagement_rec709_to_scene_linear(float scene_linear[3], const float rec709[3])
void IMB_buffer_float_from_float_threaded(float *rect_to, const float *rect_from, int channels_from, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from)
#define IB_PROFILE_SRGB
#define IB_PROFILE_LINEAR_RGB
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr int64_t first() const
constexpr int64_t size() const
nullptr float
#define powf(x, y)
#define fmodf(x, y)
#define abs
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
void BKE_image_buf_fill_checker_color(uchar *rect, float *rect_float, int width, int height)
Definition image_gen.cc:372
static void checker_board_color_tint(uchar *rect, float *rect_float, int width, int height, int size, float blend, int offset)
Definition image_gen.cc:206
void BKE_image_buf_fill_color(uchar *rect_byte, float *rect_float, int width, int height, const float color[4])
Definition image_gen.cc:26
static void checker_board_color_prepare_slice(uchar *rect, float *rect_float, int width, int height, int offset, int total_height)
Definition image_gen.cc:361
#define BLEND_FLOAT(real, add)
Definition image_gen.cc:164
#define BLEND_CHAR(real, add)
Definition image_gen.cc:165
static void checker_board_text(uchar *rect, float *rect_float, int width, int height, int step, int outline)
Definition image_gen.cc:294
static void checker_board_color_fill(uchar *rect, float *rect_float, int width, int height, int offset, int total_height)
Definition image_gen.cc:168
static void image_buf_fill_checker_slice(uchar *rect, float *rect_float, int width, int height, int offset)
Definition image_gen.cc:54
void BKE_image_buf_fill_checker(uchar *rect, float *rect_float, int width, int height)
Definition image_gen.cc:151
static void checker_board_grid_fill(uchar *rect, float *rect_float, int width, int height, float blend, int offset)
Definition image_gen.cc:256
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
#define floorf
#define fabsf
i
Definition text_draw.cc:230
static int blend(const Tex *tex, const float texvec[3], TexResult *texres)