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