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