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