Cleanup: remove redundant doxygen \file argument
[blender.git] / source / blender / blenkernel / intern / image_gen.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file \ingroup bke
18  */
19
20 #include <math.h>
21 #include <stdlib.h>
22
23 #include "BLI_math_base.h"
24 #include "BLI_math_color.h"
25 #include "BLI_math_vector.h"
26
27 #include "BKE_image.h"
28
29 #include "IMB_imbuf.h"
30 #include "IMB_imbuf_types.h"
31
32 #include "BLF_api.h"
33
34 typedef struct FillColorThreadData {
35         unsigned char *rect;
36         float *rect_float;
37         int width;
38         float color[4];
39 } FillColorThreadData;
40
41 static void image_buf_fill_color_slice(unsigned char *rect,
42                                        float *rect_float,
43                                        int width, int height,
44                                        const float color[4])
45 {
46         int x, y;
47
48         /* blank image */
49         if (rect_float) {
50                 float linear_color[4];
51                 srgb_to_linearrgb_v4(linear_color, color);
52                 for (y = 0; y < height; y++) {
53                         for (x = 0; x < width; x++) {
54                                 copy_v4_v4(rect_float, linear_color);
55                                 rect_float += 4;
56                         }
57                 }
58         }
59
60         if (rect) {
61                 unsigned char ccol[4];
62                 rgba_float_to_uchar(ccol, color);
63                 for (y = 0; y < height; y++) {
64                         for (x = 0; x < width; x++) {
65                                 rect[0] = ccol[0];
66                                 rect[1] = ccol[1];
67                                 rect[2] = ccol[2];
68                                 rect[3] = ccol[3];
69                                 rect += 4;
70                         }
71                 }
72         }
73 }
74
75 static void image_buf_fill_color_thread_do(void *data_v,
76                                            int start_scanline,
77                                            int num_scanlines)
78 {
79         FillColorThreadData *data = (FillColorThreadData *)data_v;
80         size_t offset = ((size_t)start_scanline) * data->width * 4;
81         unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL;
82         float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL;
83         image_buf_fill_color_slice(rect,
84                                    rect_float,
85                                    data->width,
86                                    num_scanlines,
87                                    data->color);
88 }
89
90 void BKE_image_buf_fill_color(unsigned char *rect,
91                               float *rect_float,
92                               int width, int height,
93                               const float color[4])
94 {
95         if (((size_t)width) * height < 64 * 64) {
96                 image_buf_fill_color_slice(rect, rect_float, width, height, color);
97         }
98         else {
99                 FillColorThreadData data;
100                 data.rect = rect;
101                 data.rect_float = rect_float;
102                 data.width = width;
103                 copy_v4_v4(data.color, color);
104                 IMB_processor_apply_threaded_scanlines(
105                     height, image_buf_fill_color_thread_do, &data);
106         }
107 }
108
109 static void image_buf_fill_checker_slice(unsigned char *rect,
110                                          float *rect_float,
111                                          int width, int height,
112                                          int offset)
113 {
114         /* these two passes could be combined into one, but it's more readable and
115          * easy to tweak like this, speed isn't really that much of an issue in this situation... */
116
117         int checkerwidth = 32, dark = 1;
118         int x, y;
119
120         unsigned char *rect_orig = rect;
121         float *rect_float_orig = rect_float;
122
123
124         float h = 0.0, hoffs = 0.0;
125         float hsv[3] = {0.0f, 0.9f, 0.9f};
126         float rgb[3];
127
128         float dark_linear_color = 0.0f, bright_linear_color = 0.0f;
129         if (rect_float != NULL) {
130                 dark_linear_color = srgb_to_linearrgb(0.25f);
131                 bright_linear_color = srgb_to_linearrgb(0.58f);
132         }
133
134         /* checkers */
135         for (y = offset; y < height + offset; y++) {
136                 dark = powf(-1.0f, floorf(y / checkerwidth));
137
138                 for (x = 0; x < width; x++) {
139                         if (x % checkerwidth == 0) dark = -dark;
140
141                         if (rect_float) {
142                                 if (dark > 0) {
143                                         rect_float[0] = rect_float[1] = rect_float[2] = dark_linear_color;
144                                         rect_float[3] = 1.0f;
145                                 }
146                                 else {
147                                         rect_float[0] = rect_float[1] = rect_float[2] = bright_linear_color;
148                                         rect_float[3] = 1.0f;
149                                 }
150                                 rect_float += 4;
151                         }
152                         else {
153                                 if (dark > 0) {
154                                         rect[0] = rect[1] = rect[2] = 64;
155                                         rect[3] = 255;
156                                 }
157                                 else {
158                                         rect[0] = rect[1] = rect[2] = 150;
159                                         rect[3] = 255;
160                                 }
161                                 rect += 4;
162                         }
163                 }
164         }
165
166         rect = rect_orig;
167         rect_float = rect_float_orig;
168
169         /* 2nd pass, colored + */
170         for (y = offset; y < height + offset; y++) {
171                 hoffs = 0.125f * floorf(y / checkerwidth);
172
173                 for (x = 0; x < width; x++) {
174                         h = 0.125f * floorf(x / checkerwidth);
175
176                         if ((abs((x % checkerwidth) - (checkerwidth / 2)) < 4) &&
177                             (abs((y % checkerwidth) - (checkerwidth / 2)) < 4))
178                         {
179                                 if ((abs((x % checkerwidth) - (checkerwidth / 2)) < 1) ||
180                                     (abs((y % checkerwidth) - (checkerwidth / 2)) < 1))
181                                 {
182                                         hsv[0] = fmodf(fabsf(h - hoffs), 1.0f);
183                                         hsv_to_rgb_v(hsv, rgb);
184
185                                         if (rect) {
186                                                 rect[0] = (char)(rgb[0] * 255.0f);
187                                                 rect[1] = (char)(rgb[1] * 255.0f);
188                                                 rect[2] = (char)(rgb[2] * 255.0f);
189                                                 rect[3] = 255;
190                                         }
191
192                                         if (rect_float) {
193                                                 srgb_to_linearrgb_v3_v3(rect_float, rgb);
194                                                 rect_float[3] = 1.0f;
195                                         }
196                                 }
197                         }
198
199                         if (rect_float) rect_float += 4;
200                         if (rect) rect += 4;
201                 }
202         }
203 }
204
205 typedef struct FillCheckerThreadData {
206         unsigned char *rect;
207         float *rect_float;
208         int width;
209 } FillCheckerThreadData;
210
211 static void image_buf_fill_checker_thread_do(void *data_v,
212                                              int start_scanline,
213                                              int num_scanlines)
214 {
215         FillCheckerThreadData *data = (FillCheckerThreadData *)data_v;
216         size_t offset = ((size_t)start_scanline) * data->width * 4;
217         unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL;
218         float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL;
219         image_buf_fill_checker_slice(rect,
220                                      rect_float,
221                                      data->width,
222                                      num_scanlines,
223                                      start_scanline);
224 }
225
226 void BKE_image_buf_fill_checker(unsigned char *rect,
227                                 float *rect_float,
228                                 int width, int height)
229 {
230         if (((size_t)width) * height < 64 * 64) {
231                 image_buf_fill_checker_slice(rect, rect_float, width, height, 0);
232         }
233         else {
234                 FillCheckerThreadData data;
235                 data.rect = rect;
236                 data.rect_float = rect_float;
237                 data.width = width;
238                 IMB_processor_apply_threaded_scanlines(
239                     height, image_buf_fill_checker_thread_do, &data);
240         }
241 }
242
243 /* Utility functions for BKE_image_buf_fill_checker_color */
244
245 #define BLEND_FLOAT(real, add)  (real + add <= 1.0f) ? (real + add) : 1.0f
246 #define BLEND_CHAR(real, add) ((real + (char)(add * 255.0f)) <= 255) ? (real + (char)(add * 255.0f)) : 255
247
248 static void checker_board_color_fill(unsigned char *rect,
249                                      float *rect_float,
250                                      int width,
251                                      int height,
252                                      int offset,
253                                      int total_height)
254 {
255         int hue_step, y, x;
256         float hsv[3], rgb[3];
257
258         hsv[1] = 1.0;
259
260         hue_step = power_of_2_max_i(width / 8);
261         if (hue_step < 8) hue_step = 8;
262
263         for (y = offset; y < height + offset; y++) {
264
265                 hsv[2] = 0.1 + (y * (0.4 / total_height)); /* use a number lower then 1.0 else its too bright */
266                 for (x = 0; x < width; x++) {
267                         hsv[0] = (float)((double)(x / hue_step) * 1.0 / width * hue_step);
268                         hsv_to_rgb_v(hsv, rgb);
269
270                         if (rect) {
271                                 rect[0] = (char)(rgb[0] * 255.0f);
272                                 rect[1] = (char)(rgb[1] * 255.0f);
273                                 rect[2] = (char)(rgb[2] * 255.0f);
274                                 rect[3] = 255;
275
276                                 rect += 4;
277                         }
278
279                         if (rect_float) {
280                                 rect_float[0] = rgb[0];
281                                 rect_float[1] = rgb[1];
282                                 rect_float[2] = rgb[2];
283                                 rect_float[3] = 1.0f;
284
285                                 rect_float += 4;
286                         }
287                 }
288         }
289 }
290
291 static void checker_board_color_tint(unsigned char *rect,
292                                      float *rect_float,
293                                      int width,
294                                      int height,
295                                      int size,
296                                      float blend,
297                                      int offset)
298 {
299         int x, y;
300         float blend_half = blend * 0.5f;
301
302         for (y = offset; y < height + offset; y++) {
303                 for (x = 0; x < width; x++) {
304                         if (((y / size) % 2 == 1 && (x / size) % 2 == 1) ||
305                             ((y / size) % 2 == 0 && (x / size) % 2 == 0))
306                         {
307                                 if (rect) {
308                                         rect[0] = (char)BLEND_CHAR(rect[0], blend);
309                                         rect[1] = (char)BLEND_CHAR(rect[1], blend);
310                                         rect[2] = (char)BLEND_CHAR(rect[2], blend);
311                                         rect[3] = 255;
312
313                                         rect += 4;
314                                 }
315                                 if (rect_float) {
316                                         rect_float[0] = BLEND_FLOAT(rect_float[0], blend);
317                                         rect_float[1] = BLEND_FLOAT(rect_float[1], blend);
318                                         rect_float[2] = BLEND_FLOAT(rect_float[2], blend);
319                                         rect_float[3] = 1.0f;
320
321                                         rect_float += 4;
322                                 }
323                         }
324                         else {
325                                 if (rect) {
326                                         rect[0] = (char)BLEND_CHAR(rect[0], blend_half);
327                                         rect[1] = (char)BLEND_CHAR(rect[1], blend_half);
328                                         rect[2] = (char)BLEND_CHAR(rect[2], blend_half);
329                                         rect[3] = 255;
330
331                                         rect += 4;
332                                 }
333                                 if (rect_float) {
334                                         rect_float[0] = BLEND_FLOAT(rect_float[0], blend_half);
335                                         rect_float[1] = BLEND_FLOAT(rect_float[1], blend_half);
336                                         rect_float[2] = BLEND_FLOAT(rect_float[2], blend_half);
337                                         rect_float[3] = 1.0f;
338
339                                         rect_float += 4;
340                                 }
341                         }
342
343                 }
344         }
345 }
346
347 static void checker_board_grid_fill(unsigned char *rect,
348                                     float *rect_float,
349                                     int width,
350                                     int height,
351                                     float blend,
352                                     int offset)
353 {
354         int x, y;
355         for (y = offset; y < height + offset; y++) {
356                 for (x = 0; x < width; x++) {
357                         if (((y % 32) == 0) || ((x % 32) == 0) || x == 0) {
358                                 if (rect) {
359                                         rect[0] = BLEND_CHAR(rect[0], blend);
360                                         rect[1] = BLEND_CHAR(rect[1], blend);
361                                         rect[2] = BLEND_CHAR(rect[2], blend);
362                                         rect[3] = 255;
363
364                                         rect += 4;
365                                 }
366                                 if (rect_float) {
367                                         rect_float[0] = BLEND_FLOAT(rect_float[0], blend);
368                                         rect_float[1] = BLEND_FLOAT(rect_float[1], blend);
369                                         rect_float[2] = BLEND_FLOAT(rect_float[2], blend);
370                                         rect_float[3] = 1.0f;
371
372                                         rect_float += 4;
373                                 }
374                         }
375                         else {
376                                 if (rect_float) rect_float += 4;
377                                 if (rect) rect += 4;
378                         }
379                 }
380         }
381 }
382
383 /* defined in image.c */
384
385 static void checker_board_text(unsigned char *rect,
386                                float *rect_float,
387                                int width,
388                                int height,
389                                int step,
390                                int outline)
391 {
392         int x, y;
393         int pen_x, pen_y;
394         char text[3] = {'A', '1', '\0'};
395         const int mono = blf_mono_font_render;
396
397         BLF_size(mono, 54, 72); /* hard coded size! */
398
399         /* OCIO_TODO: using NULL as display will assume using sRGB display
400          *            this is correct since currently generated images are assumed to be in sRGB space,
401          *            but this would probably needed to be fixed in some way
402          */
403         BLF_buffer(mono, rect_float, rect, width, height, 4, NULL);
404
405         const float text_color[4]   = {0.0, 0.0, 0.0, 1.0};
406         const float text_outline[4] = {1.0, 1.0, 1.0, 1.0};
407
408         for (y = 0; y < height; y += step) {
409                 text[1] = '1';
410
411                 for (x = 0; x < width; x += step) {
412                         /* hard coded offset */
413                         pen_x = x + 33;
414                         pen_y = y + 44;
415
416                         /* terribly crappy outline font! */
417                         BLF_buffer_col(mono, text_outline);
418
419                         BLF_position(mono, pen_x - outline, pen_y, 0.0);
420                         BLF_draw_buffer(mono, text, 2);
421                         BLF_position(mono, pen_x + outline, pen_y, 0.0);
422                         BLF_draw_buffer(mono, text, 2);
423                         BLF_position(mono, pen_x, pen_y - outline, 0.0);
424                         BLF_draw_buffer(mono, text, 2);
425                         BLF_position(mono, pen_x, pen_y + outline, 0.0);
426                         BLF_draw_buffer(mono, text, 2);
427
428                         BLF_position(mono, pen_x - outline, pen_y - outline, 0.0);
429                         BLF_draw_buffer(mono, text, 2);
430                         BLF_position(mono, pen_x + outline, pen_y + outline, 0.0);
431                         BLF_draw_buffer(mono, text, 2);
432                         BLF_position(mono, pen_x - outline, pen_y + outline, 0.0);
433                         BLF_draw_buffer(mono, text, 2);
434                         BLF_position(mono, pen_x + outline, pen_y - outline, 0.0);
435                         BLF_draw_buffer(mono, text, 2);
436
437                         BLF_buffer_col(mono, text_color);
438                         BLF_position(mono, pen_x, pen_y, 0.0);
439                         BLF_draw_buffer(mono, text, 2);
440
441                         text[1]++;
442                 }
443                 text[0]++;
444         }
445
446         /* cleanup the buffer. */
447         BLF_buffer(mono, NULL, NULL, 0, 0, 0, NULL);
448 }
449
450 static void checker_board_color_prepare_slice(unsigned char *rect,
451                                               float *rect_float,
452                                               int width,
453                                               int height,
454                                               int offset,
455                                               int total_height)
456 {
457         checker_board_color_fill(rect, rect_float, width, height, offset, total_height);
458         checker_board_color_tint(rect, rect_float, width, height, 1, 0.03f, offset);
459         checker_board_color_tint(rect, rect_float, width, height, 4, 0.05f, offset);
460         checker_board_color_tint(rect, rect_float, width, height, 32, 0.07f, offset);
461         checker_board_color_tint(rect, rect_float, width, height, 128, 0.15f, offset);
462         checker_board_grid_fill(rect, rect_float, width, height, 1.0f / 4.0f, offset);
463 }
464
465 typedef struct FillCheckerColorThreadData {
466         unsigned char *rect;
467         float *rect_float;
468         int width, height;
469 } FillCheckerColorThreadData;
470
471 static void checker_board_color_prepare_thread_do(void *data_v,
472                                                   int start_scanline,
473                                                   int num_scanlines)
474 {
475         FillCheckerColorThreadData *data = (FillCheckerColorThreadData *)data_v;
476         size_t offset = ((size_t)data->width) * start_scanline * 4;
477         unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL;
478         float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL;
479         checker_board_color_prepare_slice(rect,
480                                           rect_float,
481                                           data->width,
482                                           num_scanlines,
483                                           start_scanline,
484                                           data->height);
485 }
486
487 void BKE_image_buf_fill_checker_color(unsigned char *rect, float *rect_float, int width, int height)
488 {
489         if (((size_t)width) * height < 64 * 64) {
490                 checker_board_color_prepare_slice(rect, rect_float, width, height, 0, height);
491         }
492         else {
493                 FillCheckerColorThreadData data;
494                 data.rect = rect;
495                 data.rect_float = rect_float;
496                 data.width = width;
497                 data.height = height;
498                 IMB_processor_apply_threaded_scanlines(
499                     height, checker_board_color_prepare_thread_do, &data);
500         }
501
502         checker_board_text(rect, rect_float, width, height, 128, 2);
503
504         if (rect_float != NULL) {
505                 /* TODO(sergey): Currently it's easier to fill in form buffer and
506                  * linearize it afterwards. This could be optimized with some smart
507                  * trickery around blending factors and such.
508                  */
509                 IMB_buffer_float_from_float_threaded(rect_float, rect_float,
510                                                      4,
511                                                      IB_PROFILE_LINEAR_RGB,
512                                                      IB_PROFILE_SRGB,
513                                                      true,
514                                                      width, height,
515                                                      width, width);
516         }
517 }