Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Stockner <lukas.stockner@freenet.de>2019-12-12 18:06:08 +0300
committerLukas Stockner <lukas.stockner@freenet.de>2019-12-12 20:40:37 +0300
commitc30d6571bb47734e0bcb2fced5cf11cb6d8b1169 (patch)
treef92f065147296e6c72e6cd85f26711157d81e09c /source/blender/editors/sculpt_paint
parentd7a8a606889fed58775c88bfdc079bee3c9333e2 (diff)
Add support for tiled images and the UDIM naming scheme
This patch contains the work that I did during my week at the Code Quest - adding support for tiled images to Blender. With this patch, images now contain a list of tiles. By default, this just contains one tile, but if the source type is set to Tiled, the user can add additional tiles. When acquiring an ImBuf, the tile to be loaded is specified in the ImageUser. Therefore, code that is not yet aware of tiles will just access the default tile as usual. The filenames of the additional tiles are derived from the original filename according to the UDIM naming scheme - the filename contains an index that is calculated as (1001 + 10*<y coordinate of the tile> + <x coordinate of the tile>), where the x coordinate never goes above 9. Internally, the various tiles are stored in a cache just like sequences. When acquired for the first time, the code will try to load the corresponding file from disk. Alternatively, a new operator can be used to initialize the tile similar to the New Image operator. The following features are supported so far: - Automatic detection and loading of all tiles when opening the first tile (1001) - Saving all tiles - Adding and removing tiles - Filling tiles with generated images - Drawing all tiles in the Image Editor - Viewing a tiled grid even if no image is selected - Rendering tiled images in Eevee - Rendering tiled images in Cycles (in SVM mode) - Automatically skipping loading of unused tiles in Cycles - 2D texture painting (also across tiles) - 3D texture painting (also across tiles, only limitation: individual faces can not cross tile borders) - Assigning custom labels to individual tiles (drawn in the Image Editor instead of the ID) - Different resolutions between tiles There still are some missing features that will be added later (see T72390): - Workbench engine support - Packing/Unpacking support - Baking support - Cycles OSL support - many other Blender features that rely on images Thanks to Brecht for the review and to all who tested the intermediate versions! Differential Revision: https://developer.blender.org/D3509
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c21
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c722
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c118
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h2
4 files changed, 559 insertions, 304 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 0b770f17314..de09a52258f 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -119,7 +119,8 @@ void imapaint_region_tiles(
*ty = (y >> ED_IMAGE_UNDO_TILE_BITS);
}
-void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h, bool find_old)
+void ED_imapaint_dirty_region(
+ Image *ima, ImBuf *ibuf, int tile_number, int x, int y, int w, int h, bool find_old)
{
ImBuf *tmpibuf = NULL;
int tilex, tiley, tilew, tileh, tx, ty;
@@ -152,7 +153,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int
for (ty = tiley; ty <= tileh; ty++) {
for (tx = tilex; tx <= tilew; tx++) {
ED_image_paint_tile_push(
- undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old);
+ undo_tiles, ima, ibuf, &tmpibuf, tile_number, tx, ty, NULL, NULL, false, find_old);
}
}
@@ -163,7 +164,8 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int
}
}
-void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint)
+void imapaint_image_update(
+ SpaceImage *sima, Image *image, ImBuf *ibuf, ImageUser *iuser, short texpaint)
{
if (imapaintpartial.x1 != imapaintpartial.x2 && imapaintpartial.y1 != imapaintpartial.y2) {
IMB_partial_display_buffer_update_delayed(
@@ -180,8 +182,7 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te
int h = imapaintpartial.y2 - imapaintpartial.y1;
if (w && h) {
/* Testing with partial update in uv editor too */
- GPU_paint_update_image(
- image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h);
+ GPU_paint_update_image(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h);
}
}
}
@@ -623,7 +624,7 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
else {
srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
}
- paint_2d_bucket_fill(C, color, brush, pop->prevmouse, pop->custom_paint);
+ paint_2d_bucket_fill(C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
}
else {
paint_proj_stroke(C,
@@ -1297,7 +1298,10 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op)
+void ED_imapaint_bucket_fill(struct bContext *C,
+ float color[3],
+ wmOperator *op,
+ const int mouse[2])
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceImage *sima = CTX_wm_space_image(C);
@@ -1307,7 +1311,8 @@ void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op)
ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
- paint_2d_bucket_fill(C, color, NULL, NULL, NULL);
+ float mouse_init[2] = {mouse[0], mouse[1]};
+ paint_2d_bucket_fill(C, color, NULL, mouse_init, NULL, NULL);
BKE_undosys_step_push(wm->undo_stack, C, op->type->name);
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index 004caae8a00..06d79b8a49d 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -30,6 +30,7 @@
#include "DNA_space_types.h"
#include "DNA_object_types.h"
+#include "BLI_listbase.h"
#include "BLI_math_color_blend.h"
#include "BLI_stack.h"
#include "BLI_bitmap.h"
@@ -84,22 +85,21 @@ typedef struct BrushPainterCache {
unsigned short *tex_mask_old;
unsigned int tex_mask_old_w;
unsigned int tex_mask_old_h;
+
+ int image_size[2];
} BrushPainterCache;
typedef struct BrushPainter {
Scene *scene;
Brush *brush;
- float lastpaintpos[2]; /* position of last paint op */
- float startpaintpos[2]; /* position of first paint */
-
short firsttouch; /* first paint op */
struct ImagePool *pool; /* image pool */
rctf tex_mapping; /* texture coordinate mapping */
rctf mask_mapping; /* mask texture coordinate mapping */
- BrushPainterCache cache;
+ bool cache_invert;
} BrushPainter;
typedef struct ImagePaintRegion {
@@ -108,6 +108,27 @@ typedef struct ImagePaintRegion {
int width, height;
} ImagePaintRegion;
+typedef enum ImagePaintTileState {
+ PAINT2D_TILE_UNINITIALIZED = 0,
+ PAINT2D_TILE_MISSING,
+ PAINT2D_TILE_READY,
+} ImagePaintTileState;
+
+typedef struct ImagePaintTile {
+ ImageUser iuser;
+ ImBuf *canvas;
+ float radius_fac;
+ int size[2];
+ float uv_origin[2]; /* Stores the position of this tile in UV space. */
+ bool need_redraw;
+ BrushPainterCache cache;
+
+ ImagePaintTileState state;
+
+ float last_paintpos[2]; /* position of last paint op */
+ float start_paintpos[2]; /* position of first paint */
+} ImagePaintTile;
+
typedef struct ImagePaintState {
BrushPainter *painter;
SpaceImage *sima;
@@ -119,10 +140,7 @@ typedef struct ImagePaintState {
Brush *brush;
short tool, blend;
Image *image;
- ImBuf *canvas;
ImBuf *clonecanvas;
- const char *warnpackedfile;
- const char *warnmultifile;
bool do_masking;
@@ -133,7 +151,8 @@ typedef struct ImagePaintState {
int do_facesel;
int symmetry;
- bool need_redraw;
+ ImagePaintTile *tiles;
+ int num_tiles;
BlurKernel *blurkernel;
} ImagePaintState;
@@ -145,63 +164,60 @@ static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush, bool inver
painter->brush = brush;
painter->scene = scene;
painter->firsttouch = 1;
- painter->cache.lastdiameter = -1; /* force ibuf create in refresh */
- painter->cache.invert = invert;
+ painter->cache_invert = invert;
return painter;
}
-static void brush_painter_2d_require_imbuf(BrushPainter *painter,
- bool use_float,
- bool use_color_correction)
+static void brush_painter_2d_require_imbuf(
+ Brush *brush, ImagePaintTile *tile, bool use_float, bool use_color_correction, bool invert)
{
- Brush *brush = painter->brush;
+ BrushPainterCache *cache = &tile->cache;
- if ((painter->cache.use_float != use_float)) {
- if (painter->cache.ibuf) {
- IMB_freeImBuf(painter->cache.ibuf);
+ if ((cache->use_float != use_float)) {
+ if (cache->ibuf) {
+ IMB_freeImBuf(cache->ibuf);
}
- if (painter->cache.curve_mask) {
- MEM_freeN(painter->cache.curve_mask);
+ if (cache->curve_mask) {
+ MEM_freeN(cache->curve_mask);
}
- if (painter->cache.tex_mask) {
- MEM_freeN(painter->cache.tex_mask);
+ if (cache->tex_mask) {
+ MEM_freeN(cache->tex_mask);
}
- if (painter->cache.tex_mask_old) {
- MEM_freeN(painter->cache.tex_mask_old);
+ if (cache->tex_mask_old) {
+ MEM_freeN(cache->tex_mask_old);
}
- painter->cache.ibuf = NULL;
- painter->cache.curve_mask = NULL;
- painter->cache.tex_mask = NULL;
- painter->cache.lastdiameter = -1; /* force ibuf create in refresh */
- }
-
- painter->cache.use_float = use_float;
- painter->cache.use_color_correction = use_float && use_color_correction;
- painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ?
- true :
- false;
- painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false;
+ cache->ibuf = NULL;
+ cache->curve_mask = NULL;
+ cache->tex_mask = NULL;
+ cache->lastdiameter = -1; /* force ibuf create in refresh */
+ cache->invert = invert;
+ }
+
+ cache->use_float = use_float;
+ cache->use_color_correction = use_float && use_color_correction;
+ cache->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true :
+ false;
+ cache->is_maskbrush = (brush->mask_mtex.tex) ? true : false;
}
-static void brush_painter_2d_free(BrushPainter *painter)
+static void brush_painter_cache_2d_free(BrushPainterCache *cache)
{
- if (painter->cache.ibuf) {
- IMB_freeImBuf(painter->cache.ibuf);
+ if (cache->ibuf) {
+ IMB_freeImBuf(cache->ibuf);
}
- if (painter->cache.texibuf) {
- IMB_freeImBuf(painter->cache.texibuf);
+ if (cache->texibuf) {
+ IMB_freeImBuf(cache->texibuf);
}
- if (painter->cache.curve_mask) {
- MEM_freeN(painter->cache.curve_mask);
+ if (cache->curve_mask) {
+ MEM_freeN(cache->curve_mask);
}
- if (painter->cache.tex_mask) {
- MEM_freeN(painter->cache.tex_mask);
+ if (cache->tex_mask) {
+ MEM_freeN(cache->tex_mask);
}
- if (painter->cache.tex_mask_old) {
- MEM_freeN(painter->cache.tex_mask_old);
+ if (cache->tex_mask_old) {
+ MEM_freeN(cache->tex_mask_old);
}
- MEM_freeN(painter);
}
static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3])
@@ -212,7 +228,7 @@ static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3])
}
/* create a mask with the mask texture */
-static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int size)
+static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, const int size)
{
Scene *scene = painter->scene;
Brush *brush = painter->brush;
@@ -240,6 +256,7 @@ static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int si
/* update rectangular section of the brush image */
static void brush_painter_mask_imbuf_update(BrushPainter *painter,
+ ImagePaintTile *tile,
unsigned short *tex_mask_old,
int origx,
int origy,
@@ -247,10 +264,11 @@ static void brush_painter_mask_imbuf_update(BrushPainter *painter,
int h,
int xt,
int yt,
- int diameter)
+ const int diameter)
{
Scene *scene = painter->scene;
Brush *brush = painter->brush;
+ BrushPainterCache *cache = &tile->cache;
rctf tex_mapping = painter->mask_mapping;
struct ImagePool *pool = painter->pool;
unsigned short res;
@@ -259,8 +277,8 @@ static void brush_painter_mask_imbuf_update(BrushPainter *painter,
int x, y, thread = 0;
- unsigned short *tex_mask = painter->cache.tex_mask;
- unsigned short *tex_mask_cur = painter->cache.tex_mask_old;
+ unsigned short *tex_mask = cache->tex_mask;
+ unsigned short *tex_mask_cur = cache->tex_mask_old;
/* fill pixels */
for (y = origy; y < h; y++) {
@@ -280,8 +298,7 @@ static void brush_painter_mask_imbuf_update(BrushPainter *painter,
/* read from old texture buffer */
if (use_texture_old) {
- res = *(tex_mask_old +
- ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt)));
+ res = *(tex_mask_old + ((y - origy + yt) * cache->tex_mask_old_w + (x - origx + xt)));
}
/* write to new texture mask */
@@ -298,10 +315,11 @@ static void brush_painter_mask_imbuf_update(BrushPainter *painter,
* textures that stick to the surface where only part of the pixels are new
*/
static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter,
+ ImagePaintTile *tile,
const float pos[2],
- int diameter)
+ const int diameter)
{
- BrushPainterCache *cache = &painter->cache;
+ BrushPainterCache *cache = &tile->cache;
unsigned short *tex_mask_old;
int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2;
@@ -319,15 +337,16 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter,
if (tex_mask_old) {
ImBuf maskibuf;
ImBuf maskibuf_old;
- maskibuf.x = maskibuf.y = diameter;
+ maskibuf.x = diameter;
+ maskibuf.y = diameter;
maskibuf_old.x = cache->tex_mask_old_w;
maskibuf_old.y = cache->tex_mask_old_h;
srcx = srcy = 0;
w = cache->tex_mask_old_w;
h = cache->tex_mask_old_h;
- destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2);
- desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2);
+ destx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2);
+ desty = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2);
/* hack, use temporary rects so that clipping works */
IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h);
@@ -345,7 +364,8 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter,
/* blend existing texture in new position */
if ((x1 < x2) && (y1 < y2)) {
- brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter);
+ brush_painter_mask_imbuf_update(
+ painter, tile, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter);
}
if (tex_mask_old) {
@@ -354,16 +374,17 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter,
/* sample texture in new areas */
if ((0 < x1) && (0 < diameter)) {
- brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter);
+ brush_painter_mask_imbuf_update(painter, tile, NULL, 0, 0, x1, diameter, 0, 0, diameter);
}
if ((x2 < diameter) && (0 < diameter)) {
- brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter);
+ brush_painter_mask_imbuf_update(
+ painter, tile, NULL, x2, 0, diameter, diameter, 0, 0, diameter);
}
if ((x1 < x2) && (0 < y1)) {
- brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter);
+ brush_painter_mask_imbuf_update(painter, tile, NULL, x1, 0, x2, y1, 0, 0, diameter);
}
if ((x1 < x2) && (y2 < diameter)) {
- brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter);
+ brush_painter_mask_imbuf_update(painter, tile, NULL, x1, y2, x2, diameter, 0, 0, diameter);
}
/* through with sampling, now update sizes */
@@ -445,13 +466,12 @@ static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter,
}
/* create imbuf with brush color */
-static ImBuf *brush_painter_imbuf_new(BrushPainter *painter,
- int size,
- float pressure,
- float distance)
+static ImBuf *brush_painter_imbuf_new(
+ BrushPainter *painter, ImagePaintTile *tile, const int size, float pressure, float distance)
{
Scene *scene = painter->scene;
Brush *brush = painter->brush;
+ BrushPainterCache *cache = &tile->cache;
const char *display_device = scene->display_settings.display_device;
struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
@@ -459,9 +479,9 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter,
rctf tex_mapping = painter->tex_mapping;
struct ImagePool *pool = painter->pool;
- bool use_color_correction = painter->cache.use_color_correction;
- bool use_float = painter->cache.use_float;
- bool is_texbrush = painter->cache.is_texbrush;
+ bool use_color_correction = cache->use_color_correction;
+ bool use_float = cache->use_float;
+ bool is_texbrush = cache->is_texbrush;
int x, y, thread = 0;
float brush_rgb[3];
@@ -471,14 +491,8 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter,
/* get brush color */
if (brush->imagepaint_tool == PAINT_TOOL_DRAW) {
- paint_brush_color_get(scene,
- brush,
- use_color_correction,
- painter->cache.invert,
- distance,
- pressure,
- brush_rgb,
- display);
+ paint_brush_color_get(
+ scene, brush, use_color_correction, cache->invert, distance, pressure, brush_rgb, display);
}
else {
brush_rgb[0] = 1.0f;
@@ -526,11 +540,19 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter,
}
/* update rectangular section of the brush image */
-static void brush_painter_imbuf_update(
- BrushPainter *painter, ImBuf *oldtexibuf, int origx, int origy, int w, int h, int xt, int yt)
+static void brush_painter_imbuf_update(BrushPainter *painter,
+ ImagePaintTile *tile,
+ ImBuf *oldtexibuf,
+ int origx,
+ int origy,
+ int w,
+ int h,
+ int xt,
+ int yt)
{
Scene *scene = painter->scene;
Brush *brush = painter->brush;
+ BrushPainterCache *cache = &tile->cache;
const char *display_device = scene->display_settings.display_device;
struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
@@ -538,21 +560,21 @@ static void brush_painter_imbuf_update(
rctf tex_mapping = painter->tex_mapping;
struct ImagePool *pool = painter->pool;
- bool use_color_correction = painter->cache.use_color_correction;
- bool use_float = painter->cache.use_float;
- bool is_texbrush = painter->cache.is_texbrush;
+ bool use_color_correction = cache->use_color_correction;
+ bool use_float = cache->use_float;
+ bool is_texbrush = cache->is_texbrush;
bool use_texture_old = (oldtexibuf != NULL);
int x, y, thread = 0;
float brush_rgb[3];
- ImBuf *ibuf = painter->cache.ibuf;
- ImBuf *texibuf = painter->cache.texibuf;
+ ImBuf *ibuf = cache->ibuf;
+ ImBuf *texibuf = cache->texibuf;
/* get brush color */
if (brush->imagepaint_tool == PAINT_TOOL_DRAW) {
paint_brush_color_get(
- scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display);
+ scene, brush, use_color_correction, cache->invert, 0.0, 1.0, brush_rgb, display);
}
else {
brush_rgb[0] = 1.0f;
@@ -641,10 +663,11 @@ static void brush_painter_imbuf_update(
* can be considerably faster for brushes that change size due to pressure or
* textures that stick to the surface where only part of the pixels are new */
static void brush_painter_imbuf_partial_update(BrushPainter *painter,
+ ImagePaintTile *tile,
const float pos[2],
- int diameter)
+ const int diameter)
{
- BrushPainterCache *cache = &painter->cache;
+ BrushPainterCache *cache = &tile->cache;
ImBuf *oldtexibuf, *ibuf;
int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2;
@@ -663,8 +686,8 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter,
srcx = srcy = 0;
w = oldtexibuf->x;
h = oldtexibuf->y;
- destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2);
- desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2);
+ destx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2);
+ desty = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2);
IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h);
}
@@ -681,7 +704,7 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter,
/* blend existing texture in new position */
if ((x1 < x2) && (y1 < y2)) {
- brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy);
+ brush_painter_imbuf_update(painter, tile, oldtexibuf, x1, y1, x2, y2, srcx, srcy);
}
if (oldtexibuf) {
@@ -690,29 +713,30 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter,
/* sample texture in new areas */
if ((0 < x1) && (0 < ibuf->y)) {
- brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0);
+ brush_painter_imbuf_update(painter, tile, NULL, 0, 0, x1, ibuf->y, 0, 0);
}
if ((x2 < ibuf->x) && (0 < ibuf->y)) {
- brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0);
+ brush_painter_imbuf_update(painter, tile, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0);
}
if ((x1 < x2) && (0 < y1)) {
- brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0);
+ brush_painter_imbuf_update(painter, tile, NULL, x1, 0, x2, y1, 0, 0);
}
if ((x1 < x2) && (y2 < ibuf->y)) {
- brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0);
+ brush_painter_imbuf_update(painter, tile, NULL, x1, y2, x2, ibuf->y, 0, 0);
}
}
static void brush_painter_2d_tex_mapping(ImagePaintState *s,
- int diameter,
+ ImBuf *canvas,
+ const int diameter,
const float startpos[2],
const float pos[2],
const float mouse[2],
int mapmode,
rctf *mapping)
{
- float invw = 1.0f / (float)s->canvas->x;
- float invh = 1.0f / (float)s->canvas->y;
+ float invw = 1.0f / (float)canvas->x;
+ float invh = 1.0f / (float)canvas->y;
int xmin, ymin, xmax, ymax;
int ipos[2];
@@ -756,6 +780,7 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s,
static void brush_painter_2d_refresh_cache(ImagePaintState *s,
BrushPainter *painter,
+ ImagePaintTile *tile,
const float pos[2],
const float mouse[2],
float pressure,
@@ -765,7 +790,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
const Scene *scene = painter->scene;
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
Brush *brush = painter->brush;
- BrushPainterCache *cache = &painter->cache;
+ BrushPainterCache *cache = &tile->cache;
/* Adding 4 pixels of padding for brush antialiasing */
const int diameter = MAX2(1, size * 2) + 4;
@@ -782,7 +807,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
painter->pool = BKE_image_pool_new();
/* determine how can update based on textures used */
- if (painter->cache.is_texbrush) {
+ if (cache->is_texbrush) {
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) {
tex_rotation += ups->brush_rotation;
}
@@ -794,15 +819,16 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
}
brush_painter_2d_tex_mapping(s,
+ tile->canvas,
diameter,
- painter->startpaintpos,
+ tile->start_paintpos,
pos,
mouse,
brush->mtex.brush_map_mode,
&painter->tex_mapping);
}
- if (painter->cache.is_maskbrush) {
+ if (cache->is_maskbrush) {
bool renew_maxmask = false;
bool do_partial_update_mask = false;
/* invalidate case for all mapping modes */
@@ -822,7 +848,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
renew_maxmask = true;
}
- if ((diameter != cache->lastdiameter) || (mask_rotation != cache->last_mask_rotation) ||
+ if (diameter != cache->lastdiameter || (mask_rotation != cache->last_mask_rotation) ||
renew_maxmask) {
if (cache->tex_mask) {
MEM_freeN(cache->tex_mask);
@@ -830,15 +856,16 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
}
brush_painter_2d_tex_mapping(s,
+ tile->canvas,
diameter,
- painter->startpaintpos,
+ tile->start_paintpos,
pos,
mouse,
brush->mask_mtex.brush_map_mode,
&painter->mask_mapping);
if (do_partial_update_mask) {
- brush_painter_mask_imbuf_partial_update(painter, pos, diameter);
+ brush_painter_mask_imbuf_partial_update(painter, tile, pos, diameter);
}
else {
cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter);
@@ -856,8 +883,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size, pos);
/* detect if we need to recreate image brush buffer */
- if ((diameter != cache->lastdiameter) || (tex_rotation != cache->last_tex_rotation) ||
- do_random || update_color) {
+ if (diameter != cache->lastdiameter || (tex_rotation != cache->last_tex_rotation) || do_random ||
+ update_color) {
if (cache->ibuf) {
IMB_freeImBuf(cache->ibuf);
cache->ibuf = NULL;
@@ -865,11 +892,11 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
if (do_partial_update) {
/* do partial update of texture */
- brush_painter_imbuf_partial_update(painter, pos, diameter);
+ brush_painter_imbuf_partial_update(painter, tile, pos, diameter);
}
else {
/* create brush from scratch */
- cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance);
+ cache->ibuf = brush_painter_imbuf_new(painter, tile, diameter, pressure, distance);
}
cache->lastdiameter = diameter;
@@ -878,11 +905,11 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
}
else if (do_partial_update) {
/* do only partial update of texture */
- int dx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]);
- int dy = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]);
+ int dx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]);
+ int dy = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]);
if ((dx != 0) || (dy != 0)) {
- brush_painter_imbuf_partial_update(painter, pos, diameter);
+ brush_painter_imbuf_partial_update(painter, tile, pos, diameter);
}
}
@@ -890,6 +917,56 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
painter->pool = NULL;
}
+static bool paint_2d_ensure_tile_canvas(ImagePaintState *s, int i)
+{
+ if (i == 0) {
+ return true;
+ }
+ if (i >= s->num_tiles) {
+ return false;
+ }
+
+ if (s->tiles[i].state == PAINT2D_TILE_READY) {
+ return true;
+ }
+ if (s->tiles[i].state == PAINT2D_TILE_MISSING) {
+ return false;
+ }
+
+ s->tiles[i].cache.lastdiameter = -1;
+
+ s->tiles[i].iuser.ok = true;
+
+ ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL);
+ if (ibuf != NULL) {
+ if (ibuf->channels != 4) {
+ s->tiles[i].state = PAINT2D_TILE_MISSING;
+ }
+ else if ((s->tiles[0].canvas->rect && !ibuf->rect) ||
+ (s->tiles[0].canvas->rect_float && !ibuf->rect_float)) {
+ s->tiles[i].state = PAINT2D_TILE_MISSING;
+ }
+ else {
+ s->tiles[i].size[0] = ibuf->x;
+ s->tiles[i].size[1] = ibuf->y;
+ s->tiles[i].radius_fac = sqrtf(((float)ibuf->x * (float)ibuf->y) /
+ (s->tiles[0].size[0] * s->tiles[0].size[1]));
+ s->tiles[i].state = PAINT2D_TILE_READY;
+ }
+ }
+ else {
+ s->tiles[i].state = PAINT2D_TILE_MISSING;
+ }
+
+ if (s->tiles[i].state == PAINT2D_TILE_MISSING) {
+ BKE_image_release_ibuf(s->image, ibuf, NULL);
+ return false;
+ }
+
+ s->tiles[i].canvas = ibuf;
+ return true;
+}
+
/* keep these functions in sync */
static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, float r_rgb[4])
{
@@ -935,15 +1012,15 @@ static void paint_2d_ibuf_rgb_set(
}
}
-static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short tile)
+static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short paint_tile)
{
- if (tile & PAINT_TILE_X) {
+ if (paint_tile & PAINT_TILE_X) {
*x %= ibuf->x;
if (*x < 0) {
*x += ibuf->x;
}
}
- if (tile & PAINT_TILE_Y) {
+ if (paint_tile & PAINT_TILE_Y) {
*y %= ibuf->y;
if (*y < 0) {
*y += ibuf->y;
@@ -951,12 +1028,13 @@ static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short tile)
}
}
-static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short tile, float w)
+static float paint_2d_ibuf_add_if(
+ ImBuf *ibuf, int x, int y, float *outrgb, short paint_tile, float w)
{
float inrgb[4];
- if (tile) {
- paint_2d_ibuf_tile_convert(ibuf, &x, &y, tile);
+ if (paint_tile) {
+ paint_2d_ibuf_tile_convert(ibuf, &x, &y, paint_tile);
}
/* need to also do clipping here always since tiled coordinates
* are not always within bounds */
@@ -973,10 +1051,14 @@ static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, shor
return w;
}
-static void paint_2d_lift_soften(
- ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile)
+static void paint_2d_lift_soften(ImagePaintState *s,
+ ImagePaintTile *tile,
+ ImBuf *ibuf,
+ ImBuf *ibufb,
+ int *pos,
+ const short paint_tile)
{
- bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0));
+ bool sharpen = (tile->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0));
float threshold = s->brush->sharp_threshold;
int x, y, xi, yi, xo, yo, xk, yk;
float count;
@@ -992,7 +1074,7 @@ static void paint_2d_lift_soften(
in_off[1] = pos[1];
out_off[0] = out_off[1] = 0;
- if (!tile) {
+ if (!paint_tile) {
IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], &out_off[1], &dim[0], &dim[1]);
if ((dim[0] == 0) || (dim[1] == 0)) {
@@ -1010,8 +1092,8 @@ static void paint_2d_lift_soften(
yi = in_off[1] + y;
count = 0.0;
- if (tile) {
- paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile);
+ if (paint_tile) {
+ paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, paint_tile);
if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) {
paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba);
}
@@ -1031,7 +1113,7 @@ static void paint_2d_lift_soften(
xi + xk - kernel->pixel_len,
yi + yk - kernel->pixel_len,
outrgb,
- tile,
+ paint_tile,
kernel->wdata[xk + yk * kernel->side]);
}
}
@@ -1085,7 +1167,7 @@ static void paint_2d_set_region(
static int paint_2d_torus_split_region(ImagePaintRegion region[4],
ImBuf *dbuf,
ImBuf *sbuf,
- short tile)
+ short paint_tile)
{
int destx = region->destx;
int desty = region->desty;
@@ -1096,7 +1178,7 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4],
int origw, origh, w, h, tot = 0;
/* convert destination and source coordinates to be within image */
- if (tile & PAINT_TILE_X) {
+ if (paint_tile & PAINT_TILE_X) {
destx = destx % dbuf->x;
if (destx < 0) {
destx += dbuf->x;
@@ -1106,7 +1188,7 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4],
srcx += sbuf->x;
}
}
- if (tile & PAINT_TILE_Y) {
+ if (paint_tile & PAINT_TILE_Y) {
desty = desty % dbuf->y;
if (desty < 0) {
desty += dbuf->y;
@@ -1126,15 +1208,15 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4],
paint_2d_set_region(&region[tot++], destx, desty, srcx, srcy, w, h);
/* do 3 other rects if needed */
- if ((tile & PAINT_TILE_X) && w < origw) {
+ if ((paint_tile & PAINT_TILE_X) && w < origw) {
paint_2d_set_region(
&region[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h);
}
- if ((tile & PAINT_TILE_Y) && h < origh) {
+ if ((paint_tile & PAINT_TILE_Y) && h < origh) {
paint_2d_set_region(
&region[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h);
}
- if ((tile & PAINT_TILE_X) && (tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) {
+ if ((paint_tile & PAINT_TILE_X) && (paint_tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) {
paint_2d_set_region(&region[tot++],
(destx + w) % dbuf->x,
(desty + h) % dbuf->y,
@@ -1147,13 +1229,13 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4],
return tot;
}
-static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short tile)
+static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short paint_tile)
{
ImagePaintRegion region[4];
int a, tot;
paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y);
- tot = paint_2d_torus_split_region(region, ibufb, ibuf, tile);
+ tot = paint_2d_torus_split_region(region, ibufb, ibuf, paint_tile);
for (a = 0; a < tot; a++) {
IMB_rectblend(ibufb,
@@ -1229,9 +1311,8 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[
}
static void paint_2d_do_making_brush(ImagePaintState *s,
+ ImagePaintTile *tile,
ImagePaintRegion *region,
- unsigned short *curveb,
- unsigned short *texmaskb,
ImBuf *frombuf,
float mask_max,
short blend,
@@ -1252,21 +1333,21 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
int origx = region->destx - tx * ED_IMAGE_UNDO_TILE_SIZE;
int origy = region->desty - ty * ED_IMAGE_UNDO_TILE_SIZE;
- if (s->canvas->rect_float) {
+ if (tile->canvas->rect_float) {
tmpbuf.rect_float = ED_image_paint_tile_find(
- undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
+ undo_tiles, s->image, tile->canvas, tile->iuser.tile, tx, ty, &mask, false);
}
else {
tmpbuf.rect = ED_image_paint_tile_find(
- undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
+ undo_tiles, s->image, tile->canvas, tile->iuser.tile, tx, ty, &mask, false);
}
- IMB_rectblend(s->canvas,
+ IMB_rectblend(tile->canvas,
&tmpbuf,
frombuf,
mask,
- curveb,
- texmaskb,
+ tile->cache.curve_mask,
+ tile->cache.tex_mask,
mask_max,
region->destx,
region->desty,
@@ -1284,9 +1365,8 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
typedef struct Paint2DForeachData {
ImagePaintState *s;
+ ImagePaintTile *tile;
ImagePaintRegion *region;
- unsigned short *curveb;
- unsigned short *texmaskb;
ImBuf *frombuf;
float mask_max;
short blend;
@@ -1300,9 +1380,8 @@ static void paint_2d_op_foreach_do(void *__restrict data_v,
{
Paint2DForeachData *data = (Paint2DForeachData *)data_v;
paint_2d_do_making_brush(data->s,
+ data->tile,
data->region,
- data->curveb,
- data->texmaskb,
data->frombuf,
data->mask_max,
data->blend,
@@ -1313,16 +1392,16 @@ static void paint_2d_op_foreach_do(void *__restrict data_v,
}
static int paint_2d_op(void *state,
- ImBuf *ibufb,
- unsigned short *curveb,
- unsigned short *texmaskb,
+ ImagePaintTile *tile,
const float lastpos[2],
const float pos[2])
{
ImagePaintState *s = ((ImagePaintState *)state);
ImBuf *clonebuf = NULL, *frombuf;
+ ImBuf *canvas = tile->canvas;
+ ImBuf *ibufb = tile->cache.ibuf;
ImagePaintRegion region[4];
- short tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y);
+ short paint_tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y);
short blend = s->blend;
const float *offset = s->brush->clone.offset;
float liftpos[2];
@@ -1334,7 +1413,7 @@ static int paint_2d_op(void *state,
/* lift from canvas */
if (s->tool == PAINT_TOOL_SOFTEN) {
- paint_2d_lift_soften(s, s->canvas, ibufb, bpos, tile);
+ paint_2d_lift_soften(s, tile, canvas, ibufb, bpos, paint_tile);
blend = IMB_BLEND_INTERPOLATE;
}
else if (s->tool == PAINT_TOOL_SMEAR) {
@@ -1343,12 +1422,12 @@ static int paint_2d_op(void *state,
}
paint_2d_convert_brushco(ibufb, lastpos, blastpos);
- paint_2d_lift_smear(s->canvas, ibufb, blastpos, tile);
+ paint_2d_lift_smear(canvas, ibufb, blastpos, paint_tile);
blend = IMB_BLEND_INTERPOLATE;
}
else if (s->tool == PAINT_TOOL_CLONE && s->clonecanvas) {
- liftpos[0] = pos[0] - offset[0] * s->canvas->x;
- liftpos[1] = pos[1] - offset[1] * s->canvas->y;
+ liftpos[0] = pos[0] - offset[0] * canvas->x;
+ liftpos[1] = pos[1] - offset[1] * canvas->y;
paint_2d_convert_brushco(ibufb, liftpos, bliftpos);
clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos);
@@ -1356,9 +1435,9 @@ static int paint_2d_op(void *state,
frombuf = (clonebuf) ? clonebuf : ibufb;
- if (tile) {
+ if (paint_tile) {
paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
- tot = paint_2d_torus_split_region(region, s->canvas, frombuf, tile);
+ tot = paint_2d_torus_split_region(region, canvas, frombuf, paint_tile);
}
else {
paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
@@ -1368,7 +1447,8 @@ static int paint_2d_op(void *state,
/* blend into canvas */
for (a = 0; a < tot; a++) {
ED_imapaint_dirty_region(s->image,
- s->canvas,
+ canvas,
+ tile->iuser.tile,
region[a].destx,
region[a].desty,
region[a].width,
@@ -1379,7 +1459,7 @@ static int paint_2d_op(void *state,
/* masking, find original pixels tiles from undo buffer to composite over */
int tilex, tiley, tilew, tileh;
- imapaint_region_tiles(s->canvas,
+ imapaint_region_tiles(canvas,
region[a].destx,
region[a].desty,
region[a].width,
@@ -1391,14 +1471,13 @@ static int paint_2d_op(void *state,
if (tiley == tileh) {
paint_2d_do_making_brush(
- s, &region[a], curveb, texmaskb, frombuf, mask_max, blend, tilex, tiley, tilew, tileh);
+ s, tile, &region[a], frombuf, mask_max, blend, tilex, tiley, tilew, tileh);
}
else {
Paint2DForeachData data;
data.s = s;
+ data.tile = tile;
data.region = &region[a];
- data.curveb = curveb;
- data.texmaskb = texmaskb;
data.frombuf = frombuf;
data.mask_max = mask_max;
data.blend = blend;
@@ -1412,12 +1491,12 @@ static int paint_2d_op(void *state,
}
else {
/* no masking, composite brush directly onto canvas */
- IMB_rectblend_threaded(s->canvas,
- s->canvas,
+ IMB_rectblend_threaded(canvas,
+ canvas,
frombuf,
NULL,
- curveb,
- texmaskb,
+ tile->cache.curve_mask,
+ tile->cache.tex_mask,
mask_max,
region[a].destx,
region[a].desty,
@@ -1439,47 +1518,25 @@ static int paint_2d_op(void *state,
return 1;
}
-static int paint_2d_canvas_set(ImagePaintState *s, Image *ima)
+static int paint_2d_canvas_set(ImagePaintState *s)
{
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL);
-
- /* verify that we can paint and set canvas */
- if (ima == NULL) {
- return 0;
- }
- else if (BKE_image_has_packedfile(ima) && ima->rr) {
- s->warnpackedfile = ima->id.name + 2;
- return 0;
- }
- else if (ibuf && ibuf->channels != 4) {
- s->warnmultifile = ima->id.name + 2;
- return 0;
- }
- else if (!ibuf || !(ibuf->rect || ibuf->rect_float)) {
- return 0;
- }
-
- s->image = ima;
- s->canvas = ibuf;
-
/* set clone canvas */
if (s->tool == PAINT_TOOL_CLONE) {
- ima = s->brush->clone.image;
- ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL);
+ Image *ima = s->brush->clone.image;
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) {
BKE_image_release_ibuf(ima, ibuf, NULL);
- BKE_image_release_ibuf(s->image, s->canvas, NULL);
return 0;
}
s->clonecanvas = ibuf;
/* temporarily add float rect for cloning */
- if (s->canvas->rect_float && !s->clonecanvas->rect_float) {
+ if (s->tiles[0].canvas->rect_float && !s->clonecanvas->rect_float) {
IMB_float_from_rect(s->clonecanvas);
}
- else if (!s->canvas->rect_float && !s->clonecanvas->rect) {
+ else if (!s->tiles[0].canvas->rect_float && !s->clonecanvas->rect) {
IMB_rect_from_float(s->clonecanvas);
}
}
@@ -1492,7 +1549,9 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima)
static void paint_2d_canvas_free(ImagePaintState *s)
{
- BKE_image_release_ibuf(s->image, s->canvas, NULL);
+ for (int i = 0; i < s->num_tiles; i++) {
+ BKE_image_release_ibuf(s->image, s->tiles[i].canvas, NULL);
+ }
BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL);
if (s->blurkernel) {
@@ -1501,71 +1560,107 @@ static void paint_2d_canvas_free(ImagePaintState *s)
}
}
+static void paint_2d_transform_mouse(ImagePaintState *s, const float in[2], float out[2])
+{
+ UI_view2d_region_to_view(s->v2d, in[0], in[1], &out[0], &out[1]);
+}
+
+static bool is_inside_tile(const int size[2], const float pos[2], const float brush[2])
+{
+ return (pos[0] >= -brush[0]) && (pos[0] < size[0] + brush[0]) && (pos[1] >= -brush[1]) &&
+ (pos[1] < size[1] + brush[1]);
+}
+
+static void paint_2d_uv_to_coord(ImagePaintTile *tile, const float uv[2], float coord[2])
+{
+ coord[0] = (uv[0] - tile->uv_origin[0]) * tile->size[0];
+ coord[1] = (uv[1] - tile->uv_origin[1]) * tile->size[1];
+}
+
void paint_2d_stroke(void *ps,
const float prev_mval[2],
const float mval[2],
const bool eraser,
float pressure,
float distance,
- float size)
+ float base_size)
{
- float newuv[2], olduv[2];
+ float new_uv[2], old_uv[2];
ImagePaintState *s = ps;
BrushPainter *painter = s->painter;
- ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL);
- const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA);
- if (!ibuf) {
- return;
- }
+ const bool is_data = s->tiles[0].canvas->colormanage_flag & IMB_COLORMANAGE_IS_DATA;
s->blend = s->brush->blend;
if (eraser) {
s->blend = IMB_BLEND_ERASE_ALPHA;
}
- UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]);
- UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &olduv[0], &olduv[1]);
-
- newuv[0] *= ibuf->x;
- newuv[1] *= ibuf->y;
-
- olduv[0] *= ibuf->x;
- olduv[1] *= ibuf->y;
+ UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &new_uv[0], &new_uv[1]);
+ UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &old_uv[0], &old_uv[1]);
+ float last_uv[2], start_uv[2];
+ UI_view2d_region_to_view(s->v2d, 0.0f, 0.0f, &start_uv[0], &start_uv[1]);
if (painter->firsttouch) {
- float startuv[2];
-
- UI_view2d_region_to_view(s->v2d, 0, 0, &startuv[0], &startuv[1]);
-
/* paint exactly once on first touch */
- painter->startpaintpos[0] = startuv[0] * ibuf->x;
- painter->startpaintpos[1] = startuv[1] * ibuf->y;
-
- painter->firsttouch = 0;
- copy_v2_v2(painter->lastpaintpos, newuv);
+ copy_v2_v2(last_uv, new_uv);
}
else {
- copy_v2_v2(painter->lastpaintpos, olduv);
+ copy_v2_v2(last_uv, old_uv);
}
- /* OCIO_TODO: float buffers are now always linear, so always use color correction
- * this should probably be changed when texture painting color space is supported
- */
- brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data);
+ float uv_brush_size[2] = {base_size / s->tiles[0].size[0], base_size / s->tiles[0].size[1]};
+
+ for (int i = 0; i < s->num_tiles; i++) {
+ ImagePaintTile *tile = &s->tiles[i];
+
+ /* First test: Project brush into UV space, clip against tile. */
+ const int uv_size[2] = {1, 1};
+ float local_new_uv[2], local_old_uv[2];
+ sub_v2_v2v2(local_new_uv, new_uv, tile->uv_origin);
+ sub_v2_v2v2(local_old_uv, old_uv, tile->uv_origin);
+ if (!(is_inside_tile(uv_size, local_new_uv, uv_brush_size) ||
+ is_inside_tile(uv_size, local_old_uv, uv_brush_size))) {
+ continue;
+ }
+
+ /* Lazy tile loading to get size in pixels. */
+ if (!paint_2d_ensure_tile_canvas(s, i)) {
+ continue;
+ }
+
+ float size = base_size * tile->radius_fac;
+
+ float new_coord[2], old_coord[2];
+ paint_2d_uv_to_coord(tile, new_uv, new_coord);
+ paint_2d_uv_to_coord(tile, old_uv, old_coord);
+ if (painter->firsttouch) {
+ paint_2d_uv_to_coord(tile, start_uv, tile->start_paintpos);
+ }
+ paint_2d_uv_to_coord(tile, last_uv, tile->last_paintpos);
+
+ /* Second check in pixel coordinates. */
+ const float pixel_brush_size[] = {size, size};
+ if (!(is_inside_tile(tile->size, new_coord, pixel_brush_size) ||
+ is_inside_tile(tile->size, old_coord, pixel_brush_size))) {
+ continue;
+ }
+
+ ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &tile->iuser, NULL);
- brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size);
+ /* OCIO_TODO: float buffers are now always linear, so always use color correction
+ * this should probably be changed when texture painting color space is supported
+ */
+ brush_painter_2d_require_imbuf(
+ painter->brush, tile, (ibuf->rect_float != NULL), !is_data, painter->cache_invert);
- if (paint_2d_op(s,
- painter->cache.ibuf,
- painter->cache.curve_mask,
- painter->cache.tex_mask,
- olduv,
- newuv)) {
- s->need_redraw = true;
+ brush_painter_2d_refresh_cache(s, painter, tile, new_coord, mval, pressure, distance, size);
+
+ if (paint_2d_op(s, tile, old_coord, new_coord))
+ tile->need_redraw = true;
}
- BKE_image_release_ibuf(s->image, ibuf, NULL);
+ painter->firsttouch = 0;
}
void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode)
@@ -1588,13 +1683,57 @@ void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode)
s->image = s->sima->image;
s->symmetry = settings->imapaint.paint.symmetry_flags;
- if (!paint_2d_canvas_set(s, s->image)) {
- if (s->warnmultifile) {
- BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint");
- }
- if (s->warnpackedfile) {
- BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted");
- }
+ if (s->image == NULL) {
+ MEM_freeN(s);
+ return NULL;
+ }
+ if (BKE_image_has_packedfile(s->image) && s->image->rr != NULL) {
+ BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted");
+ MEM_freeN(s);
+ return 0;
+ }
+
+ s->num_tiles = BLI_listbase_count(&s->image->tiles);
+ s->tiles = MEM_callocN(sizeof(ImagePaintTile) * s->num_tiles, "ImagePaintTile");
+ for (int i = 0; i < s->num_tiles; i++) {
+ BKE_imageuser_default(&s->tiles[i].iuser);
+ }
+ s->tiles[0].iuser.ok = true;
+
+ zero_v2(s->tiles[0].uv_origin);
+
+ ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[0].iuser, NULL);
+ if (ibuf == NULL) {
+ MEM_freeN(s->tiles);
+ MEM_freeN(s);
+ return NULL;
+ }
+
+ if (ibuf->channels != 4) {
+ BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint");
+ MEM_freeN(s->tiles);
+ MEM_freeN(s);
+ return NULL;
+ }
+
+ s->tiles[0].size[0] = ibuf->x;
+ s->tiles[0].size[1] = ibuf->y;
+ s->tiles[0].radius_fac = 1.0f;
+
+ s->tiles[0].canvas = ibuf;
+ s->tiles[0].state = PAINT2D_TILE_READY;
+
+ /* Initialize offsets here, they're needed for the uv space clip test before lazy-loading the
+ * tile properly. */
+ int tile_idx = 0;
+ for (ImageTile *tile = s->image->tiles.first; tile; tile = tile->next, tile_idx++) {
+ s->tiles[tile_idx].iuser.tile = tile->tile_number;
+ s->tiles[tile_idx].uv_origin[0] = ((tile->tile_number - 1001) % 10);
+ s->tiles[tile_idx].uv_origin[1] = ((tile->tile_number - 1001) / 10);
+ }
+
+ if (!paint_2d_canvas_set(s)) {
+ MEM_freeN(s->tiles);
MEM_freeN(s);
return NULL;
@@ -1616,18 +1755,28 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final)
{
ImagePaintState *s = ps;
- if (s->need_redraw) {
- ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL);
+ bool had_redraw = false;
+ for (int i = 0; i < s->num_tiles; i++) {
+ if (s->tiles[i].need_redraw) {
+ ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL);
- imapaint_image_update(s->sima, s->image, ibuf, false);
- ED_imapaint_clear_partial_redraw();
+ imapaint_image_update(s->sima, s->image, ibuf, &s->tiles[i].iuser, false);
- BKE_image_release_ibuf(s->image, ibuf, NULL);
+ BKE_image_release_ibuf(s->image, ibuf, NULL);
- s->need_redraw = false;
+ s->tiles[i].need_redraw = false;
+ had_redraw = true;
+ }
}
- else if (!final) {
- return;
+
+ if (had_redraw) {
+ ED_imapaint_clear_partial_redraw();
+ if (s->sima == NULL || !s->sima->lock) {
+ ED_region_tag_redraw(CTX_wm_region(C));
+ }
+ else {
+ WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image);
+ }
}
if (final) {
@@ -1639,14 +1788,6 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final)
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image);
DEG_id_tag_update(&s->image->id, 0);
}
- else {
- if (!s->sima || !s->sima->lock) {
- ED_region_tag_redraw(CTX_wm_region(C));
- }
- else {
- WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image);
- }
- }
}
void paint_2d_stroke_done(void *ps)
@@ -1654,7 +1795,11 @@ void paint_2d_stroke_done(void *ps)
ImagePaintState *s = ps;
paint_2d_canvas_free(s);
- brush_painter_2d_free(s->painter);
+ for (int i = 0; i < s->num_tiles; i++) {
+ brush_painter_cache_2d_free(&s->tiles[i].cache);
+ }
+ MEM_freeN(s->painter);
+ MEM_freeN(s->tiles);
paint_brush_exit_tex(s->brush);
MEM_freeN(s);
@@ -1713,9 +1858,29 @@ static void paint_2d_fill_add_pixel_float(const int x_px,
}
}
+static ImageUser *paint_2d_get_tile_iuser(ImagePaintState *s, int tile_number)
+{
+ ImageUser *iuser = &s->tiles[0].iuser;
+ for (int i = 0; i < s->num_tiles; i++) {
+ if (s->tiles[i].iuser.tile == tile_number) {
+ if (!paint_2d_ensure_tile_canvas(s, i)) {
+ return NULL;
+ }
+ iuser = &s->tiles[i].iuser;
+ break;
+ }
+ }
+
+ return iuser;
+}
+
/* this function expects linear space color values */
-void paint_2d_bucket_fill(
- const bContext *C, const float color[3], Brush *br, const float mouse_init[2], void *ps)
+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)
{
SpaceImage *sima = CTX_wm_space_image(C);
Image *ima = sima->image;
@@ -1734,8 +1899,17 @@ void paint_2d_bucket_fill(
return;
}
- ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL);
+ float uv_origin[2];
+ float image_init[2];
+ paint_2d_transform_mouse(s, mouse_init, image_init);
+ int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_origin);
+ ImageUser *iuser = paint_2d_get_tile_iuser(s, tile_number);
+ if (!iuser) {
+ return;
+ }
+
+ ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if (!ibuf) {
return;
}
@@ -1753,9 +1927,9 @@ void paint_2d_bucket_fill(
color_f[3] = strength;
}
- if (!mouse_init || !br) {
+ if (!mouse_final || !br) {
/* first case, no image UV, fill the whole image */
- ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false);
+ ED_imapaint_dirty_region(ima, ibuf, tile_number, 0, 0, ibuf->x, ibuf->y, false);
if (do_float) {
for (x_px = 0; x_px < ibuf->x; x_px++) {
@@ -1783,15 +1957,12 @@ void paint_2d_bucket_fill(
BLI_bitmap *touched;
size_t coordinate;
int width = ibuf->x;
- float image_init[2];
int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0;
float pixel_color[4];
/* We are comparing to sum of three squared values
* (assumed in range [0,1]), so need to multiply... */
float threshold_sq = br->fill_threshold * br->fill_threshold * 3;
- UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]);
-
x_px = image_init[0] * ibuf->x;
y_px = image_init[1] * ibuf->y;
@@ -1801,7 +1972,7 @@ void paint_2d_bucket_fill(
}
/* change image invalidation method later */
- ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false);
+ ED_imapaint_dirty_region(ima, ibuf, tile_number, 0, 0, ibuf->x, ibuf->y, false);
stack = BLI_stack_new(sizeof(size_t), __func__);
touched = BLI_BITMAP_NEW(((size_t)ibuf->x) * ibuf->y, "bucket_fill_bitmap");
@@ -1913,7 +2084,7 @@ void paint_2d_bucket_fill(
BLI_stack_free(stack);
}
- imapaint_image_update(sima, ima, ibuf, false);
+ imapaint_image_update(sima, ima, ibuf, iuser, false);
ED_imapaint_clear_partial_redraw();
BKE_image_release_ibuf(ima, ibuf, NULL);
@@ -1938,19 +2109,26 @@ void paint_2d_gradient_fill(
bool do_float;
- if (!ima) {
+ if (ima == NULL) {
return;
}
- ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL);
+ float uv_origin[2];
+ int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_origin);
+ ImageUser *iuser = paint_2d_get_tile_iuser(s, tile_number);
+ if (!iuser) {
+ return;
+ }
- if (!ibuf) {
+ ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
+ if (ibuf == NULL) {
return;
}
- UI_view2d_region_to_view(
- s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]);
- UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]);
+ paint_2d_transform_mouse(s, mouse_final, image_final);
+ paint_2d_transform_mouse(s, mouse_init, image_init);
+ sub_v2_v2(image_init, uv_origin);
+ sub_v2_v2(image_final, uv_origin);
image_final[0] *= ibuf->x;
image_final[1] *= ibuf->y;
@@ -1967,7 +2145,7 @@ void paint_2d_gradient_fill(
do_float = (ibuf->rect_float != NULL);
/* this will be substituted by something else when selection is available */
- ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false);
+ ED_imapaint_dirty_region(ima, ibuf, tile_number, 0, 0, ibuf->x, ibuf->y, false);
if (do_float) {
for (x_px = 0; x_px < ibuf->x; x_px++) {
@@ -2027,7 +2205,7 @@ void paint_2d_gradient_fill(
}
}
- imapaint_image_update(sima, ima, ibuf, false);
+ imapaint_image_update(sima, ima, ibuf, iuser, false);
ED_imapaint_clear_partial_redraw();
BKE_image_release_ibuf(ima, ibuf, NULL);
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index c57490041bc..6a67c469955 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -197,6 +197,7 @@ BLI_INLINE unsigned char f_to_char(const float val)
*/
typedef struct ProjPaintImage {
Image *ima;
+ ImageUser iuser;
ImBuf *ibuf;
ImagePaintPartialRedraw *partRedrawRect;
/** Only used to build undo tiles during painting. */
@@ -530,6 +531,18 @@ BLI_INLINE const MPoly *ps_tri_index_to_mpoly(const ProjPaintState *ps, int tri_
/* Finish projection painting structs */
+static int project_paint_face_paint_tile(Image *ima, const float *uv)
+{
+ if (ima == NULL || ima->source != IMA_SRC_TILED) {
+ return 0;
+ }
+
+ /* Currently, faces are assumed to belong to one tile, so checking the first loop is enough. */
+ int tx = (int)uv[0];
+ int ty = (int)uv[1];
+ return 1001 + 10 * ty + tx;
+}
+
static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int tri_index)
{
const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index);
@@ -729,9 +742,17 @@ static bool project_paint_PickColor(const ProjPaintState *ps,
ima = project_paint_face_paint_image(ps, tri_index);
/** we must have got the imbuf before getting here. */
- ibuf = BKE_image_get_first_ibuf(ima);
- if (!ibuf) {
- return 0;
+ int tile_number = project_paint_face_paint_tile(ima, lt_tri_uv[0]);
+ ImageUser iuser;
+ BKE_imageuser_default(&iuser);
+ iuser.tile = tile_number;
+ ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
+ if (ibuf == NULL) {
+ iuser.tile = 0;
+ ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
+ if (ibuf == NULL) {
+ return 0;
+ }
}
if (interp) {
@@ -1154,6 +1175,8 @@ static bool check_seam(const ProjPaintState *ps,
const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)};
Image *tpage = project_paint_face_paint_image(ps, tri_index);
Image *orig_tpage = project_paint_face_paint_image(ps, orig_face);
+ int tile = project_paint_face_paint_tile(tpage, lt_tri_uv[0]);
+ int orig_tile = project_paint_face_paint_tile(orig_tpage, orig_lt_tri_uv[0]);
BLI_assert(i1_fidx != -1);
@@ -1171,7 +1194,8 @@ static bool check_seam(const ProjPaintState *ps,
}
/* first test if they have the same image */
- if ((orig_tpage == tpage) && cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) &&
+ if ((orig_tpage == tpage) && (orig_tile == tile) &&
+ cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) &&
cmp_uv(orig_lt_tri_uv[orig_i2_fidx], lt_tri_uv[i2_fidx])) {
/* if faces don't have the same winding in uv space,
* they are on the same side so edge is boundary */
@@ -1817,6 +1841,7 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
pjIma->ima,
pjIma->ibuf,
tinf->tmpibuf,
+ pjIma->iuser.tile,
tx,
ty,
&pjIma->maskRect[tile_index],
@@ -1829,6 +1854,7 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
pjIma->ima,
pjIma->ibuf,
tinf->tmpibuf,
+ pjIma->iuser.tile,
tx,
ty,
NULL,
@@ -3486,6 +3512,7 @@ static void project_bucket_init(const ProjPaintState *ps,
ImBuf *ibuf = NULL;
Image *tpage_last = NULL, *tpage;
ImBuf *tmpibuf = NULL;
+ int tile_last = 0;
if (ps->image_tot == 1) {
/* Simple loop, no context switching */
@@ -3509,17 +3536,34 @@ static void project_bucket_init(const ProjPaintState *ps,
for (node = ps->bucketFaces[bucket_index]; node; node = node->next) {
tri_index = POINTER_AS_INT(node->link);
+ const MLoopTri *lt = &ps->mlooptri_eval[tri_index];
+ const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)};
+
/* Image context switching */
tpage = project_paint_face_paint_image(ps, tri_index);
- if (tpage_last != tpage) {
+ int tile = project_paint_face_paint_tile(tpage, lt_tri_uv[0]);
+ if (tpage_last != tpage || tile_last != tile) {
tpage_last = tpage;
+ tile_last = tile;
+ ibuf = NULL;
for (image_index = 0; image_index < ps->image_tot; image_index++) {
- if (ps->projImages[image_index].ima == tpage_last) {
- ibuf = ps->projImages[image_index].ibuf;
+ ProjPaintImage *projIma = &ps->projImages[image_index];
+ if ((projIma->ima == tpage) && (projIma->iuser.tile == tile)) {
+ ibuf = projIma->ibuf;
break;
}
}
+ if (ibuf == NULL) {
+ /* Failed to find the specific tile, fall back to the primary tile. */
+ for (image_index = 0; image_index < ps->image_tot; image_index++) {
+ ProjPaintImage *projIma = &ps->projImages[image_index];
+ if ((projIma->ima == tpage) && (projIma->iuser.tile == 0)) {
+ ibuf = projIma->ibuf;
+ break;
+ }
+ }
+ }
}
/* context switching done */
@@ -4232,22 +4276,36 @@ static bool project_paint_winclip(const ProjPaintState *ps, const ProjPaintFaceC
}
#endif // PROJ_DEBUG_WINCLIP
+typedef struct PrepareImageEntry {
+ struct PrepareImageEntry *next, *prev;
+ Image *ima;
+ int tile;
+} PrepareImageEntry;
+
static void project_paint_build_proj_ima(ProjPaintState *ps,
MemArena *arena,
- LinkNode *image_LinkList)
+ ListBase *used_images)
{
ProjPaintImage *projIma;
- LinkNode *node;
+ PrepareImageEntry *entry;
int i;
/* build an array of images we use */
projIma = ps->projImages = BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot);
- for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) {
+ for (entry = used_images->first, i = 0; entry; entry = entry->next, i++, projIma++) {
+ memset(&projIma->iuser, 0, sizeof(ImageUser));
+ BKE_imageuser_default(&projIma->iuser);
+ projIma->iuser.tile = entry->tile;
int size;
- projIma->ima = node->link;
+ projIma->ima = entry->ima;
projIma->touch = 0;
- projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL);
+ projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, NULL);
+ if (projIma->ibuf == NULL) {
+ projIma->iuser.tile = 0;
+ projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, NULL);
+ BLI_assert(projIma->ibuf != NULL);
+ }
size = sizeof(void **) * ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->x) *
ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->y);
projIma->partRedrawRect = BLI_memarena_alloc(
@@ -4270,15 +4328,18 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps,
const bool is_multi_view)
{
/* Image Vars - keep track of images we have used */
- LinkNodePair image_LinkList = {NULL, NULL};
+ ListBase used_images = {NULL};
Image *tpage_last = NULL, *tpage;
TexPaintSlot *slot_last = NULL;
TexPaintSlot *slot = NULL;
+ int tile_last = -1, tile;
const MLoopTri *lt;
int image_index = -1, tri_index;
int prev_poly = -1;
+ BLI_assert(ps->image_tot == 0);
+
for (tri_index = 0, lt = ps->mlooptri_eval; tri_index < ps->totlooptri_eval; tri_index++, lt++) {
bool is_face_sel;
bool skip_tri = false;
@@ -4321,6 +4382,8 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps,
ps->poly_to_loop_uv[lt->poly] = mloopuv_base;
+ tile = project_paint_face_paint_tile(tpage, mloopuv_base[lt->tri[0]].uv);
+
#ifndef PROJ_DEBUG_NOSEAMBLEED
project_paint_bleed_add_face_user(ps, arena, lt, tri_index);
#endif
@@ -4382,18 +4445,24 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps,
}
}
- if (tpage_last != tpage) {
-
- image_index = BLI_linklist_index(image_LinkList.list, tpage);
+ if (tpage_last != tpage || tile_last != tile) {
+ image_index = 0;
+ for (PrepareImageEntry *e = used_images.first; e; e = e->next, image_index++) {
+ if (e->ima == tpage && e->tile == tile) {
+ break;
+ }
+ }
- if (image_index == -1 && BKE_image_has_ibuf(tpage, NULL)) {
- /* MemArena doesn't have an append func */
- BLI_linklist_append(&image_LinkList, tpage);
- image_index = ps->image_tot;
+ if (image_index == ps->image_tot) {
+ PrepareImageEntry *e = MEM_callocN(sizeof(PrepareImageEntry), "PrepareImageEntry");
+ e->ima = tpage;
+ e->tile = tile;
+ BLI_addtail(&used_images, e);
ps->image_tot++;
}
tpage_last = tpage;
+ tile_last = tile;
}
if (image_index != -1) {
@@ -4406,11 +4475,11 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps,
/* build an array of images we use*/
if (ps->is_shared_user == false) {
- project_paint_build_proj_ima(ps, arena, image_LinkList.list);
+ project_paint_build_proj_ima(ps, arena, &used_images);
}
/* we have built the array, discard the linked list */
- BLI_linklist_free(image_LinkList.list, NULL);
+ BLI_freelistN(&used_images);
}
/* run once per stroke before projection painting */
@@ -4675,7 +4744,7 @@ static bool project_image_refresh_tagged(ProjPaintState *ps)
pr = &(projIma->partRedrawRect[i]);
if (pr->x2 != -1) { /* TODO - use 'enabled' ? */
set_imapaintpartial(pr);
- imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true);
+ imapaint_image_update(NULL, projIma->ima, projIma->ibuf, &projIma->iuser, true);
redraw = 1;
}
@@ -6441,7 +6510,8 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data)
gen_type,
color,
false,
- is_data);
+ is_data,
+ false); /* TODO(lukas): Add option */
return ima;
}
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 84665728e17..53beb981522 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -186,6 +186,7 @@ bool image_texture_paint_poll(struct bContext *C);
void imapaint_image_update(struct SpaceImage *sima,
struct Image *image,
struct ImBuf *ibuf,
+ struct ImageUser *iuser,
short texpaint);
struct ImagePaintPartialRedraw *get_imapaintpartial(void);
void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr);
@@ -206,6 +207,7 @@ void paint_2d_bucket_fill(const struct bContext *C,
const float color[3],
struct Brush *br,
const float mouse_init[2],
+ const float mouse_final[2],
void *ps);
void paint_2d_gradient_fill(const struct bContext *C,
struct Brush *br,