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