Blender V4.5
paint_image_2d.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
14#include "DNA_brush_types.h"
15#include "DNA_object_types.h"
16#include "DNA_scene_types.h"
17#include "DNA_space_types.h"
18
19#include "BLI_bitmap.h"
20#include "BLI_listbase.h"
22#include "BLI_stack.h"
23#include "BLI_task.h"
24
25#include "BKE_brush.hh"
26#include "BKE_colorband.hh"
27#include "BKE_context.hh"
28#include "BKE_image.hh"
29#include "BKE_paint.hh"
30#include "BKE_report.hh"
31
32#include "DEG_depsgraph.hh"
33
34#include "ED_paint.hh"
35#include "ED_screen.hh"
36
38#include "IMB_imbuf.hh"
39#include "IMB_imbuf_types.hh"
40
41#include "WM_api.hh"
42#include "WM_types.hh"
43
44#include "UI_view2d.hh"
45
46#include "paint_intern.hh"
47
48/* Brush Painting for 2D image editor */
49
50/* Defines and Structs */
51
53 bool use_float; /* need float imbuf? */
54 bool use_color_correction; /* use color correction for float */
55 bool invert;
56
59
64
71
73
74 // int image_size[2]; /* UNUSED. */
75};
76
79 const Paint *paint;
81
82 /* Store initial starting points for perlin noise on the beginning of each stroke when using
83 * color jitter. */
84 std::optional<blender::float3> initial_hsv_jitter;
85
86 bool firsttouch; /* first paint op */
87
88 ImagePool *pool; /* image pool */
89 rctf tex_mapping; /* texture coordinate mapping */
90 rctf mask_mapping; /* mask texture coordinate mapping */
91
93};
94
97 int srcx, srcy;
99};
100
106
111 int size[2];
112 float uv_origin[2]; /* Stores the position of this tile in UV space. */
115
117
118 float last_paintpos[2]; /* position of last paint op */
119 float start_paintpos[2]; /* position of first paint */
120};
121
142
144 const Paint *paint,
145 Brush *brush,
146 bool invert)
147{
148 BrushPainter *painter = MEM_new<BrushPainter>(__func__);
149
150 painter->brush = brush;
151 painter->scene = scene;
152 painter->paint = paint;
153 if (BKE_brush_color_jitter_get_settings(scene, paint, brush)) {
155 }
156 painter->firsttouch = true;
157 painter->cache_invert = invert;
158
159 return painter;
160}
161
163 Brush *brush, ImagePaintTile *tile, bool use_float, bool use_color_correction, bool invert)
164{
165 BrushPainterCache *cache = &tile->cache;
166
167 if (cache->use_float != use_float) {
168 if (cache->ibuf) {
169 IMB_freeImBuf(cache->ibuf);
170 }
171 if (cache->tex_mask) {
172 MEM_freeN(cache->tex_mask);
173 }
174 if (cache->tex_mask_old) {
175 MEM_freeN(cache->tex_mask_old);
176 }
177 cache->ibuf = nullptr;
178 cache->tex_mask = nullptr;
179 cache->lastdiameter = -1; /* force ibuf create in refresh */
180 }
181
182 cache->use_float = use_float;
183 cache->use_color_correction = use_float && use_color_correction;
184 cache->invert = invert;
185 cache->is_texbrush = (brush->mtex.tex &&
187 true :
188 false;
189 cache->is_maskbrush = (brush->mask_mtex.tex) ? true : false;
190}
191
193{
194 if (cache->ibuf) {
195 IMB_freeImBuf(cache->ibuf);
196 }
197 if (cache->texibuf) {
198 IMB_freeImBuf(cache->texibuf);
199 }
201 if (cache->tex_mask) {
202 MEM_freeN(cache->tex_mask);
203 }
204 if (cache->tex_mask_old) {
205 MEM_freeN(cache->tex_mask_old);
206 }
207}
208
209static void brush_imbuf_tex_co(const rctf *mapping, int x, int y, float texco[3])
210{
211 texco[0] = mapping->xmin + x * mapping->xmax;
212 texco[1] = mapping->ymin + y * mapping->ymax;
213 texco[2] = 0.0f;
214}
215
216/* create a mask with the mask texture */
218{
219 Scene *scene = painter->scene;
220 Brush *brush = painter->brush;
221 rctf mask_mapping = painter->mask_mapping;
222 ImagePool *pool = painter->pool;
223
224 float texco[3];
225 ushort *mask, *m;
226 int x, y, thread = 0;
227
229 m = mask;
230
231 for (y = 0; y < size; y++) {
232 for (x = 0; x < size; x++, m++) {
233 float res;
234 brush_imbuf_tex_co(&mask_mapping, x, y, texco);
235 res = BKE_brush_sample_masktex(scene, brush, texco, thread, pool);
236 *m = ushort(65535.0f * res);
237 }
238 }
239
240 return mask;
241}
242
243/* update rectangular section of the brush image */
246 const ushort *tex_mask_old,
247 int origx,
248 int origy,
249 int w,
250 int h,
251 int xt,
252 int yt,
253 const int diameter)
254{
255 Scene *scene = painter->scene;
256 Brush *brush = painter->brush;
257 BrushPainterCache *cache = &tile->cache;
258 rctf tex_mapping = painter->mask_mapping;
259 ImagePool *pool = painter->pool;
260 ushort res;
261
262 bool use_texture_old = (tex_mask_old != nullptr);
263
264 int x, y, thread = 0;
265
266 ushort *tex_mask = cache->tex_mask;
267 ushort *tex_mask_cur = cache->tex_mask_old;
268
269 /* fill pixels */
270 for (y = origy; y < h; y++) {
271 for (x = origx; x < w; x++) {
272 /* sample texture */
273 float texco[3];
274
275 /* handle byte pixel */
276 ushort *b = tex_mask + (y * diameter + x);
277 ushort *t = tex_mask_cur + (y * diameter + x);
278
279 if (!use_texture_old) {
280 brush_imbuf_tex_co(&tex_mapping, x, y, texco);
281 res = ushort(65535.0f * BKE_brush_sample_masktex(scene, brush, texco, thread, pool));
282 }
283
284 /* read from old texture buffer */
285 if (use_texture_old) {
286 res = *(tex_mask_old + ((y - origy + yt) * cache->tex_mask_old_w + (x - origx + xt)));
287 }
288
289 /* write to new texture mask */
290 *t = res;
291 /* write to mask image buffer */
292 *b = res;
293 }
294 }
295}
296
304 const float pos[2],
305 const int diameter)
306{
307 BrushPainterCache *cache = &tile->cache;
308 ushort *tex_mask_old;
309 int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2;
310
311 /* create brush image buffer if it didn't exist yet */
312 if (!cache->tex_mask) {
313 cache->tex_mask = MEM_malloc_arrayN<ushort>(diameter * diameter, __func__);
314 }
315
316 /* create new texture image buffer with coordinates relative to old */
317 tex_mask_old = cache->tex_mask_old;
318 cache->tex_mask_old = MEM_malloc_arrayN<ushort>(diameter * diameter, __func__);
319
320 if (tex_mask_old) {
321 ImBuf maskibuf;
322 ImBuf maskibuf_old;
323 maskibuf.x = diameter;
324 maskibuf.y = diameter;
325 maskibuf_old.x = cache->tex_mask_old_w;
326 maskibuf_old.y = cache->tex_mask_old_h;
327
328 srcx = srcy = 0;
329 w = cache->tex_mask_old_w;
330 h = cache->tex_mask_old_h;
331 destx = int(floorf(tile->last_paintpos[0])) - int(floorf(pos[0])) + (diameter / 2 - w / 2);
332 desty = int(floorf(tile->last_paintpos[1])) - int(floorf(pos[1])) + (diameter / 2 - h / 2);
333
334 /* hack, use temporary rects so that clipping works */
335 IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h);
336 }
337 else {
338 srcx = srcy = 0;
339 destx = desty = 0;
340 w = h = 0;
341 }
342
343 x1 = min_ii(destx, diameter);
344 y1 = min_ii(desty, diameter);
345 x2 = min_ii(destx + w, diameter);
346 y2 = min_ii(desty + h, diameter);
347
348 /* blend existing texture in new position */
349 if ((x1 < x2) && (y1 < y2)) {
351 painter, tile, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter);
352 }
353
354 if (tex_mask_old) {
355 MEM_freeN(tex_mask_old);
356 }
357
358 /* sample texture in new areas */
359 if ((0 < x1) && (0 < diameter)) {
360 brush_painter_mask_imbuf_update(painter, tile, nullptr, 0, 0, x1, diameter, 0, 0, diameter);
361 }
362 if ((x2 < diameter) && (0 < diameter)) {
364 painter, tile, nullptr, x2, 0, diameter, diameter, 0, 0, diameter);
365 }
366 if ((x1 < x2) && (0 < y1)) {
367 brush_painter_mask_imbuf_update(painter, tile, nullptr, x1, 0, x2, y1, 0, 0, diameter);
368 }
369 if ((x1 < x2) && (y2 < diameter)) {
370 brush_painter_mask_imbuf_update(painter, tile, nullptr, x1, y2, x2, diameter, 0, 0, diameter);
371 }
372
373 /* through with sampling, now update sizes */
374 cache->tex_mask_old_w = diameter;
375 cache->tex_mask_old_h = diameter;
376}
377
378/* create imbuf with brush color */
380 BrushPainter *painter, ImagePaintTile *tile, const int size, float pressure, float distance)
381{
382 Scene *scene = painter->scene;
383 const Paint *paint = painter->paint;
384 Brush *brush = painter->brush;
385 BrushPainterCache *cache = &tile->cache;
386
387 const char *display_device = scene->display_settings.display_device;
388 const ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
389
390 rctf tex_mapping = painter->tex_mapping;
391 ImagePool *pool = painter->pool;
392
393 bool use_color_correction = cache->use_color_correction;
394 bool use_float = cache->use_float;
395 bool is_texbrush = cache->is_texbrush;
396
397 int x, y, thread = 0;
398 float brush_rgb[3];
399
400 /* allocate image buffer */
401 ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_float_data : IB_byte_data);
402
403 /* get brush color */
406 paint,
407 brush,
408 painter->initial_hsv_jitter,
409 use_color_correction,
410 cache->invert,
411 distance,
412 pressure,
413 display,
414 brush_rgb);
415 }
416 else {
417 brush_rgb[0] = 1.0f;
418 brush_rgb[1] = 1.0f;
419 brush_rgb[2] = 1.0f;
420 }
421
422 /* fill image buffer */
423 for (y = 0; y < size; y++) {
424 for (x = 0; x < size; x++) {
425 /* sample texture and multiply with brush color */
426 float texco[3], rgba[4];
427
428 if (is_texbrush) {
429 brush_imbuf_tex_co(&tex_mapping, x, y, texco);
430 const MTex *mtex = &brush->mtex;
431 BKE_brush_sample_tex_3d(scene, brush, mtex, texco, rgba, thread, pool);
432 /* TODO(sergey): Support texture paint color space. */
433 if (!use_float) {
435 }
436 mul_v3_v3(rgba, brush_rgb);
437 }
438 else {
439 copy_v3_v3(rgba, brush_rgb);
440 rgba[3] = 1.0f;
441 }
442
443 if (use_float) {
444 /* write to float pixel */
445 float *dstf = ibuf->float_buffer.data + (y * size + x) * 4;
446 mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */
447 dstf[3] = rgba[3];
448 }
449 else {
450 /* write to byte pixel */
451 uchar *dst = ibuf->byte_buffer.data + (y * size + x) * 4;
452
453 rgb_float_to_uchar(dst, rgba);
454 dst[3] = unit_float_to_uchar_clamp(rgba[3]);
455 }
456 }
457 }
458
459 return ibuf;
460}
461
462/* update rectangular section of the brush image */
465 ImBuf *oldtexibuf,
466 int origx,
467 int origy,
468 int w,
469 int h,
470 int xt,
471 int yt)
472{
473 Scene *scene = painter->scene;
474 const Paint *paint = painter->paint;
475 Brush *brush = painter->brush;
476 const MTex *mtex = &brush->mtex;
477 BrushPainterCache *cache = &tile->cache;
478
479 const char *display_device = scene->display_settings.display_device;
480 const ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
481
482 rctf tex_mapping = painter->tex_mapping;
483 ImagePool *pool = painter->pool;
484
485 bool use_color_correction = cache->use_color_correction;
486 bool use_float = cache->use_float;
487 bool is_texbrush = cache->is_texbrush;
488 bool use_texture_old = (oldtexibuf != nullptr);
489
490 int x, y, thread = 0;
491 float brush_rgb[3];
492
493 ImBuf *ibuf = cache->ibuf;
494 ImBuf *texibuf = cache->texibuf;
495
496 /* get brush color */
499 paint,
500 brush,
501 painter->initial_hsv_jitter,
502 use_color_correction,
503 cache->invert,
504 0.0f,
505 1.0f,
506 display,
507 brush_rgb);
508 }
509 else {
510 brush_rgb[0] = 1.0f;
511 brush_rgb[1] = 1.0f;
512 brush_rgb[2] = 1.0f;
513 }
514
515 /* fill pixels */
516 for (y = origy; y < h; y++) {
517 for (x = origx; x < w; x++) {
518 /* sample texture and multiply with brush color */
519 float texco[3], rgba[4];
520
521 if (!use_texture_old) {
522 if (is_texbrush) {
523 brush_imbuf_tex_co(&tex_mapping, x, y, texco);
524 BKE_brush_sample_tex_3d(scene, brush, mtex, texco, rgba, thread, pool);
525 /* TODO(sergey): Support texture paint color space. */
526 if (!use_float) {
528 }
529 mul_v3_v3(rgba, brush_rgb);
530 }
531 else {
532 copy_v3_v3(rgba, brush_rgb);
533 rgba[3] = 1.0f;
534 }
535 }
536
537 if (use_float) {
538 /* handle float pixel */
539 float *bf = ibuf->float_buffer.data + (y * ibuf->x + x) * 4;
540 float *tf = texibuf->float_buffer.data + (y * texibuf->x + x) * 4;
541
542 /* read from old texture buffer */
543 if (use_texture_old) {
544 const float *otf = oldtexibuf->float_buffer.data +
545 ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4;
546 copy_v4_v4(rgba, otf);
547 }
548
549 /* write to new texture buffer */
550 copy_v4_v4(tf, rgba);
551
552 /* output premultiplied float image, mf was already premultiplied */
553 mul_v3_v3fl(bf, rgba, rgba[3]);
554 bf[3] = rgba[3];
555 }
556 else {
557 uchar crgba[4];
558
559 /* handle byte pixel */
560 uchar *b = ibuf->byte_buffer.data + (y * ibuf->x + x) * 4;
561 uchar *t = texibuf->byte_buffer.data + (y * texibuf->x + x) * 4;
562
563 /* read from old texture buffer */
564 if (use_texture_old) {
565 uchar *ot = oldtexibuf->byte_buffer.data +
566 ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4;
567 crgba[0] = ot[0];
568 crgba[1] = ot[1];
569 crgba[2] = ot[2];
570 crgba[3] = ot[3];
571 }
572 else {
573 rgba_float_to_uchar(crgba, rgba);
574 }
575
576 /* write to new texture buffer */
577 t[0] = crgba[0];
578 t[1] = crgba[1];
579 t[2] = crgba[2];
580 t[3] = crgba[3];
581
582 /* write to brush image buffer */
583 b[0] = crgba[0];
584 b[1] = crgba[1];
585 b[2] = crgba[2];
586 b[3] = crgba[3];
587 }
588 }
589 }
590}
591
592/* update the brush image by trying to reuse the cached texture result. this
593 * can be considerably faster for brushes that change size due to pressure or
594 * textures that stick to the surface where only part of the pixels are new */
597 const float pos[2],
598 const int diameter)
599{
600 BrushPainterCache *cache = &tile->cache;
601 ImBuf *oldtexibuf, *ibuf;
602 int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2;
603
604 /* create brush image buffer if it didn't exist yet */
605 imbflag = (cache->use_float) ? IB_float_data : IB_byte_data;
606 if (!cache->ibuf) {
607 cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag);
608 }
609 ibuf = cache->ibuf;
610
611 /* create new texture image buffer with coordinates relative to old */
612 oldtexibuf = cache->texibuf;
613 cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag);
614
615 if (oldtexibuf) {
616 srcx = srcy = 0;
617 w = oldtexibuf->x;
618 h = oldtexibuf->y;
619 destx = int(floorf(tile->last_paintpos[0])) - int(floorf(pos[0])) + (diameter / 2 - w / 2);
620 desty = int(floorf(tile->last_paintpos[1])) - int(floorf(pos[1])) + (diameter / 2 - h / 2);
621
622 IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h);
623 }
624 else {
625 srcx = srcy = 0;
626 destx = desty = 0;
627 w = h = 0;
628 }
629
630 x1 = min_ii(destx, ibuf->x);
631 y1 = min_ii(desty, ibuf->y);
632 x2 = min_ii(destx + w, ibuf->x);
633 y2 = min_ii(desty + h, ibuf->y);
634
635 /* blend existing texture in new position */
636 if ((x1 < x2) && (y1 < y2)) {
637 brush_painter_imbuf_update(painter, tile, oldtexibuf, x1, y1, x2, y2, srcx, srcy);
638 }
639
640 if (oldtexibuf) {
641 IMB_freeImBuf(oldtexibuf);
642 }
643
644 /* sample texture in new areas */
645 if ((0 < x1) && (0 < ibuf->y)) {
646 brush_painter_imbuf_update(painter, tile, nullptr, 0, 0, x1, ibuf->y, 0, 0);
647 }
648 if ((x2 < ibuf->x) && (0 < ibuf->y)) {
649 brush_painter_imbuf_update(painter, tile, nullptr, x2, 0, ibuf->x, ibuf->y, 0, 0);
650 }
651 if ((x1 < x2) && (0 < y1)) {
652 brush_painter_imbuf_update(painter, tile, nullptr, x1, 0, x2, y1, 0, 0);
653 }
654 if ((x1 < x2) && (y2 < ibuf->y)) {
655 brush_painter_imbuf_update(painter, tile, nullptr, x1, y2, x2, ibuf->y, 0, 0);
656 }
657}
658
661 const int diameter,
662 const float pos[2],
663 const float mouse[2],
664 int mapmode,
665 rctf *r_mapping)
666{
667 float invw = 1.0f / float(tile->canvas->x);
668 float invh = 1.0f / float(tile->canvas->y);
669 float start[2];
670
671 /* find start coordinate of brush in canvas */
672 start[0] = pos[0] - diameter / 2.0f;
673 start[1] = pos[1] - diameter / 2.0f;
674
675 if (mapmode == MTEX_MAP_MODE_STENCIL) {
676 /* map from view coordinates of brush to region coordinates */
677 float xmin, ymin, xmax, ymax;
678 UI_view2d_view_to_region_fl(s->v2d, start[0] * invw, start[1] * invh, &xmin, &ymin);
680 s->v2d, (start[0] + diameter) * invw, (start[1] + diameter) * invh, &xmax, &ymax);
681
682 /* output r_mapping from brush ibuf x/y to region coordinates */
683 r_mapping->xmax = (xmax - xmin) / float(diameter);
684 r_mapping->ymax = (ymax - ymin) / float(diameter);
685 r_mapping->xmin = xmin + (tile->uv_origin[0] * tile->size[0] * r_mapping->xmax);
686 r_mapping->ymin = ymin + (tile->uv_origin[1] * tile->size[1] * r_mapping->ymax);
687 }
688 else if (mapmode == MTEX_MAP_MODE_3D) {
689 /* 3D mapping, just mapping to canvas 0..1. */
690 r_mapping->xmin = 2.0f * (start[0] * invw - 0.5f);
691 r_mapping->ymin = 2.0f * (start[1] * invh - 0.5f);
692 r_mapping->xmax = 2.0f * invw;
693 r_mapping->ymax = 2.0f * invh;
694 }
695 else if (ELEM(mapmode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_RANDOM)) {
696 /* view mapping */
697 r_mapping->xmin = mouse[0] - diameter * 0.5f + 0.5f;
698 r_mapping->ymin = mouse[1] - diameter * 0.5f + 0.5f;
699 r_mapping->xmax = 1.0f;
700 r_mapping->ymax = 1.0f;
701 }
702 else /* if (mapmode == MTEX_MAP_MODE_TILED) */ {
703 r_mapping->xmin = int(-diameter * 0.5) + int(floorf(pos[0])) -
704 int(floorf(tile->start_paintpos[0]));
705 r_mapping->ymin = int(-diameter * 0.5) + int(floorf(pos[1])) -
706 int(floorf(tile->start_paintpos[1]));
707 r_mapping->xmax = 1.0f;
708 r_mapping->ymax = 1.0f;
709 }
710}
711
713 BrushPainter *painter,
715 const float pos[2],
716 const float mouse[2],
717 float pressure,
718 float distance,
719 float size)
720{
721 const Scene *scene = painter->scene;
723 Brush *brush = painter->brush;
724 BrushPainterCache *cache = &tile->cache;
725 /* Adding 4 pixels of padding for brush anti-aliasing. */
726 const int diameter = std::max(1, int(size * 2)) + 4;
727
728 bool do_random = false;
729 bool do_partial_update = false;
730 bool update_color = ((brush->flag & BRUSH_USE_GRADIENT) &&
734 (cache->last_pressure != pressure))) ||
735 BKE_brush_color_jitter_get_settings(scene, painter->paint, brush);
736 float tex_rotation = -brush->mtex.rot;
737 float mask_rotation = -brush->mask_mtex.rot;
738
739 painter->pool = BKE_image_pool_new();
740
741 /* determine how can update based on textures used */
742 if (cache->is_texbrush) {
743 if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) {
744 tex_rotation += ups->brush_rotation;
745 }
746 else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) {
747 do_random = true;
748 }
749 else if (!((brush->flag & BRUSH_ANCHORED) || update_color)) {
750 do_partial_update = true;
751 }
752
754 s, tile, diameter, pos, mouse, brush->mtex.brush_map_mode, &painter->tex_mapping);
755 }
756
757 if (cache->is_maskbrush) {
758 bool renew_maxmask = false;
759 bool do_partial_update_mask = false;
760 /* invalidate case for all mapping modes */
762 mask_rotation += ups->brush_rotation_sec;
763 }
764 else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) {
765 renew_maxmask = true;
766 }
767 else if (!(brush->flag & BRUSH_ANCHORED)) {
768 do_partial_update_mask = true;
769 renew_maxmask = true;
770 }
771 /* explicitly disable partial update even if it has been enabled above */
772 if (brush->mask_pressure) {
773 do_partial_update_mask = false;
774 renew_maxmask = true;
775 }
776
777 if (diameter != cache->lastdiameter || (mask_rotation != cache->last_mask_rotation) ||
778 renew_maxmask)
779 {
780 MEM_SAFE_FREE(cache->tex_mask);
781
783 s, tile, diameter, pos, mouse, brush->mask_mtex.brush_map_mode, &painter->mask_mapping);
784
785 if (do_partial_update_mask) {
787 }
788 else {
789 cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter);
790 }
791 cache->last_mask_rotation = mask_rotation;
792 }
793 }
794
795 /* Re-initialize the curve mask. Mask is always recreated due to the change of position. */
796 paint_curve_mask_cache_update(&cache->curve_mask_cache, brush, diameter, size, pos);
797
798 /* detect if we need to recreate image brush buffer */
799 if (diameter != cache->lastdiameter || (tex_rotation != cache->last_tex_rotation) || do_random ||
800 update_color)
801 {
802 if (cache->ibuf) {
803 IMB_freeImBuf(cache->ibuf);
804 cache->ibuf = nullptr;
805 }
806
807 if (do_partial_update) {
808 /* do partial update of texture */
809 brush_painter_imbuf_partial_update(painter, tile, pos, diameter);
810 }
811 else {
812 /* create brush from scratch */
813 cache->ibuf = brush_painter_imbuf_new(painter, tile, diameter, pressure, distance);
814 }
815
816 cache->lastdiameter = diameter;
817 cache->last_tex_rotation = tex_rotation;
818 cache->last_pressure = pressure;
819 }
820 else if (do_partial_update) {
821 /* do only partial update of texture */
822 int dx = int(floorf(tile->last_paintpos[0])) - int(floorf(pos[0]));
823 int dy = int(floorf(tile->last_paintpos[1])) - int(floorf(pos[1]));
824
825 if ((dx != 0) || (dy != 0)) {
826 brush_painter_imbuf_partial_update(painter, tile, pos, diameter);
827 }
828 }
829
830 BKE_image_pool_free(painter->pool);
831 painter->pool = nullptr;
832}
833
835{
836 if (i == 0) {
837 return true;
838 }
839 if (i >= s->num_tiles) {
840 return false;
841 }
842
843 if (s->tiles[i].state == PAINT2D_TILE_READY) {
844 return true;
845 }
846 if (s->tiles[i].state == PAINT2D_TILE_MISSING) {
847 return false;
848 }
849
850 s->tiles[i].cache.lastdiameter = -1;
851
852 ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, nullptr);
853 if (ibuf != nullptr) {
854 if (ibuf->channels != 4) {
856 }
857 else if ((s->tiles[0].canvas->byte_buffer.data && !ibuf->byte_buffer.data) ||
858 (s->tiles[0].canvas->float_buffer.data && !ibuf->float_buffer.data))
859 {
861 }
862 else {
863 s->tiles[i].size[0] = ibuf->x;
864 s->tiles[i].size[1] = ibuf->y;
865 s->tiles[i].radius_fac = sqrtf((float(ibuf->x) * float(ibuf->y)) /
866 (s->tiles[0].size[0] * s->tiles[0].size[1]));
868 }
869 }
870 else {
872 }
873
874 if (s->tiles[i].state == PAINT2D_TILE_MISSING) {
875 BKE_image_release_ibuf(s->image, ibuf, nullptr);
876 return false;
877 }
878
879 s->tiles[i].canvas = ibuf;
880 return true;
881}
882
883/* keep these functions in sync */
884static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, float r_rgb[4])
885{
886 if (ibuf->float_buffer.data) {
887 const float *rrgbf = ibuf->float_buffer.data + (ibuf->x * y + x) * 4;
888 copy_v4_v4(r_rgb, rrgbf);
889 }
890 else {
891 uchar *rrgb = ibuf->byte_buffer.data + (ibuf->x * y + x) * 4;
893 }
894}
896 ImBuf *ibuf, int x, int y, const bool is_torus, const float rgb[4])
897{
898 if (is_torus) {
899 x %= ibuf->x;
900 if (x < 0) {
901 x += ibuf->x;
902 }
903 y %= ibuf->y;
904 if (y < 0) {
905 y += ibuf->y;
906 }
907 }
908
909 if (ibuf->float_buffer.data) {
910 float *rrgbf = ibuf->float_buffer.data + (ibuf->x * y + x) * 4;
911 float map_alpha = (rgb[3] == 0.0f) ? rrgbf[3] : rrgbf[3] / rgb[3];
912
913 mul_v3_v3fl(rrgbf, rgb, map_alpha);
914 rrgbf[3] = rgb[3];
915 }
916 else {
917 uchar straight[4];
918 uchar *rrgb = ibuf->byte_buffer.data + (ibuf->x * y + x) * 4;
919
920 premul_float_to_straight_uchar(straight, rgb);
921 rrgb[0] = straight[0];
922 rrgb[1] = straight[1];
923 rrgb[2] = straight[2];
924 rrgb[3] = straight[3];
925 }
926}
927
928static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short paint_tile)
929{
930 if (paint_tile & PAINT_TILE_X) {
931 *x %= ibuf->x;
932 if (*x < 0) {
933 *x += ibuf->x;
934 }
935 }
936 if (paint_tile & PAINT_TILE_Y) {
937 *y %= ibuf->y;
938 if (*y < 0) {
939 *y += ibuf->y;
940 }
941 }
942}
943
945 ImBuf *ibuf, int x, int y, float *outrgb, short paint_tile, float w)
946{
947 float inrgb[4];
948
949 if (paint_tile) {
950 paint_2d_ibuf_tile_convert(ibuf, &x, &y, paint_tile);
951 }
952 /* need to also do clipping here always since tiled coordinates
953 * are not always within bounds */
954 if (x < ibuf->x && x >= 0 && y < ibuf->y && y >= 0) {
955 paint_2d_ibuf_rgb_get(ibuf, x, y, inrgb);
956 }
957 else {
958 return 0.0f;
959 }
960
961 mul_v4_fl(inrgb, w);
962 add_v4_v4(outrgb, inrgb);
963
964 return w;
965}
966
969 ImBuf *ibuf,
970 ImBuf *ibufb,
971 const int *pos,
972 const short paint_tile)
973{
974 bool sharpen = (tile->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0));
975 float threshold = s->brush->sharp_threshold;
976 int x, y, xi, yi, xo, yo, xk, yk;
977 float count;
978 int out_off[2], in_off[2], dim[2];
979 int diff_pos[2];
980 float outrgb[4];
981 float rgba[4];
982 BlurKernel *kernel = s->blurkernel;
983
984 dim[0] = ibufb->x;
985 dim[1] = ibufb->y;
986 in_off[0] = pos[0];
987 in_off[1] = pos[1];
988 out_off[0] = out_off[1] = 0;
989
990 if (!paint_tile) {
991 IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], &out_off[1], &dim[0], &dim[1]);
992
993 if ((dim[0] == 0) || (dim[1] == 0)) {
994 return;
995 }
996 }
997
998 /* find offset inside mask buffers to sample them */
999 sub_v2_v2v2_int(diff_pos, out_off, in_off);
1000
1001 for (y = 0; y < dim[1]; y++) {
1002 for (x = 0; x < dim[0]; x++) {
1003 /* get input pixel */
1004 xi = in_off[0] + x;
1005 yi = in_off[1] + y;
1006
1007 count = 0.0;
1008 if (paint_tile) {
1009 paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, paint_tile);
1010 if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) {
1011 paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba);
1012 }
1013 else {
1014 zero_v4(rgba);
1015 }
1016 }
1017 else {
1018 /* coordinates have been clipped properly here, it should be safe to do this */
1019 paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba);
1020 }
1021 zero_v4(outrgb);
1022
1023 for (yk = 0; yk < kernel->side; yk++) {
1024 for (xk = 0; xk < kernel->side; xk++) {
1026 xi + xk - kernel->pixel_len,
1027 yi + yk - kernel->pixel_len,
1028 outrgb,
1029 paint_tile,
1030 kernel->wdata[xk + yk * kernel->side]);
1031 }
1032 }
1033
1034 if (count > 0.0f) {
1035 mul_v4_fl(outrgb, 1.0f / count);
1036
1037 if (sharpen) {
1038 /* subtract blurred image from normal image gives high pass filter */
1039 sub_v3_v3v3(outrgb, rgba, outrgb);
1040
1041 /* Now rgba_ub contains the edge result, but this should be converted to luminance to
1042 * avoid colored speckles appearing in final image, and also to check for threshold. */
1043 outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb);
1044 if (fabsf(outrgb[0]) > threshold) {
1045 float mask = BKE_brush_alpha_get(s->scene, s->brush);
1046 float alpha = rgba[3];
1047 rgba[3] = outrgb[3] = mask;
1048
1049 /* add to enhance edges */
1050 blend_color_add_float(outrgb, rgba, outrgb);
1051 outrgb[3] = alpha;
1052 }
1053 else {
1054 copy_v4_v4(outrgb, rgba);
1055 }
1056 }
1057 }
1058 else {
1059 copy_v4_v4(outrgb, rgba);
1060 }
1061 /* write into brush buffer */
1062 xo = out_off[0] + x;
1063 yo = out_off[1] + y;
1064 paint_2d_ibuf_rgb_set(ibufb, xo, yo, false, outrgb);
1065 }
1066 }
1067}
1068
1070 ImagePaintRegion *region, int destx, int desty, int srcx, int srcy, int width, int height)
1071{
1072 region->destx = destx;
1073 region->desty = desty;
1074 region->srcx = srcx;
1075 region->srcy = srcy;
1076 region->width = width;
1077 region->height = height;
1078}
1079
1081 ImBuf *dbuf,
1082 ImBuf *sbuf,
1083 short paint_tile)
1084{
1085 int destx = region->destx;
1086 int desty = region->desty;
1087 int srcx = region->srcx;
1088 int srcy = region->srcy;
1089 int width = region->width;
1090 int height = region->height;
1091 int origw, origh, w, h, tot = 0;
1092
1093 /* convert destination and source coordinates to be within image */
1094 if (paint_tile & PAINT_TILE_X) {
1095 destx = destx % dbuf->x;
1096 if (destx < 0) {
1097 destx += dbuf->x;
1098 }
1099 srcx = srcx % sbuf->x;
1100 if (srcx < 0) {
1101 srcx += sbuf->x;
1102 }
1103 }
1104 if (paint_tile & PAINT_TILE_Y) {
1105 desty = desty % dbuf->y;
1106 if (desty < 0) {
1107 desty += dbuf->y;
1108 }
1109 srcy = srcy % sbuf->y;
1110 if (srcy < 0) {
1111 srcy += sbuf->y;
1112 }
1113 }
1114 /* clip width of blending area to destination imbuf, to avoid writing the
1115 * same pixel twice */
1116 origw = w = (width > dbuf->x) ? dbuf->x : width;
1117 origh = h = (height > dbuf->y) ? dbuf->y : height;
1118
1119 /* clip within image */
1120 IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &w, &h);
1121 paint_2d_set_region(&region[tot++], destx, desty, srcx, srcy, w, h);
1122
1123 /* do 3 other rects if needed */
1124 if ((paint_tile & PAINT_TILE_X) && w < origw) {
1126 &region[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h);
1127 }
1128 if ((paint_tile & PAINT_TILE_Y) && h < origh) {
1130 &region[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h);
1131 }
1132 if ((paint_tile & PAINT_TILE_X) && (paint_tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) {
1133 paint_2d_set_region(&region[tot++],
1134 (destx + w) % dbuf->x,
1135 (desty + h) % dbuf->y,
1136 (srcx + w) % sbuf->x,
1137 (srcy + h) % sbuf->y,
1138 origw - w,
1139 origh - h);
1140 }
1141
1142 return tot;
1143}
1144
1145static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short paint_tile)
1146{
1147 ImagePaintRegion region[4];
1148 int a, tot;
1149
1150 paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y);
1151 tot = paint_2d_torus_split_region(region, ibufb, ibuf, paint_tile);
1152
1153 for (a = 0; a < tot; a++) {
1154 IMB_rectblend(ibufb,
1155 ibufb,
1156 ibuf,
1157 nullptr,
1158 nullptr,
1159 nullptr,
1160 0,
1161 region[a].destx,
1162 region[a].desty,
1163 region[a].destx,
1164 region[a].desty,
1165 region[a].srcx,
1166 region[a].srcy,
1167 region[a].width,
1168 region[a].height,
1170 false);
1171 }
1172}
1173
1174static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, const int *pos)
1175{
1176 /* NOTE: #allocImbuf returns zeroed memory, so regions outside image will
1177 * have zero alpha, and hence not be blended onto the image */
1178 int w = ibufb->x, h = ibufb->y, destx = 0, desty = 0, srcx = pos[0], srcy = pos[1];
1179 ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags);
1180
1181 IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h);
1182 IMB_rectblend(clonebuf,
1183 clonebuf,
1184 ibufb,
1185 nullptr,
1186 nullptr,
1187 nullptr,
1188 0,
1189 destx,
1190 desty,
1191 destx,
1192 desty,
1193 destx,
1194 desty,
1195 w,
1196 h,
1198 false);
1199 IMB_rectblend(clonebuf,
1200 clonebuf,
1201 ibuf,
1202 nullptr,
1203 nullptr,
1204 nullptr,
1205 0,
1206 destx,
1207 desty,
1208 destx,
1209 desty,
1210 srcx,
1211 srcy,
1212 w,
1213 h,
1215 false);
1216
1217 return clonebuf;
1218}
1219
1220static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2])
1221{
1222 ipos[0] = int(floorf(pos[0] - ibufb->x / 2));
1223 ipos[1] = int(floorf(pos[1] - ibufb->y / 2));
1224}
1225
1228 ImagePaintRegion *region,
1229 ImBuf *frombuf,
1230 float mask_max,
1231 short blend,
1232 int tilex,
1233 int tiley,
1234 int tilew,
1235 int tileh)
1236{
1237 ImBuf tmpbuf;
1239
1241
1242 for (int ty = tiley; ty <= tileh; ty++) {
1243 for (int tx = tilex; tx <= tilew; tx++) {
1244 /* retrieve original pixels + mask from undo buffer */
1245 ushort *mask;
1246 int origx = region->destx - tx * ED_IMAGE_UNDO_TILE_SIZE;
1247 int origy = region->desty - ty * ED_IMAGE_UNDO_TILE_SIZE;
1248
1249 if (tile->canvas->float_buffer.data) {
1251 &tmpbuf,
1252 static_cast<float *>(ED_image_paint_tile_find(
1253 undo_tiles, s->image, tile->canvas, &tile->iuser, tx, ty, &mask, false)),
1255 }
1256 else {
1258 &tmpbuf,
1259 static_cast<uchar *>(ED_image_paint_tile_find(
1260 undo_tiles, s->image, tile->canvas, &tile->iuser, tx, ty, &mask, false)),
1262 }
1263
1264 IMB_rectblend(tile->canvas,
1265 &tmpbuf,
1266 frombuf,
1267 mask,
1268 tile->cache.curve_mask_cache.curve_mask,
1269 tile->cache.tex_mask,
1270 mask_max,
1271 region->destx,
1272 region->desty,
1273 origx,
1274 origy,
1275 region->srcx,
1276 region->srcy,
1277 region->width,
1278 region->height,
1280 ((s->brush->flag & BRUSH_ACCUMULATE) != 0));
1281 }
1282 }
1283}
1284
1295
1296static void paint_2d_op_foreach_do(void *__restrict data_v,
1297 const int iter,
1298 const TaskParallelTLS *__restrict /*tls*/)
1299{
1302 data->tile,
1303 data->region,
1304 data->frombuf,
1305 data->mask_max,
1306 data->blend,
1307 data->tilex,
1308 iter,
1309 data->tilew,
1310 iter);
1311}
1312
1313static int paint_2d_op(void *state,
1315 const float lastpos[2],
1316 const float pos[2])
1317{
1319 const ImagePaintSettings &image_paint_settings = s->scene->toolsettings->imapaint;
1320 ImBuf *clonebuf = nullptr, *frombuf;
1321 ImBuf *canvas = tile->canvas;
1322 ImBuf *ibufb = tile->cache.ibuf;
1323 ImagePaintRegion region[4];
1324 short paint_tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y);
1325 short blend = s->blend;
1326 const float *offset = image_paint_settings.clone_offset;
1327 float liftpos[2];
1328 float mask_max = BKE_brush_alpha_get(s->scene, s->brush);
1329 int bpos[2], blastpos[2], bliftpos[2];
1330 int a, tot;
1331
1332 paint_2d_convert_brushco(ibufb, pos, bpos);
1333
1334 /* lift from canvas */
1336 paint_2d_lift_soften(s, tile, canvas, ibufb, bpos, paint_tile);
1338 }
1340 if (lastpos[0] == pos[0] && lastpos[1] == pos[1]) {
1341 return 0;
1342 }
1343
1344 paint_2d_convert_brushco(ibufb, lastpos, blastpos);
1345 paint_2d_lift_smear(canvas, ibufb, blastpos, paint_tile);
1347 }
1349 liftpos[0] = pos[0] - offset[0] * canvas->x;
1350 liftpos[1] = pos[1] - offset[1] * canvas->y;
1351
1352 paint_2d_convert_brushco(ibufb, liftpos, bliftpos);
1353 clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos);
1354 }
1355
1356 frombuf = (clonebuf) ? clonebuf : ibufb;
1357
1358 if (paint_tile) {
1359 paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
1360 tot = paint_2d_torus_split_region(region, canvas, frombuf, paint_tile);
1361 }
1362 else {
1363 paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
1364 tot = 1;
1365 }
1366
1367 /* blend into canvas */
1368 for (a = 0; a < tot; a++) {
1370 canvas,
1371 &tile->iuser,
1372 region[a].destx,
1373 region[a].desty,
1374 region[a].width,
1375 region[a].height,
1376 true);
1377
1378 if (s->do_masking) {
1379 /* masking, find original pixels tiles from undo buffer to composite over */
1380 int tilex, tiley, tilew, tileh;
1381
1382 imapaint_region_tiles(canvas,
1383 region[a].destx,
1384 region[a].desty,
1385 region[a].width,
1386 region[a].height,
1387 &tilex,
1388 &tiley,
1389 &tilew,
1390 &tileh);
1391
1392 if (tiley == tileh) {
1394 s, tile, &region[a], frombuf, mask_max, blend, tilex, tiley, tilew, tileh);
1395 }
1396 else {
1398 data.s = s;
1399 data.tile = tile;
1400 data.region = &region[a];
1401 data.frombuf = frombuf;
1402 data.mask_max = mask_max;
1403 data.blend = blend;
1404 data.tilex = tilex;
1405 data.tilew = tilew;
1406
1407 TaskParallelSettings settings;
1409 BLI_task_parallel_range(tiley, tileh + 1, &data, paint_2d_op_foreach_do, &settings);
1410 }
1411 }
1412 else {
1413 /* no masking, composite brush directly onto canvas */
1415 canvas,
1416 frombuf,
1417 nullptr,
1418 tile->cache.curve_mask_cache.curve_mask,
1419 tile->cache.tex_mask,
1420 mask_max,
1421 region[a].destx,
1422 region[a].desty,
1423 region[a].destx,
1424 region[a].desty,
1425 region[a].srcx,
1426 region[a].srcy,
1427 region[a].width,
1428 region[a].height,
1430 false);
1431 }
1432 }
1433
1434 if (clonebuf) {
1435 IMB_freeImBuf(clonebuf);
1436 }
1437
1438 return 1;
1439}
1440
1441static int paint_2d_canvas_set(ImagePaintState *s, const Paint *paint)
1442{
1443 /* set clone canvas */
1445 const ImagePaintSettings &image_paint_settings = s->scene->toolsettings->imapaint;
1446 Image *ima = image_paint_settings.clone;
1447 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr);
1448
1449 if (!ima || !ibuf || !(ibuf->byte_buffer.data || ibuf->float_buffer.data)) {
1450 BKE_image_release_ibuf(ima, ibuf, nullptr);
1451 return 0;
1452 }
1453
1454 s->clonecanvas = ibuf;
1455
1456 /* temporarily add float rect for cloning */
1459 }
1460 else if (!s->tiles[0].canvas->float_buffer.data && !s->clonecanvas->byte_buffer.data) {
1462 }
1463 }
1464
1465 /* set masking */
1466 s->do_masking = paint_use_opacity_masking(s->scene, paint, s->brush);
1467
1468 return 1;
1469}
1470
1472{
1473 for (int i = 0; i < s->num_tiles; i++) {
1474 BKE_image_release_ibuf(s->image, s->tiles[i].canvas, nullptr);
1475 }
1476 const ImagePaintSettings &image_paint_settings = s->scene->toolsettings->imapaint;
1477 BKE_image_release_ibuf(image_paint_settings.clone, s->clonecanvas, nullptr);
1478
1479 if (s->blurkernel) {
1481 MEM_delete(s->blurkernel);
1482 }
1483}
1484
1485static void paint_2d_transform_mouse(View2D *v2d, const float in[2], float out[2])
1486{
1487 UI_view2d_region_to_view(v2d, in[0], in[1], &out[0], &out[1]);
1488}
1489
1490static bool is_inside_tile(const int size[2], const float pos[2], const float brush[2])
1491{
1492 return (pos[0] >= -brush[0]) && (pos[0] < size[0] + brush[0]) && (pos[1] >= -brush[1]) &&
1493 (pos[1] < size[1] + brush[1]);
1494}
1495
1496static void paint_2d_uv_to_coord(ImagePaintTile *tile, const float uv[2], float coord[2])
1497{
1498 coord[0] = (uv[0] - tile->uv_origin[0]) * tile->size[0];
1499 coord[1] = (uv[1] - tile->uv_origin[1]) * tile->size[1];
1500}
1501
1502void paint_2d_stroke(void *ps,
1503 const float prev_mval[2],
1504 const float mval[2],
1505 const bool eraser,
1506 float pressure,
1507 float distance,
1508 float base_size)
1509{
1510 float new_uv[2], old_uv[2];
1511 ImagePaintState *s = static_cast<ImagePaintState *>(ps);
1512 BrushPainter *painter = s->painter;
1513
1514 const bool is_data = s->tiles[0].canvas->colormanage_flag & IMB_COLORMANAGE_IS_DATA;
1515
1516 s->blend = s->brush->blend;
1517 if (eraser) {
1519 }
1520
1521 UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &new_uv[0], &new_uv[1]);
1522 UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &old_uv[0], &old_uv[1]);
1523
1524 float last_uv[2], start_uv[2];
1525 UI_view2d_region_to_view(s->v2d, 0.0f, 0.0f, &start_uv[0], &start_uv[1]);
1526 if (painter->firsttouch) {
1527 /* paint exactly once on first touch */
1528 copy_v2_v2(last_uv, new_uv);
1529 }
1530 else {
1531 copy_v2_v2(last_uv, old_uv);
1532 }
1533
1534 const float uv_brush_size[2] = {
1535 (s->symmetry & PAINT_TILE_X) ? FLT_MAX : base_size / s->tiles[0].size[0],
1536 (s->symmetry & PAINT_TILE_Y) ? FLT_MAX : base_size / s->tiles[0].size[1]};
1537
1538 for (int i = 0; i < s->num_tiles; i++) {
1539 ImagePaintTile *tile = &s->tiles[i];
1540
1541 /* First test: Project brush into UV space, clip against tile. */
1542 const int uv_size[2] = {1, 1};
1543 float local_new_uv[2], local_old_uv[2];
1544 sub_v2_v2v2(local_new_uv, new_uv, tile->uv_origin);
1545 sub_v2_v2v2(local_old_uv, old_uv, tile->uv_origin);
1546 if (!(is_inside_tile(uv_size, local_new_uv, uv_brush_size) ||
1547 is_inside_tile(uv_size, local_old_uv, uv_brush_size)))
1548 {
1549 continue;
1550 }
1551
1552 /* Lazy tile loading to get size in pixels. */
1553 if (!paint_2d_ensure_tile_canvas(s, i)) {
1554 continue;
1555 }
1556
1557 float size = base_size * tile->radius_fac;
1558
1559 float new_coord[2], old_coord[2];
1560 paint_2d_uv_to_coord(tile, new_uv, new_coord);
1561 paint_2d_uv_to_coord(tile, old_uv, old_coord);
1562 if (painter->firsttouch) {
1563 paint_2d_uv_to_coord(tile, start_uv, tile->start_paintpos);
1564 }
1565 paint_2d_uv_to_coord(tile, last_uv, tile->last_paintpos);
1566
1567 /* Second check in pixel coordinates. */
1568 const float pixel_brush_size[] = {(s->symmetry & PAINT_TILE_X) ? FLT_MAX : size,
1569 (s->symmetry & PAINT_TILE_Y) ? FLT_MAX : size};
1570 if (!(is_inside_tile(tile->size, new_coord, pixel_brush_size) ||
1571 is_inside_tile(tile->size, old_coord, pixel_brush_size)))
1572 {
1573 continue;
1574 }
1575
1576 ImBuf *ibuf = tile->canvas;
1577
1578 /* OCIO_TODO: float buffers are now always linear, so always use color correction
1579 * this should probably be changed when texture painting color space is supported
1580 */
1582 tile,
1583 (ibuf->float_buffer.data != nullptr),
1584 !is_data,
1585 painter->cache_invert);
1586
1587 brush_painter_2d_refresh_cache(s, painter, tile, new_coord, mval, pressure, distance, size);
1588
1589 if (paint_2d_op(s, tile, old_coord, new_coord)) {
1590 tile->need_redraw = true;
1591 }
1592 }
1593
1594 painter->firsttouch = false;
1595}
1596
1598{
1599 Scene *scene = CTX_data_scene(C);
1601 ToolSettings *settings = scene->toolsettings;
1603 Brush *brush = BKE_paint_brush(&settings->imapaint.paint);
1604
1606
1608 s->v2d = &CTX_wm_region(C)->v2d;
1609 s->scene = scene;
1610
1611 s->brush = brush;
1612 s->brush_type = brush->image_brush_type;
1613 s->blend = brush->blend;
1614
1615 s->image = s->sima->image;
1616 s->symmetry = settings->imapaint.paint.symmetry_flags;
1617
1618 if (s->image == nullptr) {
1619 MEM_freeN(s);
1620 return nullptr;
1621 }
1622 if (BKE_image_has_packedfile(s->image) && s->image->rr != nullptr) {
1623 BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted");
1624 MEM_freeN(s);
1625 return nullptr;
1626 }
1627
1630 for (int i = 0; i < s->num_tiles; i++) {
1631 s->tiles[i].iuser = sima->iuser;
1632 }
1633
1634 zero_v2(s->tiles[0].uv_origin);
1635
1636 ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[0].iuser, nullptr);
1637 if (ibuf == nullptr) {
1638 MEM_freeN(s->tiles);
1639 MEM_freeN(s);
1640 return nullptr;
1641 }
1642
1643 if (ibuf->channels != 4) {
1644 BKE_image_release_ibuf(s->image, ibuf, nullptr);
1645 BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint");
1646 MEM_freeN(s->tiles);
1647 MEM_freeN(s);
1648 return nullptr;
1649 }
1650
1651 s->tiles[0].size[0] = ibuf->x;
1652 s->tiles[0].size[1] = ibuf->y;
1653 s->tiles[0].radius_fac = 1.0f;
1654
1655 s->tiles[0].canvas = ibuf;
1657
1658 /* Initialize offsets here, they're needed for the uv space clip test before lazy-loading the
1659 * tile properly. */
1660 int tile_idx = 0;
1661 for (ImageTile *tile = static_cast<ImageTile *>(s->image->tiles.first); tile;
1662 tile = tile->next, tile_idx++)
1663 {
1664 s->tiles[tile_idx].iuser.tile = tile->tile_number;
1665 s->tiles[tile_idx].uv_origin[0] = ((tile->tile_number - 1001) % 10);
1666 s->tiles[tile_idx].uv_origin[1] = ((tile->tile_number - 1001) / 10);
1667 }
1668
1669 if (!paint_2d_canvas_set(s, paint)) {
1670 MEM_freeN(s->tiles);
1671
1672 MEM_freeN(s);
1673 return nullptr;
1674 }
1675
1677 s->blurkernel = paint_new_blur_kernel(brush, false);
1678 }
1679
1681
1682 /* create painter */
1683 s->painter = brush_painter_2d_new(scene, paint, s->brush, mode == BRUSH_STROKE_INVERT);
1684
1685 return s;
1686}
1687
1688void paint_2d_redraw(const bContext *C, void *ps, bool final)
1689{
1690 ImagePaintState *s = static_cast<ImagePaintState *>(ps);
1691
1692 bool had_redraw = false;
1693 for (int i = 0; i < s->num_tiles; i++) {
1694 if (s->tiles[i].need_redraw) {
1695 ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, nullptr);
1696
1697 imapaint_image_update(s->sima, s->image, ibuf, &s->tiles[i].iuser, false);
1698
1699 BKE_image_release_ibuf(s->image, ibuf, nullptr);
1700
1701 s->tiles[i].need_redraw = false;
1702 had_redraw = true;
1703 }
1704 }
1705
1706 if (had_redraw) {
1708 if (s->sima == nullptr || !s->sima->lock) {
1710 }
1711 else {
1713 }
1714 }
1715
1716 if (final) {
1717 if (s->image && !(s->sima && s->sima->lock)) {
1719 }
1720
1721 /* compositor listener deals with updating */
1723 DEG_id_tag_update(&s->image->id, 0);
1724 }
1725}
1726
1728{
1729 ImagePaintState *s = static_cast<ImagePaintState *>(ps);
1730
1732 for (int i = 0; i < s->num_tiles; i++) {
1734 }
1735 MEM_delete(s->painter);
1736 MEM_freeN(s->tiles);
1738
1739 MEM_freeN(s);
1740}
1741
1742static void paint_2d_fill_add_pixel_byte(const int x_px,
1743 const int y_px,
1744 ImBuf *ibuf,
1745 BLI_Stack *stack,
1746 BLI_bitmap *touched,
1747 const float color[4],
1748 float threshold_sq)
1749{
1750 size_t coordinate;
1751
1752 if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) {
1753 return;
1754 }
1755
1756 coordinate = size_t(y_px) * ibuf->x + x_px;
1757
1758 if (!BLI_BITMAP_TEST(touched, coordinate)) {
1759 float color_f[4];
1760 uchar *color_b = ibuf->byte_buffer.data + 4 * coordinate;
1761 rgba_uchar_to_float(color_f, color_b);
1762 straight_to_premul_v4(color_f);
1763
1764 if (len_squared_v4v4(color_f, color) <= threshold_sq) {
1765 BLI_stack_push(stack, &coordinate);
1766 }
1767 BLI_BITMAP_SET(touched, coordinate, true);
1768 }
1769}
1770
1771static void paint_2d_fill_add_pixel_float(const int x_px,
1772 const int y_px,
1773 ImBuf *ibuf,
1774 BLI_Stack *stack,
1775 BLI_bitmap *touched,
1776 const float color[4],
1777 float threshold_sq)
1778{
1779 size_t coordinate;
1780
1781 if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) {
1782 return;
1783 }
1784
1785 coordinate = size_t(y_px) * ibuf->x + x_px;
1786
1787 if (!BLI_BITMAP_TEST(touched, coordinate)) {
1788 if (len_squared_v4v4(ibuf->float_buffer.data + 4 * coordinate, color) <= threshold_sq) {
1789 BLI_stack_push(stack, &coordinate);
1790 }
1791 BLI_BITMAP_SET(touched, coordinate, true);
1792 }
1793}
1794
1796{
1797 ImageUser *iuser = &s->tiles[0].iuser;
1798 for (int i = 0; i < s->num_tiles; i++) {
1799 if (s->tiles[i].iuser.tile == tile_number) {
1800 if (!paint_2d_ensure_tile_canvas(s, i)) {
1801 return nullptr;
1802 }
1803 iuser = &s->tiles[i].iuser;
1804 break;
1805 }
1806 }
1807
1808 return iuser;
1809}
1810
1812 const float color[3],
1813 Brush *br,
1814 const float mouse_init[2],
1815 const float mouse_final[2],
1816 void *ps)
1817{
1819 Image *ima = sima->image;
1820
1821 ImagePaintState *s = static_cast<ImagePaintState *>(ps);
1822
1823 ImBuf *ibuf;
1824 int x_px, y_px;
1825 uint color_b;
1826 float color_f[4];
1827 float strength = (s && br) ? BKE_brush_alpha_get(s->scene, br) : 1.0f;
1828
1829 bool do_float;
1830
1831 if (!ima) {
1832 return;
1833 }
1834
1835 View2D *v2d = s ? s->v2d : &CTX_wm_region(C)->v2d;
1836 float uv_origin[2];
1837 float image_init[2];
1838 paint_2d_transform_mouse(v2d, mouse_init, image_init);
1839
1840 int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_origin);
1841
1842 ImageUser local_iuser, *iuser;
1843 if (s != nullptr) {
1844 iuser = paint_2d_get_tile_iuser(s, tile_number);
1845 if (iuser == nullptr) {
1846 return;
1847 }
1848 }
1849 else {
1850 iuser = &local_iuser;
1851 BKE_imageuser_default(iuser);
1852 iuser->tile = tile_number;
1853 }
1854
1855 ibuf = BKE_image_acquire_ibuf(ima, iuser, nullptr);
1856 if (!ibuf) {
1857 return;
1858 }
1859
1860 do_float = (ibuf->float_buffer.data != nullptr);
1861 /* First check if our image is float. If it is we should correct the color to be in linear space.
1862 */
1863 if (!do_float) {
1864 rgb_float_to_uchar((uchar *)&color_b, color);
1865 *(((char *)&color_b) + 3) = strength * 255;
1866 }
1867 else {
1868 srgb_to_linearrgb_v3_v3(color_f, color);
1869 color_f[3] = strength;
1870 }
1871
1872 if (!mouse_final || !br) {
1873 /* first case, no image UV, fill the whole image */
1874 ED_imapaint_dirty_region(ima, ibuf, iuser, 0, 0, ibuf->x, ibuf->y, false);
1875
1876 if (do_float) {
1877 for (x_px = 0; x_px < ibuf->x; x_px++) {
1878 for (y_px = 0; y_px < ibuf->y; y_px++) {
1879 blend_color_mix_float(ibuf->float_buffer.data + 4 * (size_t(y_px) * ibuf->x + x_px),
1880 ibuf->float_buffer.data + 4 * (size_t(y_px) * ibuf->x + x_px),
1881 color_f);
1882 }
1883 }
1884 }
1885 else {
1886 for (x_px = 0; x_px < ibuf->x; x_px++) {
1887 for (y_px = 0; y_px < ibuf->y; y_px++) {
1888 blend_color_mix_byte(ibuf->byte_buffer.data + 4 * (size_t(y_px) * ibuf->x + x_px),
1889 ibuf->byte_buffer.data + 4 * (size_t(y_px) * ibuf->x + x_px),
1890 (uchar *)&color_b);
1891 }
1892 }
1893 }
1894 }
1895 else {
1896 /* second case, start sweeping the neighboring pixels, looking for pixels whose
1897 * value is within the brush fill threshold from the fill color */
1898 BLI_Stack *stack;
1899 BLI_bitmap *touched;
1900 size_t coordinate;
1901 int width = ibuf->x;
1902 float pixel_color[4];
1903 /* We are comparing to sum of three squared values
1904 * (assumed in range [0,1]), so need to multiply... */
1905 float threshold_sq = br->fill_threshold * br->fill_threshold * 3;
1906
1907 x_px = image_init[0] * ibuf->x;
1908 y_px = image_init[1] * ibuf->y;
1909
1910 if (x_px >= ibuf->x || x_px < 0 || y_px > ibuf->y || y_px < 0) {
1911 BKE_image_release_ibuf(ima, ibuf, nullptr);
1912 return;
1913 }
1914
1915 /* change image invalidation method later */
1916 ED_imapaint_dirty_region(ima, ibuf, iuser, 0, 0, ibuf->x, ibuf->y, false);
1917
1918 stack = BLI_stack_new(sizeof(size_t), __func__);
1919 touched = BLI_BITMAP_NEW(size_t(ibuf->x) * ibuf->y, "bucket_fill_bitmap");
1920
1921 coordinate = (size_t(y_px) * ibuf->x + x_px);
1922
1923 if (do_float) {
1924 copy_v4_v4(pixel_color, ibuf->float_buffer.data + 4 * coordinate);
1925 }
1926 else {
1927 uchar *pixel_color_b = ibuf->byte_buffer.data + 4 * coordinate;
1928 rgba_uchar_to_float(pixel_color, pixel_color_b);
1929 straight_to_premul_v4(pixel_color);
1930 }
1931
1932 BLI_stack_push(stack, &coordinate);
1933 BLI_BITMAP_SET(touched, coordinate, true);
1934
1935 if (do_float) {
1936 while (!BLI_stack_is_empty(stack)) {
1937 BLI_stack_pop(stack, &coordinate);
1938
1939 IMB_blend_color_float(ibuf->float_buffer.data + 4 * (coordinate),
1940 ibuf->float_buffer.data + 4 * (coordinate),
1941 color_f,
1942 IMB_BlendMode(br->blend));
1943
1944 /* reconstruct the coordinates here */
1945 x_px = coordinate % width;
1946 y_px = coordinate / width;
1947
1949 x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq);
1951 x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq);
1953 x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq);
1955 x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq);
1957 x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq);
1959 x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq);
1961 x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq);
1963 x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq);
1964 }
1965 }
1966 else {
1967 while (!BLI_stack_is_empty(stack)) {
1968 BLI_stack_pop(stack, &coordinate);
1969
1970 IMB_blend_color_byte(ibuf->byte_buffer.data + 4 * coordinate,
1971 ibuf->byte_buffer.data + 4 * coordinate,
1972 (uchar *)&color_b,
1973 IMB_BlendMode(br->blend));
1974
1975 /* reconstruct the coordinates here */
1976 x_px = coordinate % width;
1977 y_px = coordinate / width;
1978
1980 x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq);
1982 x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq);
1984 x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq);
1986 x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq);
1988 x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq);
1990 x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq);
1992 x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq);
1994 x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq);
1995 }
1996 }
1997
1998 MEM_freeN(touched);
1999 BLI_stack_free(stack);
2000 }
2001
2002 imapaint_image_update(sima, ima, ibuf, iuser, false);
2004
2005 BKE_image_release_ibuf(ima, ibuf, nullptr);
2006
2008}
2009
2011 const bContext *C, Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps)
2012{
2014 Image *ima = sima->image;
2015 ImagePaintState *s = static_cast<ImagePaintState *>(ps);
2016
2017 ImBuf *ibuf;
2018 int x_px, y_px;
2019 uint color_b;
2020 float color_f[4];
2021 float image_init[2], image_final[2];
2022 float tangent[2];
2023 float line_len_sq_inv, line_len;
2024 const float brush_alpha = BKE_brush_alpha_get(s->scene, br);
2025
2026 bool do_float;
2027
2028 if (ima == nullptr) {
2029 return;
2030 }
2031
2032 float uv_origin[2];
2033 int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_origin);
2034 ImageUser *iuser = paint_2d_get_tile_iuser(s, tile_number);
2035 if (!iuser) {
2036 return;
2037 }
2038
2039 ibuf = BKE_image_acquire_ibuf(ima, iuser, nullptr);
2040 if (ibuf == nullptr) {
2041 return;
2042 }
2043
2044 paint_2d_transform_mouse(s->v2d, mouse_final, image_final);
2045 paint_2d_transform_mouse(s->v2d, mouse_init, image_init);
2046 sub_v2_v2(image_init, uv_origin);
2047 sub_v2_v2(image_final, uv_origin);
2048
2049 image_final[0] *= ibuf->x;
2050 image_final[1] *= ibuf->y;
2051
2052 image_init[0] *= ibuf->x;
2053 image_init[1] *= ibuf->y;
2054
2055 /* some math to get needed gradient variables */
2056 sub_v2_v2v2(tangent, image_final, image_init);
2057 line_len = len_squared_v2(tangent);
2058 line_len_sq_inv = 1.0f / line_len;
2059 line_len = sqrtf(line_len);
2060
2061 do_float = (ibuf->float_buffer.data != nullptr);
2062
2063 /* this will be substituted by something else when selection is available */
2064 ED_imapaint_dirty_region(ima, ibuf, iuser, 0, 0, ibuf->x, ibuf->y, false);
2065
2066 if (do_float) {
2067 for (x_px = 0; x_px < ibuf->x; x_px++) {
2068 for (y_px = 0; y_px < ibuf->y; y_px++) {
2069 float f;
2070 const float p[2] = {x_px - image_init[0], y_px - image_init[1]};
2071
2072 switch (br->gradient_fill_mode) {
2073 case BRUSH_GRADIENT_LINEAR: {
2074 f = dot_v2v2(p, tangent) * line_len_sq_inv;
2075 break;
2076 }
2078 default: {
2079 f = len_v2(p) / line_len;
2080 break;
2081 }
2082 }
2083 BKE_colorband_evaluate(br->gradient, f, color_f);
2084 /* convert to premultiplied */
2085 mul_v3_fl(color_f, color_f[3]);
2086 color_f[3] *= brush_alpha;
2087 IMB_blend_color_float(ibuf->float_buffer.data + 4 * (size_t(y_px) * ibuf->x + x_px),
2088 ibuf->float_buffer.data + 4 * (size_t(y_px) * ibuf->x + x_px),
2089 color_f,
2090 IMB_BlendMode(br->blend));
2091 }
2092 }
2093 }
2094 else {
2095 for (x_px = 0; x_px < ibuf->x; x_px++) {
2096 for (y_px = 0; y_px < ibuf->y; y_px++) {
2097 float f;
2098 const float p[2] = {x_px - image_init[0], y_px - image_init[1]};
2099
2100 switch (br->gradient_fill_mode) {
2101 case BRUSH_GRADIENT_LINEAR: {
2102 f = dot_v2v2(p, tangent) * line_len_sq_inv;
2103 break;
2104 }
2106 default: {
2107 f = len_v2(p) / line_len;
2108 break;
2109 }
2110 }
2111
2112 BKE_colorband_evaluate(br->gradient, f, color_f);
2113 linearrgb_to_srgb_v3_v3(color_f, color_f);
2114 rgba_float_to_uchar((uchar *)&color_b, color_f);
2115 ((uchar *)&color_b)[3] *= brush_alpha;
2116 IMB_blend_color_byte(ibuf->byte_buffer.data + 4 * (size_t(y_px) * ibuf->x + x_px),
2117 ibuf->byte_buffer.data + 4 * (size_t(y_px) * ibuf->x + x_px),
2118 (uchar *)&color_b,
2119 IMB_BlendMode(br->blend));
2120 }
2121 }
2122 }
2123
2124 imapaint_image_update(sima, ima, ibuf, iuser, false);
2126
2127 BKE_image_release_ibuf(ima, ibuf, nullptr);
2128
2130}
float BKE_brush_sample_tex_3d(const Scene *scene, const Brush *br, const MTex *mtex, const float point[3], float rgba[4], int thread, ImagePool *pool)
Definition brush.cc:877
const std::optional< BrushColorJitterSettings > BKE_brush_color_jitter_get_settings(const Scene *scene, const Paint *paint, const Brush *brush)
Definition brush.cc:1137
float BKE_brush_alpha_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1269
float BKE_brush_sample_masktex(const Scene *scene, Brush *br, const float point[2], int thread, ImagePool *pool)
Definition brush.cc:1000
bool BKE_colorband_evaluate(const ColorBand *coba, float in, float out[4])
Definition colorband.cc:395
SpaceImage * CTX_wm_space_image(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
int BKE_image_get_tile_from_pos(Image *ima, const float uv[2], float r_uv[2], float r_ofs[2])
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
void BKE_image_pool_free(ImagePool *pool)
ImagePool * BKE_image_pool_new()
void BKE_imageuser_default(ImageUser *iuser)
void BKE_image_free_gputextures(Image *ima)
Definition image_gpu.cc:570
bool BKE_image_has_packedfile(const Image *image)
blender::float3 seed_hsv_jitter()
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:467
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
blender::ocio::Display ColorManagedDisplay
Definition BLF_api.hh:35
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:37
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:61
#define BLI_BITMAP_SET(_bitmap, _index, _set)
Definition BLI_bitmap.h:99
unsigned int BLI_bitmap
Definition BLI_bitmap.h:13
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE int min_ii(int a, int b)
MINLINE void straight_uchar_to_premul_float(float result[4], const unsigned char color[4])
MINLINE void straight_to_premul_v4(float color[4])
void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4])
void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
void rgb_float_to_uchar(unsigned char r_col[3], const float col_f[3])
MINLINE void premul_float_to_straight_uchar(unsigned char *result, const float color[4])
MINLINE void blend_color_add_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_mix_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_mix_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4])
MINLINE void mul_v4_fl(float r[4], float f)
MINLINE void sub_v2_v2v2_int(int r[2], const int a[2], const int b[2])
MINLINE void add_v4_v4(float r[4], const float a[4])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v4(float r[4])
MINLINE float len_squared_v4v4(const float a[4], const float b[4]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v2(float r[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
void BLI_stack_pop(BLI_Stack *stack, void *dst) ATTR_NONNULL()
Definition stack.cc:138
void BLI_stack_push(BLI_Stack *stack, const void *src) ATTR_NONNULL()
Definition stack.cc:132
bool BLI_stack_is_empty(const BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.cc:250
void BLI_stack_free(BLI_Stack *stack) ATTR_NONNULL()
Definition stack.cc:96
#define BLI_stack_new(esize, descr)
unsigned char uchar
unsigned int uint
unsigned short ushort
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition task_range.cc:99
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:221
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ BRUSH_ACCUMULATE
@ BRUSH_DIR_IN
@ BRUSH_ANCHORED
@ BRUSH_USE_GRADIENT
@ IMAGE_PAINT_BRUSH_TYPE_DRAW
@ IMAGE_PAINT_BRUSH_TYPE_CLONE
@ IMAGE_PAINT_BRUSH_TYPE_SOFTEN
@ IMAGE_PAINT_BRUSH_TYPE_SMEAR
@ BRUSH_GRADIENT_SPACING_CLAMP
@ BRUSH_GRADIENT_SPACING_REPEAT
@ BRUSH_GRADIENT_LINEAR
@ BRUSH_GRADIENT_RADIAL
Object is a sort of wrapper for general info.
@ PAINT_TILE_Y
@ PAINT_TILE_X
@ MTEX_MAP_MODE_3D
@ MTEX_MAP_MODE_STENCIL
@ MTEX_MAP_MODE_RANDOM
@ MTEX_MAP_MODE_VIEW
void * ED_image_paint_tile_find(PaintTileMap *paint_tile_map, Image *image, ImBuf *ibuf, ImageUser *iuser, int x_tile, int y_tile, unsigned short **r_mask, bool validate)
void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, ImageUser *iuser, int x, int y, int w, int h, bool find_old)
#define ED_IMAGE_UNDO_TILE_SIZE
Definition ED_paint.hh:114
PaintTileMap * ED_image_paint_tile_map_get()
void ED_imapaint_clear_partial_redraw()
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3])
const ColorManagedDisplay * IMB_colormanagement_display_get_named(const char *name)
void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], const ColorManagedDisplay *display)
void IMB_rectblend_threaded(ImBuf *dbuf, const ImBuf *obuf, const ImBuf *sbuf, unsigned short *dmask, const unsigned short *curvemask, const unsigned short *texmask, float mask_max, int destx, int desty, int origx, int origy, int srcx, int srcy, int width, int height, IMB_BlendMode mode, bool accumulate)
Definition rectop.cc:939
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, ImBufOwnership ownership)
void IMB_byte_from_float(ImBuf *ibuf)
void IMB_blend_color_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4], IMB_BlendMode mode)
Definition rectop.cc:29
void IMB_rectclip(ImBuf *dbuf, const ImBuf *sbuf, int *destx, int *desty, int *srcx, int *srcy, int *width, int *height)
Definition rectop.cc:308
IMB_BlendMode
Definition IMB_imbuf.hh:185
@ IMB_BLEND_ERASE_ALPHA
Definition IMB_imbuf.hh:192
@ IMB_BLEND_COPY_RGB
Definition IMB_imbuf.hh:213
@ IMB_BLEND_COPY_ALPHA
Definition IMB_imbuf.hh:214
@ IMB_BLEND_COPY
Definition IMB_imbuf.hh:212
@ IMB_BLEND_INTERPOLATE
Definition IMB_imbuf.hh:210
void IMB_freeImBuf(ImBuf *ibuf)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
void IMB_rectblend(ImBuf *dbuf, const ImBuf *obuf, const ImBuf *sbuf, unsigned short *dmask, const unsigned short *curvemask, const unsigned short *texmask, float mask_max, int destx, int desty, int origx, int origy, int srcx, int srcy, int width, int height, IMB_BlendMode mode, bool accumulate)
Definition rectop.cc:476
void IMB_blend_color_float(float dst[4], const float src1[4], const float src2[4], IMB_BlendMode mode)
Definition rectop.cc:117
bool IMB_initImBuf(ImBuf *ibuf, unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership)
void IMB_float_from_byte(ImBuf *ibuf)
@ IMB_COLORMANAGE_IS_DATA
@ IB_DO_NOT_TAKE_OWNERSHIP
@ IB_float_data
@ IB_byte_data
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
void UI_view2d_view_to_region_fl(const View2D *v2d, float x, float y, float *r_region_x, float *r_region_y) ATTR_NONNULL()
Definition view2d.cc:1738
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1667
#define NA_EDITED
Definition WM_types.hh:581
#define NC_IMAGE
Definition WM_types.hh:381
#define NA_PAINTING
Definition WM_types.hh:588
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
#define floorf(x)
#define fabsf(x)
#define sqrtf(x)
uint pos
#define in
#define out
float distance(VecOp< float, D >, VecOp< float, D >) RET
#define MEM_SAFE_FREE(v)
int count
CCL_NAMESPACE_BEGIN ccl_device float invert(const float color, const float factor)
Definition invert.h:11
const ccl_global KernelWorkTile * tile
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static ulong state[N]
void paint_delete_blur_kernel(BlurKernel *kernel)
void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, ImageUser *iuser, short texpaint)
void paint_brush_init_tex(Brush *brush)
bool paint_use_opacity_masking(const Scene *scene, const Paint *paint, const Brush *brush)
BlurKernel * paint_new_blur_kernel(Brush *br, bool proj)
void paint_brush_color_get(Scene *scene, const Paint *paint, Brush *br, std::optional< blender::float3 > &initial_hsv_jitter, bool color_correction, bool invert, float distance, float pressure, const ColorManagedDisplay *display, float r_color[3])
void paint_brush_exit_tex(Brush *brush)
void imapaint_region_tiles(ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th)
static ushort * brush_painter_mask_ibuf_new(BrushPainter *painter, const int size)
static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf, short paint_tile)
static void brush_painter_mask_imbuf_update(BrushPainter *painter, ImagePaintTile *tile, const ushort *tex_mask_old, int origx, int origy, int w, int h, int xt, int yt, const int diameter)
static void paint_2d_fill_add_pixel_float(const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, const float color[4], float threshold_sq)
static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short paint_tile, float w)
static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2])
ImagePaintTileState
@ PAINT2D_TILE_READY
@ PAINT2D_TILE_MISSING
@ PAINT2D_TILE_UNINITIALIZED
static void paint_2d_canvas_free(ImagePaintState *s)
static ImBuf * paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, const int *pos)
static void brush_painter_cache_2d_free(BrushPainterCache *cache)
static int paint_2d_op(void *state, ImagePaintTile *tile, const float lastpos[2], const float pos[2])
static void paint_2d_lift_soften(ImagePaintState *s, ImagePaintTile *tile, ImBuf *ibuf, ImBuf *ibufb, const int *pos, const short paint_tile)
static void paint_2d_do_making_brush(ImagePaintState *s, ImagePaintTile *tile, ImagePaintRegion *region, ImBuf *frombuf, float mask_max, short blend, int tilex, int tiley, int tilew, int tileh)
void * paint_2d_new_stroke(bContext *C, wmOperator *op, int mode)
void paint_2d_bucket_fill(const bContext *C, const float color[3], Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps)
void paint_2d_gradient_fill(const bContext *C, Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps)
static int paint_2d_canvas_set(ImagePaintState *s, const Paint *paint)
static void brush_painter_imbuf_partial_update(BrushPainter *painter, ImagePaintTile *tile, const float pos[2], const int diameter)
void paint_2d_stroke_done(void *ps)
static void paint_2d_op_foreach_do(void *__restrict data_v, const int iter, const TaskParallelTLS *__restrict)
static void paint_2d_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const bool is_torus, const float rgb[4])
static bool paint_2d_ensure_tile_canvas(ImagePaintState *s, int i)
static void paint_2d_transform_mouse(View2D *v2d, const float in[2], float out[2])
static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short paint_tile)
static ImBuf * brush_painter_imbuf_new(BrushPainter *painter, ImagePaintTile *tile, const int size, float pressure, float distance)
void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float base_size)
static void paint_2d_uv_to_coord(ImagePaintTile *tile, const float uv[2], float coord[2])
static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, float r_rgb[4])
static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, ImagePaintTile *tile, const float pos[2], const int diameter)
static void brush_painter_2d_require_imbuf(Brush *brush, ImagePaintTile *tile, bool use_float, bool use_color_correction, bool invert)
static void paint_2d_set_region(ImagePaintRegion *region, int destx, int desty, int srcx, int srcy, int width, int height)
static ImageUser * paint_2d_get_tile_iuser(ImagePaintState *s, int tile_number)
static void brush_imbuf_tex_co(const rctf *mapping, int x, int y, float texco[3])
static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short paint_tile)
static BrushPainter * brush_painter_2d_new(Scene *scene, const Paint *paint, Brush *brush, bool invert)
static void brush_painter_2d_tex_mapping(ImagePaintState *s, ImagePaintTile *tile, const int diameter, const float pos[2], const float mouse[2], int mapmode, rctf *r_mapping)
void paint_2d_redraw(const bContext *C, void *ps, bool final)
static bool is_inside_tile(const int size[2], const float pos[2], const float brush[2])
static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, ImagePaintTile *tile, const float pos[2], const float mouse[2], float pressure, float distance, float size)
static void brush_painter_imbuf_update(BrushPainter *painter, ImagePaintTile *tile, ImBuf *oldtexibuf, int origx, int origy, int w, int h, int xt, int yt)
static void paint_2d_fill_add_pixel_byte(const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, const float color[4], float threshold_sq)
void paint_curve_mask_cache_free_data(CurveMaskCache *curve_mask_cache)
void paint_curve_mask_cache_update(CurveMaskCache *curve_mask_cache, const Brush *brush, const int diameter, const float radius, const float cursor_position[2])
@ BRUSH_STROKE_INVERT
static void image_init(Image *ima, short source, short type)
#define FLT_MAX
Definition stdcycles.h:14
float * wdata
CurveMaskCache curve_mask_cache
ImagePool * pool
std::optional< blender::float3 > initial_hsv_jitter
const Paint * paint
struct ColorBand * gradient
struct MTex mtex
float sharp_threshold
char image_brush_type
float fill_threshold
char gradient_fill_mode
short blend
char gradient_stroke_mode
struct MTex mask_mtex
int mask_pressure
Caching structure for curve mask.
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
int colormanage_flag
unsigned char planes
ImagePaintTile * tiles
BlurKernel * blurkernel
SpaceImage * sima
BrushPainter * painter
BrushPainterCache cache
ImagePaintTileState state
float start_paintpos[2]
ListBase tiles
struct RenderResult * rr
void * first
char brush_map_mode
struct Tex * tex
ImagePaintTile * tile
ImagePaintRegion * region
ImagePaintState * s
int symmetry_flags
struct ToolSettings * toolsettings
ColorManagedDisplaySettings display_settings
struct ImageUser iuser
struct Image * image
struct ImagePaintSettings imapaint
struct UnifiedPaintSettings unified_paint_settings
float xmax
float xmin
float ymax
float ymin
struct ReportList * reports
i
Definition text_draw.cc:230
static int blend(const Tex *tex, const float texvec[3], TexResult *texres)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4225