2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
2019-10-02 01:44:27 +10:00
|
|
|
* \ingroup spimage
|
2019-10-02 00:07:06 +10:00
|
|
|
*
|
|
|
|
|
* Overview
|
|
|
|
|
* ========
|
|
|
|
|
*
|
|
|
|
|
* - Each undo step is a #ImageUndoStep
|
|
|
|
|
* - Each #ImageUndoStep stores a list of #UndoImageHandle
|
|
|
|
|
* - Each #UndoImageHandle stores a list of #UndoImageBuf
|
|
|
|
|
* (this is the undo systems equivalent of an #ImBuf).
|
|
|
|
|
* - Each #UndoImageBuf stores an array of #UndoImageTile
|
|
|
|
|
* The tiles are shared between #UndoImageBuf's to avoid duplication.
|
|
|
|
|
*
|
|
|
|
|
* When the undo system manages an image, there will always be a full copy (as a #UndoImageBuf)
|
|
|
|
|
* each new undo step only stores modified tiles.
|
2018-03-23 07:51:56 +01:00
|
|
|
*/
|
|
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
#include "CLG_log.h"
|
|
|
|
|
|
2018-03-23 07:51:56 +01:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
|
|
#include "BLI_blenlib.h"
|
2022-07-26 13:09:22 +10:00
|
|
|
#include "BLI_map.hh"
|
2018-03-23 07:51:56 +01:00
|
|
|
#include "BLI_threads.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_utildefines.h"
|
2018-03-23 07:51:56 +01:00
|
|
|
|
|
|
|
|
#include "DNA_image_types.h"
|
2018-03-19 14:17:59 +01:00
|
|
|
#include "DNA_object_types.h"
|
|
|
|
|
#include "DNA_screen_types.h"
|
|
|
|
|
#include "DNA_space_types.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "DNA_windowmanager_types.h"
|
2018-03-23 07:51:56 +01:00
|
|
|
|
|
|
|
|
#include "IMB_imbuf.h"
|
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
|
|
|
|
|
|
#include "BKE_context.h"
|
|
|
|
|
#include "BKE_image.h"
|
2023-08-02 22:14:18 +02:00
|
|
|
#include "BKE_paint.hh"
|
2018-03-19 14:17:59 +01:00
|
|
|
#include "BKE_undo_system.h"
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2023-09-22 03:18:17 +02:00
|
|
|
#include "DEG_depsgraph.hh"
|
2018-03-23 12:05:55 +01:00
|
|
|
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "ED_object.hh"
|
|
|
|
|
#include "ED_paint.hh"
|
|
|
|
|
#include "ED_undo.hh"
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "ED_util.hh"
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "WM_api.hh"
|
2019-08-03 00:07:32 +10:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static CLG_LogRef LOG = {"ed.image.undo"};
|
|
|
|
|
|
2018-03-19 14:17:59 +01:00
|
|
|
/* -------------------------------------------------------------------- */
|
2019-10-02 00:07:06 +10:00
|
|
|
/** \name Thread Locking
|
2018-03-19 14:17:59 +01:00
|
|
|
* \{ */
|
|
|
|
|
|
2019-10-10 10:25:46 +11:00
|
|
|
/* This is a non-global static resource,
|
2018-03-23 07:51:56 +01:00
|
|
|
* Maybe it should be exposed as part of the
|
|
|
|
|
* paint operation, but for now just give a public interface */
|
2019-10-02 00:07:06 +10:00
|
|
|
static SpinLock paint_tiles_lock;
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2023-07-02 19:37:22 +10:00
|
|
|
void ED_image_paint_tile_lock_init()
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2019-10-02 00:07:06 +10:00
|
|
|
BLI_spin_init(&paint_tiles_lock);
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
2023-07-02 19:37:22 +10:00
|
|
|
void ED_image_paint_tile_lock_end()
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2019-10-02 00:07:06 +10:00
|
|
|
BLI_spin_end(&paint_tiles_lock);
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Paint Tiles
|
|
|
|
|
*
|
|
|
|
|
* Created on demand while painting,
|
|
|
|
|
* use to access the previous state for some paint operations.
|
|
|
|
|
*
|
|
|
|
|
* These buffers are also used for undo when available.
|
|
|
|
|
*
|
|
|
|
|
* \{ */
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
static ImBuf *imbuf_alloc_temp_tile()
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2019-10-02 01:54:58 +10:00
|
|
|
return IMB_allocImBuf(
|
|
|
|
|
ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, IB_rectfloat | IB_rect);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
struct PaintTileKey {
|
|
|
|
|
int x_tile, y_tile;
|
|
|
|
|
Image *image;
|
|
|
|
|
ImBuf *ibuf;
|
|
|
|
|
/* Copied from iuser.tile in PaintTile. */
|
|
|
|
|
int iuser_tile;
|
|
|
|
|
|
|
|
|
|
uint64_t hash() const
|
|
|
|
|
{
|
|
|
|
|
return blender::get_default_hash_4(x_tile, y_tile, image, ibuf);
|
|
|
|
|
}
|
|
|
|
|
bool operator==(const PaintTileKey &other) const
|
|
|
|
|
{
|
|
|
|
|
return x_tile == other.x_tile && y_tile == other.y_tile && image == other.image &&
|
|
|
|
|
ibuf == other.ibuf && iuser_tile == other.iuser_tile;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct PaintTile {
|
2019-10-02 00:07:06 +10:00
|
|
|
Image *image;
|
|
|
|
|
ImBuf *ibuf;
|
2020-03-04 14:40:21 +01:00
|
|
|
/* For 2D image painting the ImageUser uses most of the values.
|
|
|
|
|
* Even though views and passes are stored they are currently not supported for painting.
|
|
|
|
|
* For 3D projection painting this only uses a tile & frame number.
|
|
|
|
|
* The scene pointer must be cleared (or temporarily set it as needed, but leave cleared). */
|
|
|
|
|
ImageUser iuser;
|
2019-10-02 00:07:06 +10:00
|
|
|
union {
|
|
|
|
|
float *fp;
|
2023-05-18 10:19:01 +02:00
|
|
|
uint8_t *byte_ptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
void *pt;
|
|
|
|
|
} rect;
|
2022-07-25 07:56:17 +02:00
|
|
|
uint16_t *mask;
|
2019-10-02 00:07:06 +10:00
|
|
|
bool valid;
|
|
|
|
|
bool use_float;
|
2019-11-02 12:29:46 +11:00
|
|
|
int x_tile, y_tile;
|
2022-07-25 07:56:17 +02:00
|
|
|
};
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
static void ptile_free(PaintTile *ptile)
|
|
|
|
|
{
|
|
|
|
|
if (ptile->rect.pt) {
|
|
|
|
|
MEM_freeN(ptile->rect.pt);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2019-10-02 02:08:20 +10:00
|
|
|
if (ptile->mask) {
|
|
|
|
|
MEM_freeN(ptile->mask);
|
|
|
|
|
}
|
2019-10-02 00:07:06 +10:00
|
|
|
MEM_freeN(ptile);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
struct PaintTileMap {
|
|
|
|
|
blender::Map<PaintTileKey, PaintTile *> map;
|
|
|
|
|
|
|
|
|
|
~PaintTileMap()
|
|
|
|
|
{
|
|
|
|
|
for (PaintTile *ptile : map.values()) {
|
|
|
|
|
ptile_free(ptile);
|
|
|
|
|
}
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
};
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
static void ptile_invalidate_map(PaintTileMap *paint_tile_map)
|
2019-10-02 00:07:06 +10:00
|
|
|
{
|
2022-07-25 07:56:17 +02:00
|
|
|
for (PaintTile *ptile : paint_tile_map->map.values()) {
|
2019-10-02 00:07:06 +10:00
|
|
|
ptile->valid = false;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
void *ED_image_paint_tile_find(PaintTileMap *paint_tile_map,
|
2019-10-02 01:54:58 +10:00
|
|
|
Image *image,
|
|
|
|
|
ImBuf *ibuf,
|
2020-03-04 14:40:21 +01:00
|
|
|
ImageUser *iuser,
|
2019-10-02 01:54:58 +10:00
|
|
|
int x_tile,
|
|
|
|
|
int y_tile,
|
|
|
|
|
ushort **r_mask,
|
|
|
|
|
bool validate)
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2022-07-25 07:56:17 +02:00
|
|
|
PaintTileKey key;
|
|
|
|
|
key.ibuf = ibuf;
|
|
|
|
|
key.image = image;
|
|
|
|
|
key.iuser_tile = iuser->tile;
|
|
|
|
|
key.x_tile = x_tile;
|
|
|
|
|
key.y_tile = y_tile;
|
|
|
|
|
PaintTile **pptile = paint_tile_map->map.lookup_ptr(key);
|
|
|
|
|
if (pptile == nullptr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
PaintTile *ptile = *pptile;
|
|
|
|
|
if (r_mask) {
|
|
|
|
|
/* allocate mask if requested. */
|
|
|
|
|
if (!ptile->mask) {
|
2022-07-26 13:09:22 +10:00
|
|
|
ptile->mask = static_cast<uint16_t *>(
|
|
|
|
|
MEM_callocN(sizeof(uint16_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), "UndoImageTile.mask"));
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
*r_mask = ptile->mask;
|
|
|
|
|
}
|
|
|
|
|
if (validate) {
|
|
|
|
|
ptile->valid = true;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
return ptile->rect.pt;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
/* Set the given buffer data as an owning data of the imbuf's buffer.
|
|
|
|
|
* Returns the data pointer which was stolen from the imbuf before assignment. */
|
|
|
|
|
static uint8_t *image_undo_steal_and_assign_byte_buffer(ImBuf *ibuf, uint8_t *new_buffer_data)
|
|
|
|
|
{
|
|
|
|
|
uint8_t *old_buffer_data = IMB_steal_byte_buffer(ibuf);
|
|
|
|
|
IMB_assign_byte_buffer(ibuf, new_buffer_data, IB_TAKE_OWNERSHIP);
|
|
|
|
|
return old_buffer_data;
|
|
|
|
|
}
|
|
|
|
|
static float *image_undo_steal_and_assign_float_buffer(ImBuf *ibuf, float *new_buffer_data)
|
|
|
|
|
{
|
|
|
|
|
float *old_buffer_data = IMB_steal_float_buffer(ibuf);
|
|
|
|
|
IMB_assign_float_buffer(ibuf, new_buffer_data, IB_TAKE_OWNERSHIP);
|
|
|
|
|
return old_buffer_data;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
void *ED_image_paint_tile_push(PaintTileMap *paint_tile_map,
|
2019-10-02 01:54:58 +10:00
|
|
|
Image *image,
|
|
|
|
|
ImBuf *ibuf,
|
|
|
|
|
ImBuf **tmpibuf,
|
2020-03-04 14:40:21 +01:00
|
|
|
ImageUser *iuser,
|
2019-10-02 01:54:58 +10:00
|
|
|
int x_tile,
|
|
|
|
|
int y_tile,
|
|
|
|
|
ushort **r_mask,
|
|
|
|
|
bool **r_valid,
|
|
|
|
|
bool use_thread_lock,
|
|
|
|
|
bool find_prev)
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2022-07-25 07:56:17 +02:00
|
|
|
if (use_thread_lock) {
|
|
|
|
|
BLI_spin_lock(&paint_tiles_lock);
|
|
|
|
|
}
|
2023-05-18 10:19:01 +02:00
|
|
|
const bool has_float = (ibuf->float_buffer.data != nullptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-03-23 07:51:56 +01:00
|
|
|
/* check if tile is already pushed */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-03-23 07:51:56 +01:00
|
|
|
/* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
|
|
|
|
|
if (find_prev) {
|
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
2019-12-12 16:06:08 +01:00
|
|
|
void *data = ED_image_paint_tile_find(
|
2022-07-25 07:56:17 +02:00
|
|
|
paint_tile_map, image, ibuf, iuser, x_tile, y_tile, r_mask, true);
|
2018-03-23 07:51:56 +01:00
|
|
|
if (data) {
|
2022-07-25 07:56:17 +02:00
|
|
|
if (use_thread_lock) {
|
|
|
|
|
BLI_spin_unlock(&paint_tiles_lock);
|
|
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
if (*tmpibuf == nullptr) {
|
2019-10-02 00:07:06 +10:00
|
|
|
*tmpibuf = imbuf_alloc_temp_tile();
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-26 13:09:22 +10:00
|
|
|
PaintTile *ptile = static_cast<PaintTile *>(MEM_callocN(sizeof(PaintTile), "PaintTile"));
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
ptile->image = image;
|
|
|
|
|
ptile->ibuf = ibuf;
|
2020-03-04 14:40:21 +01:00
|
|
|
ptile->iuser = *iuser;
|
2022-07-25 07:56:17 +02:00
|
|
|
ptile->iuser.scene = nullptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2019-11-02 12:29:46 +11:00
|
|
|
ptile->x_tile = x_tile;
|
|
|
|
|
ptile->y_tile = y_tile;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-03-23 07:51:56 +01:00
|
|
|
/* add mask explicitly here */
|
2019-10-02 00:07:06 +10:00
|
|
|
if (r_mask) {
|
2022-07-26 13:09:22 +10:00
|
|
|
*r_mask = ptile->mask = static_cast<uint16_t *>(
|
|
|
|
|
MEM_callocN(sizeof(uint16_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), "PaintTile.mask"));
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
ptile->rect.pt = MEM_callocN((ibuf->float_buffer.data ? sizeof(float[4]) : sizeof(char[4])) *
|
2020-05-20 00:24:26 +02:00
|
|
|
square_i(ED_IMAGE_UNDO_TILE_SIZE),
|
|
|
|
|
"PaintTile.rect");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
ptile->use_float = has_float;
|
|
|
|
|
ptile->valid = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-10-02 01:54:58 +10:00
|
|
|
if (r_valid) {
|
|
|
|
|
*r_valid = &ptile->valid;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
IMB_rectcpy(*tmpibuf,
|
|
|
|
|
ibuf,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
2019-10-02 01:54:58 +10:00
|
|
|
x_tile * ED_IMAGE_UNDO_TILE_SIZE,
|
|
|
|
|
y_tile * ED_IMAGE_UNDO_TILE_SIZE,
|
|
|
|
|
ED_IMAGE_UNDO_TILE_SIZE,
|
|
|
|
|
ED_IMAGE_UNDO_TILE_SIZE);
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
if (has_float) {
|
2023-05-18 10:19:01 +02:00
|
|
|
ptile->rect.fp = image_undo_steal_and_assign_float_buffer(*tmpibuf, ptile->rect.fp);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-18 10:19:01 +02:00
|
|
|
ptile->rect.byte_ptr = image_undo_steal_and_assign_byte_buffer(*tmpibuf, ptile->rect.byte_ptr);
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
PaintTileKey key = {};
|
|
|
|
|
key.ibuf = ibuf;
|
|
|
|
|
key.image = image;
|
|
|
|
|
key.iuser_tile = iuser->tile;
|
|
|
|
|
key.x_tile = x_tile;
|
|
|
|
|
key.y_tile = y_tile;
|
|
|
|
|
PaintTile *existing_tile = nullptr;
|
|
|
|
|
paint_tile_map->map.add_or_modify(
|
|
|
|
|
key,
|
|
|
|
|
[&](PaintTile **pptile) { *pptile = ptile; },
|
|
|
|
|
[&](PaintTile **pptile) { existing_tile = *pptile; });
|
|
|
|
|
if (existing_tile) {
|
|
|
|
|
ptile_free(ptile);
|
|
|
|
|
ptile = existing_tile;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-02 01:54:58 +10:00
|
|
|
if (use_thread_lock) {
|
2019-10-02 00:07:06 +10:00
|
|
|
BLI_spin_unlock(&paint_tiles_lock);
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2019-10-02 00:07:06 +10:00
|
|
|
return ptile->rect.pt;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
static void ptile_restore_runtime_map(PaintTileMap *paint_tile_map)
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2019-10-02 00:07:06 +10:00
|
|
|
ImBuf *tmpibuf = imbuf_alloc_temp_tile();
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
for (PaintTile *ptile : paint_tile_map->map.values()) {
|
2019-10-02 00:07:06 +10:00
|
|
|
Image *image = ptile->image;
|
2022-07-25 07:56:17 +02:00
|
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &ptile->iuser, nullptr);
|
2023-05-18 10:19:01 +02:00
|
|
|
const bool has_float = (ibuf->float_buffer.data != nullptr);
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
if (has_float) {
|
2023-05-18 10:19:01 +02:00
|
|
|
ptile->rect.fp = image_undo_steal_and_assign_float_buffer(tmpibuf, ptile->rect.fp);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-18 10:19:01 +02:00
|
|
|
ptile->rect.byte_ptr = image_undo_steal_and_assign_byte_buffer(tmpibuf,
|
|
|
|
|
ptile->rect.byte_ptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
/* TODO(sergey): Look into implementing API which does not require such temporary buffer
|
|
|
|
|
* assignment. */
|
2019-11-02 12:22:51 +11:00
|
|
|
IMB_rectcpy(ibuf,
|
|
|
|
|
tmpibuf,
|
2019-11-02 12:29:46 +11:00
|
|
|
ptile->x_tile * ED_IMAGE_UNDO_TILE_SIZE,
|
|
|
|
|
ptile->y_tile * ED_IMAGE_UNDO_TILE_SIZE,
|
2019-11-02 12:22:51 +11:00
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
ED_IMAGE_UNDO_TILE_SIZE,
|
|
|
|
|
ED_IMAGE_UNDO_TILE_SIZE);
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
if (has_float) {
|
2023-05-18 10:19:01 +02:00
|
|
|
ptile->rect.fp = image_undo_steal_and_assign_float_buffer(tmpibuf, ptile->rect.fp);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-18 10:19:01 +02:00
|
|
|
ptile->rect.byte_ptr = image_undo_steal_and_assign_byte_buffer(tmpibuf,
|
|
|
|
|
ptile->rect.byte_ptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
|
2021-06-23 12:05:40 +10:00
|
|
|
/* Force OpenGL reload (maybe partial update will operate better?) */
|
|
|
|
|
BKE_image_free_gputextures(image);
|
|
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (ibuf->float_buffer.data) {
|
2018-03-23 07:51:56 +01:00
|
|
|
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
|
|
|
|
|
}
|
|
|
|
|
if (ibuf->mipmap[0]) {
|
2022-06-03 13:39:37 +10:00
|
|
|
ibuf->userflags |= IB_MIPMAP_INVALID; /* Force MIP-MAP recreation. */
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
BKE_image_release_ibuf(image, ibuf, nullptr);
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IMB_freeImBuf(tmpibuf);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Image Undo Tile
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
static uint32_t index_from_xy(uint32_t tile_x, uint32_t tile_y, const uint32_t tiles_dims[2])
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2019-10-02 00:07:06 +10:00
|
|
|
BLI_assert(tile_x < tiles_dims[0] && tile_y < tiles_dims[1]);
|
|
|
|
|
return (tile_y * tiles_dims[0]) + tile_x;
|
|
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
struct UndoImageTile {
|
2019-10-02 00:07:06 +10:00
|
|
|
union {
|
|
|
|
|
float *fp;
|
2023-05-18 10:19:01 +02:00
|
|
|
uint8_t *byte_ptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
void *pt;
|
|
|
|
|
} rect;
|
|
|
|
|
int users;
|
2022-07-25 07:56:17 +02:00
|
|
|
};
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static UndoImageTile *utile_alloc(bool has_float)
|
|
|
|
|
{
|
2022-07-26 13:09:22 +10:00
|
|
|
UndoImageTile *utile = static_cast<UndoImageTile *>(
|
|
|
|
|
MEM_callocN(sizeof(*utile), "ImageUndoTile"));
|
2019-10-02 00:07:06 +10:00
|
|
|
if (has_float) {
|
2022-07-26 13:09:22 +10:00
|
|
|
utile->rect.fp = static_cast<float *>(
|
|
|
|
|
MEM_mallocN(sizeof(float[4]) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__));
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-18 10:19:01 +02:00
|
|
|
utile->rect.byte_ptr = static_cast<uint8_t *>(
|
2022-07-26 13:09:22 +10:00
|
|
|
MEM_mallocN(sizeof(uint32_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__));
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
return utile;
|
|
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static void utile_init_from_imbuf(
|
2022-07-25 07:56:17 +02:00
|
|
|
UndoImageTile *utile, const uint32_t x, const uint32_t y, const ImBuf *ibuf, ImBuf *tmpibuf)
|
2019-10-02 00:07:06 +10:00
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
const bool has_float = ibuf->float_buffer.data;
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
if (has_float) {
|
2023-05-18 10:19:01 +02:00
|
|
|
utile->rect.fp = image_undo_steal_and_assign_float_buffer(tmpibuf, utile->rect.fp);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-18 10:19:01 +02:00
|
|
|
utile->rect.byte_ptr = image_undo_steal_and_assign_byte_buffer(tmpibuf, utile->rect.byte_ptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
/* TODO(sergey): Look into implementing API which does not require such temporary buffer
|
|
|
|
|
* assignment. */
|
2019-10-02 01:54:58 +10:00
|
|
|
IMB_rectcpy(tmpibuf, ibuf, 0, 0, x, y, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE);
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
if (has_float) {
|
2023-05-18 10:19:01 +02:00
|
|
|
utile->rect.fp = image_undo_steal_and_assign_float_buffer(tmpibuf, utile->rect.fp);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-18 10:19:01 +02:00
|
|
|
utile->rect.byte_ptr = image_undo_steal_and_assign_byte_buffer(tmpibuf, utile->rect.byte_ptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void utile_restore(
|
|
|
|
|
const UndoImageTile *utile, const uint x, const uint y, ImBuf *ibuf, ImBuf *tmpibuf)
|
|
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
const bool has_float = ibuf->float_buffer.data;
|
|
|
|
|
float *prev_rect_float = tmpibuf->float_buffer.data;
|
|
|
|
|
uint8_t *prev_rect = tmpibuf->byte_buffer.data;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
if (has_float) {
|
2023-05-18 10:19:01 +02:00
|
|
|
tmpibuf->float_buffer.data = utile->rect.fp;
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-18 10:19:01 +02:00
|
|
|
tmpibuf->byte_buffer.data = utile->rect.byte_ptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
/* TODO(sergey): Look into implementing API which does not require such temporary buffer
|
|
|
|
|
* assignment. */
|
2019-10-02 01:54:58 +10:00
|
|
|
IMB_rectcpy(ibuf, tmpibuf, x, y, 0, 0, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE);
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
tmpibuf->float_buffer.data = prev_rect_float;
|
|
|
|
|
tmpibuf->byte_buffer.data = prev_rect;
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void utile_decref(UndoImageTile *utile)
|
|
|
|
|
{
|
|
|
|
|
utile->users -= 1;
|
|
|
|
|
BLI_assert(utile->users >= 0);
|
|
|
|
|
if (utile->users == 0) {
|
|
|
|
|
MEM_freeN(utile->rect.pt);
|
2022-07-25 07:56:17 +02:00
|
|
|
MEM_delete(utile);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Image Undo Buffer
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-07-29 23:23:50 -05:00
|
|
|
struct UndoImageBuf {
|
2023-06-03 08:36:28 +10:00
|
|
|
UndoImageBuf *next, *prev;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The buffer after the undo step has executed.
|
|
|
|
|
*/
|
2023-06-03 08:36:28 +10:00
|
|
|
UndoImageBuf *post;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2023-05-03 14:13:27 +10:00
|
|
|
char ibuf_filepath[IMB_FILEPATH_SIZE];
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
UndoImageTile **tiles;
|
|
|
|
|
|
|
|
|
|
/** Can calculate these from dims, just for convenience. */
|
2022-07-25 07:56:17 +02:00
|
|
|
uint32_t tiles_len;
|
|
|
|
|
uint32_t tiles_dims[2];
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
uint32_t image_dims[2];
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
/** Store variables from the image. */
|
|
|
|
|
struct {
|
|
|
|
|
short source;
|
|
|
|
|
bool use_float;
|
|
|
|
|
} image_state;
|
2022-07-29 23:23:50 -05:00
|
|
|
};
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf)
|
|
|
|
|
{
|
2022-07-26 13:09:22 +10:00
|
|
|
UndoImageBuf *ubuf = static_cast<UndoImageBuf *>(MEM_callocN(sizeof(*ubuf), __func__));
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
ubuf->image_dims[0] = ibuf->x;
|
|
|
|
|
ubuf->image_dims[1] = ibuf->y;
|
|
|
|
|
|
2019-10-02 01:54:58 +10:00
|
|
|
ubuf->tiles_dims[0] = ED_IMAGE_UNDO_TILE_NUMBER(ubuf->image_dims[0]);
|
|
|
|
|
ubuf->tiles_dims[1] = ED_IMAGE_UNDO_TILE_NUMBER(ubuf->image_dims[1]);
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
ubuf->tiles_len = ubuf->tiles_dims[0] * ubuf->tiles_dims[1];
|
2022-07-26 13:09:22 +10:00
|
|
|
ubuf->tiles = static_cast<UndoImageTile **>(
|
|
|
|
|
MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__));
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2023-05-09 12:50:37 +10:00
|
|
|
STRNCPY(ubuf->ibuf_filepath, ibuf->filepath);
|
2019-10-02 00:07:06 +10:00
|
|
|
ubuf->image_state.source = image->source;
|
2023-05-18 10:19:01 +02:00
|
|
|
ubuf->image_state.use_float = ibuf->float_buffer.data != nullptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
return ubuf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ubuf_from_image_all_tiles(UndoImageBuf *ubuf, const ImBuf *ibuf)
|
|
|
|
|
{
|
|
|
|
|
ImBuf *tmpibuf = imbuf_alloc_temp_tile();
|
|
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
const bool has_float = ibuf->float_buffer.data;
|
2019-10-02 00:07:06 +10:00
|
|
|
int i = 0;
|
|
|
|
|
for (uint y_tile = 0; y_tile < ubuf->tiles_dims[1]; y_tile += 1) {
|
2019-10-02 01:54:58 +10:00
|
|
|
uint y = y_tile << ED_IMAGE_UNDO_TILE_BITS;
|
2019-10-02 00:07:06 +10:00
|
|
|
for (uint x_tile = 0; x_tile < ubuf->tiles_dims[0]; x_tile += 1) {
|
2019-10-02 01:54:58 +10:00
|
|
|
uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(ubuf->tiles[i] == nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
UndoImageTile *utile = utile_alloc(has_float);
|
|
|
|
|
utile->users = 1;
|
|
|
|
|
utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf);
|
|
|
|
|
ubuf->tiles[i] = utile;
|
|
|
|
|
|
|
|
|
|
i += 1;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
|
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
2019-12-12 16:06:08 +01:00
|
|
|
BLI_assert(i == ubuf->tiles_len);
|
|
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
IMB_freeImBuf(tmpibuf);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-15 11:47:11 +11:00
|
|
|
/** Ensure we can copy the ubuf into the ibuf. */
|
|
|
|
|
static void ubuf_ensure_compat_ibuf(const UndoImageBuf *ubuf, ImBuf *ibuf)
|
|
|
|
|
{
|
|
|
|
|
/* We could have both float and rect buffers,
|
|
|
|
|
* in this case free the float buffer if it's unused. */
|
2023-05-18 10:19:01 +02:00
|
|
|
if ((ibuf->float_buffer.data != nullptr) && (ubuf->image_state.use_float == false)) {
|
2019-10-15 11:47:11 +11:00
|
|
|
imb_freerectfloatImBuf(ibuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ibuf->x == ubuf->image_dims[0] && ibuf->y == ubuf->image_dims[1] &&
|
2023-05-18 10:19:01 +02:00
|
|
|
(ubuf->image_state.use_float ? (void *)ibuf->float_buffer.data :
|
|
|
|
|
(void *)ibuf->byte_buffer.data))
|
2019-10-15 11:47:11 +11:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
imb_freerectImbuf_all(ibuf);
|
|
|
|
|
IMB_rect_size_set(ibuf, ubuf->image_dims);
|
|
|
|
|
|
|
|
|
|
if (ubuf->image_state.use_float) {
|
2022-09-12 19:39:24 +02:00
|
|
|
imb_addrectfloatImBuf(ibuf, 4);
|
2019-10-15 11:47:11 +11:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
imb_addrectImBuf(ibuf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static void ubuf_free(UndoImageBuf *ubuf)
|
|
|
|
|
{
|
|
|
|
|
UndoImageBuf *ubuf_post = ubuf->post;
|
|
|
|
|
for (uint i = 0; i < ubuf->tiles_len; i++) {
|
|
|
|
|
UndoImageTile *utile = ubuf->tiles[i];
|
|
|
|
|
utile_decref(utile);
|
|
|
|
|
}
|
|
|
|
|
MEM_freeN(ubuf->tiles);
|
|
|
|
|
MEM_freeN(ubuf);
|
|
|
|
|
if (ubuf_post) {
|
|
|
|
|
ubuf_free(ubuf_post);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Image Undo Handle
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-07-29 23:23:50 -05:00
|
|
|
struct UndoImageHandle {
|
2023-06-03 08:36:28 +10:00
|
|
|
UndoImageHandle *next, *prev;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
/** Each undo handle refers to a single image which may have multiple buffers. */
|
|
|
|
|
UndoRefID_Image image_ref;
|
|
|
|
|
|
2023-05-27 15:10:58 +10:00
|
|
|
/**
|
|
|
|
|
* Each tile of a tiled image has its own UndoImageHandle.
|
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
2019-12-12 16:06:08 +01:00
|
|
|
* The tile number of this IUser is used to distinguish them.
|
|
|
|
|
*/
|
|
|
|
|
ImageUser iuser;
|
|
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
/**
|
|
|
|
|
* List of #UndoImageBuf's to support multiple buffers per image.
|
|
|
|
|
*/
|
|
|
|
|
ListBase buffers;
|
2022-07-29 23:23:50 -05:00
|
|
|
};
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static void uhandle_restore_list(ListBase *undo_handles, bool use_init)
|
|
|
|
|
{
|
|
|
|
|
ImBuf *tmpibuf = imbuf_alloc_temp_tile();
|
|
|
|
|
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (UndoImageHandle *, uh, undo_handles) {
|
2019-10-02 00:07:06 +10:00
|
|
|
/* Tiles only added to second set of tiles. */
|
|
|
|
|
Image *image = uh->image_ref.ptr;
|
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
2019-12-12 16:06:08 +01:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &uh->iuser, nullptr);
|
|
|
|
|
if (UNLIKELY(ibuf == nullptr)) {
|
2019-10-02 00:07:06 +10:00
|
|
|
CLOG_ERROR(&LOG, "Unable to get buffer for image '%s'", image->id.name + 2);
|
2018-03-23 07:51:56 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2019-10-02 00:07:06 +10:00
|
|
|
bool changed = false;
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (UndoImageBuf *, ubuf_iter, &uh->buffers) {
|
2019-10-02 00:07:06 +10:00
|
|
|
UndoImageBuf *ubuf = use_init ? ubuf_iter : ubuf_iter->post;
|
2019-10-15 11:47:11 +11:00
|
|
|
ubuf_ensure_compat_ibuf(ubuf, ibuf);
|
|
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
int i = 0;
|
|
|
|
|
for (uint y_tile = 0; y_tile < ubuf->tiles_dims[1]; y_tile += 1) {
|
2019-10-02 01:54:58 +10:00
|
|
|
uint y = y_tile << ED_IMAGE_UNDO_TILE_BITS;
|
2019-10-02 00:07:06 +10:00
|
|
|
for (uint x_tile = 0; x_tile < ubuf->tiles_dims[0]; x_tile += 1) {
|
2019-10-02 01:54:58 +10:00
|
|
|
uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS;
|
2019-10-02 00:07:06 +10:00
|
|
|
utile_restore(ubuf->tiles[i], x, y, ibuf, tmpibuf);
|
|
|
|
|
changed = true;
|
|
|
|
|
i += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
if (changed) {
|
|
|
|
|
BKE_image_mark_dirty(image, ibuf);
|
2022-07-26 13:09:22 +10:00
|
|
|
/* TODO(@jbakker): only mark areas that are actually updated to improve performance. */
|
2022-03-07 08:43:37 +01:00
|
|
|
BKE_image_partial_update_mark_full_update(image);
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (ibuf->float_buffer.data) {
|
2022-07-26 13:09:22 +10:00
|
|
|
ibuf->userflags |= IB_RECT_INVALID; /* Force recreate of char `rect` */
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
if (ibuf->mipmap[0]) {
|
2022-07-26 13:09:22 +10:00
|
|
|
ibuf->userflags |= IB_MIPMAP_INVALID; /* Force MIP-MAP recreation. */
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
2019-05-18 12:41:09 +02:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
DEG_id_tag_update(&image->id, 0);
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
BKE_image_release_ibuf(image, ibuf, nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IMB_freeImBuf(tmpibuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void uhandle_free_list(ListBase *undo_handles)
|
|
|
|
|
{
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (UndoImageHandle *, uh, undo_handles) {
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (UndoImageBuf *, ubuf, &uh->buffers) {
|
|
|
|
|
ubuf_free(ubuf);
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2019-10-02 00:07:06 +10:00
|
|
|
MEM_freeN(uh);
|
|
|
|
|
}
|
|
|
|
|
BLI_listbase_clear(undo_handles);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Image Undo Internal Utilities
|
|
|
|
|
* \{ */
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
/** #UndoImageHandle utilities */
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static UndoImageBuf *uhandle_lookup_ubuf(UndoImageHandle *uh,
|
2022-10-03 17:37:25 -05:00
|
|
|
const Image * /*image*/,
|
2023-05-03 14:13:27 +10:00
|
|
|
const char *ibuf_filepath)
|
2019-10-02 00:07:06 +10:00
|
|
|
{
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (UndoImageBuf *, ubuf, &uh->buffers) {
|
2023-05-03 14:13:27 +10:00
|
|
|
if (STREQ(ubuf->ibuf_filepath, ibuf_filepath)) {
|
2019-10-02 00:07:06 +10:00
|
|
|
return ubuf;
|
|
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
return nullptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf)
|
|
|
|
|
{
|
2023-05-03 14:13:27 +10:00
|
|
|
BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->filepath) == nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
UndoImageBuf *ubuf = ubuf_from_image_no_tiles(image, ibuf);
|
|
|
|
|
BLI_addtail(&uh->buffers, ubuf);
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
ubuf->post = nullptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
return ubuf;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static UndoImageBuf *uhandle_ensure_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf)
|
2019-09-18 18:49:53 +10:00
|
|
|
{
|
2023-05-03 14:13:27 +10:00
|
|
|
UndoImageBuf *ubuf = uhandle_lookup_ubuf(uh, image, ibuf->filepath);
|
2022-07-25 07:56:17 +02:00
|
|
|
if (ubuf == nullptr) {
|
2019-10-02 00:07:06 +10:00
|
|
|
ubuf = uhandle_add_ubuf(uh, image, ibuf);
|
|
|
|
|
}
|
|
|
|
|
return ubuf;
|
2019-09-18 18:49:53 +10:00
|
|
|
}
|
|
|
|
|
|
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
2019-12-12 16:06:08 +01:00
|
|
|
static UndoImageHandle *uhandle_lookup_by_name(ListBase *undo_handles,
|
|
|
|
|
const Image *image,
|
|
|
|
|
int tile_number)
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (UndoImageHandle *, uh, undo_handles) {
|
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
2019-12-12 16:06:08 +01:00
|
|
|
if (STREQ(image->id.name + 2, uh->image_ref.name + 2) && uh->iuser.tile == tile_number) {
|
2019-10-02 00:07:06 +10:00
|
|
|
return uh;
|
|
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
return nullptr;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
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
2019-12-12 16:06:08 +01:00
|
|
|
static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *image, int tile_number)
|
2018-03-19 14:17:59 +01:00
|
|
|
{
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (UndoImageHandle *, uh, undo_handles) {
|
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
2019-12-12 16:06:08 +01:00
|
|
|
if (image == uh->image_ref.ptr && uh->iuser.tile == tile_number) {
|
2019-10-02 00:07:06 +10:00
|
|
|
return uh;
|
|
|
|
|
}
|
2018-03-19 14:17:59 +01:00
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
return nullptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
|
2020-03-04 14:40:21 +01:00
|
|
|
static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, ImageUser *iuser)
|
2019-10-02 00:07:06 +10:00
|
|
|
{
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(uhandle_lookup(undo_handles, image, iuser->tile) == nullptr);
|
2022-07-26 13:09:22 +10:00
|
|
|
UndoImageHandle *uh = static_cast<UndoImageHandle *>(MEM_callocN(sizeof(*uh), __func__));
|
2019-10-02 00:07:06 +10:00
|
|
|
uh->image_ref.ptr = image;
|
2020-03-04 14:40:21 +01:00
|
|
|
uh->iuser = *iuser;
|
2022-07-25 07:56:17 +02:00
|
|
|
uh->iuser.scene = nullptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
BLI_addtail(undo_handles, uh);
|
|
|
|
|
return uh;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-04 14:40:21 +01:00
|
|
|
static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image, ImageUser *iuser)
|
2019-10-02 00:07:06 +10:00
|
|
|
{
|
2020-03-04 14:40:21 +01:00
|
|
|
UndoImageHandle *uh = uhandle_lookup(undo_handles, image, iuser->tile);
|
2022-07-25 07:56:17 +02:00
|
|
|
if (uh == nullptr) {
|
2020-03-04 14:40:21 +01:00
|
|
|
uh = uhandle_add(undo_handles, image, iuser);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
return uh;
|
2018-03-19 14:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Implements ED Undo System
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
struct ImageUndoStep {
|
2018-03-19 14:17:59 +01:00
|
|
|
UndoStep step;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
/** #UndoImageHandle */
|
|
|
|
|
ListBase handles;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* #PaintTile
|
|
|
|
|
* Run-time only data (active during a paint stroke).
|
|
|
|
|
*/
|
2022-07-26 13:09:22 +10:00
|
|
|
PaintTileMap *paint_tile_map;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2019-02-05 14:24:11 +11:00
|
|
|
bool is_encode_init;
|
|
|
|
|
ePaintMode paint_mode;
|
2022-07-25 07:56:17 +02:00
|
|
|
};
|
2018-03-19 14:17:59 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
/**
|
|
|
|
|
* Find the previous undo buffer from this one.
|
|
|
|
|
* \note We could look into undo steps even further back.
|
|
|
|
|
*/
|
|
|
|
|
static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev,
|
|
|
|
|
const Image *image,
|
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
2019-12-12 16:06:08 +01:00
|
|
|
int tile_number,
|
2019-10-02 00:07:06 +10:00
|
|
|
const UndoImageBuf *ubuf)
|
|
|
|
|
{
|
2020-03-29 16:33:51 +11:00
|
|
|
/* Use name lookup because the pointer is cleared for previous steps. */
|
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
2019-12-12 16:06:08 +01:00
|
|
|
UndoImageHandle *uh_prev = uhandle_lookup_by_name(&us_prev->handles, image, tile_number);
|
2022-07-25 07:56:17 +02:00
|
|
|
if (uh_prev != nullptr) {
|
2023-05-03 14:13:27 +10:00
|
|
|
UndoImageBuf *ubuf_reference = uhandle_lookup_ubuf(uh_prev, image, ubuf->ibuf_filepath);
|
2019-10-02 00:07:06 +10:00
|
|
|
if (ubuf_reference) {
|
|
|
|
|
ubuf_reference = ubuf_reference->post;
|
|
|
|
|
if ((ubuf_reference->image_dims[0] == ubuf->image_dims[0]) &&
|
|
|
|
|
(ubuf_reference->image_dims[1] == ubuf->image_dims[1]))
|
|
|
|
|
{
|
|
|
|
|
return ubuf_reference;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
return nullptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 14:17:59 +01:00
|
|
|
static bool image_undosys_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
Object *obact = CTX_data_active_object(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-03 13:25:03 +02:00
|
|
|
ScrArea *area = CTX_wm_area(C);
|
|
|
|
|
if (area && (area->spacetype == SPACE_IMAGE)) {
|
|
|
|
|
SpaceImage *sima = (SpaceImage *)area->spacedata.first;
|
2019-01-31 22:55:29 +11:00
|
|
|
if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
|
2018-03-19 14:17:59 +01:00
|
|
|
return true;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2018-03-19 14:17:59 +01:00
|
|
|
}
|
2019-01-31 22:55:29 +11:00
|
|
|
else {
|
2018-04-05 18:20:27 +02:00
|
|
|
if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) {
|
2018-03-19 14:17:59 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-03 08:36:28 +10:00
|
|
|
static void image_undosys_step_encode_init(bContext * /*C*/, UndoStep *us_p)
|
2018-03-19 14:17:59 +01:00
|
|
|
{
|
2022-07-25 07:56:17 +02:00
|
|
|
ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
|
2018-03-19 14:17:59 +01:00
|
|
|
/* dummy, memory is cleared anyway. */
|
2019-02-05 14:24:11 +11:00
|
|
|
us->is_encode_init = true;
|
2019-10-02 00:07:06 +10:00
|
|
|
BLI_listbase_clear(&us->handles);
|
2022-07-25 07:56:17 +02:00
|
|
|
us->paint_tile_map = MEM_new<PaintTileMap>(__func__);
|
2018-03-19 14:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-03 08:36:28 +10:00
|
|
|
static bool image_undosys_step_encode(bContext *C, Main * /*bmain*/, UndoStep *us_p)
|
2018-03-19 14:17:59 +01:00
|
|
|
{
|
2019-10-02 00:07:06 +10:00
|
|
|
/* Encoding is done along the way by adding tiles
|
|
|
|
|
* to the current 'ImageUndoStep' added by encode_init.
|
|
|
|
|
*
|
|
|
|
|
* This function ensures there are previous and current states of the image in the undo buffer.
|
|
|
|
|
*/
|
2022-07-25 07:56:17 +02:00
|
|
|
ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-03-19 14:17:59 +01:00
|
|
|
BLI_assert(us->step.data_size == 0);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-02-05 14:24:11 +11:00
|
|
|
if (us->is_encode_init) {
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
ImBuf *tmpibuf = imbuf_alloc_temp_tile();
|
|
|
|
|
|
2022-07-26 13:09:22 +10:00
|
|
|
ImageUndoStep *us_reference = reinterpret_cast<ImageUndoStep *>(
|
|
|
|
|
ED_undo_stack_get()->step_active);
|
2019-10-02 00:07:06 +10:00
|
|
|
while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) {
|
2022-07-25 07:56:17 +02:00
|
|
|
us_reference = reinterpret_cast<ImageUndoStep *>(us_reference->step.prev);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
|
2023-03-27 12:08:14 +11:00
|
|
|
/* Initialize undo tiles from paint-tiles (if they exist). */
|
2022-07-25 07:56:17 +02:00
|
|
|
for (PaintTile *ptile : us->paint_tile_map->map.values()) {
|
2019-10-02 00:07:06 +10:00
|
|
|
if (ptile->valid) {
|
2020-03-04 14:40:21 +01:00
|
|
|
UndoImageHandle *uh = uhandle_ensure(&us->handles, ptile->image, &ptile->iuser);
|
2019-10-02 00:07:06 +10:00
|
|
|
UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, ptile->image, ptile->ibuf);
|
|
|
|
|
|
2022-07-26 13:09:22 +10:00
|
|
|
UndoImageTile *utile = static_cast<UndoImageTile *>(
|
|
|
|
|
MEM_callocN(sizeof(*utile), "UndoImageTile"));
|
2019-10-02 00:07:06 +10:00
|
|
|
utile->users = 1;
|
|
|
|
|
utile->rect.pt = ptile->rect.pt;
|
2022-07-25 07:56:17 +02:00
|
|
|
ptile->rect.pt = nullptr;
|
2019-11-02 12:29:46 +11:00
|
|
|
const uint tile_index = index_from_xy(ptile->x_tile, ptile->y_tile, ubuf_pre->tiles_dims);
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(ubuf_pre->tiles[tile_index] == nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
ubuf_pre->tiles[tile_index] = utile;
|
2019-02-05 14:24:11 +11:00
|
|
|
}
|
2019-10-02 00:07:06 +10:00
|
|
|
ptile_free(ptile);
|
|
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
us->paint_tile_map->map.clear();
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (UndoImageHandle *, uh, &us->handles) {
|
|
|
|
|
LISTBASE_FOREACH (UndoImageBuf *, ubuf_pre, &uh->buffers) {
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, &uh->iuser, nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
const bool has_float = ibuf->float_buffer.data;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(ubuf_pre->post == nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
ubuf_pre->post = ubuf_from_image_no_tiles(uh->image_ref.ptr, ibuf);
|
|
|
|
|
UndoImageBuf *ubuf_post = ubuf_pre->post;
|
|
|
|
|
|
|
|
|
|
if (ubuf_pre->image_dims[0] != ubuf_post->image_dims[0] ||
|
|
|
|
|
ubuf_pre->image_dims[1] != ubuf_post->image_dims[1])
|
|
|
|
|
{
|
|
|
|
|
ubuf_from_image_all_tiles(ubuf_post, ibuf);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Search for the previous buffer. */
|
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
2019-12-12 16:06:08 +01:00
|
|
|
UndoImageBuf *ubuf_reference =
|
|
|
|
|
(us_reference ? ubuf_lookup_from_reference(
|
|
|
|
|
us_reference, uh->image_ref.ptr, uh->iuser.tile, ubuf_post) :
|
2022-07-25 07:56:17 +02:00
|
|
|
nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (uint y_tile = 0; y_tile < ubuf_pre->tiles_dims[1]; y_tile += 1) {
|
2019-10-02 01:54:58 +10:00
|
|
|
uint y = y_tile << ED_IMAGE_UNDO_TILE_BITS;
|
2019-10-02 00:07:06 +10:00
|
|
|
for (uint x_tile = 0; x_tile < ubuf_pre->tiles_dims[0]; x_tile += 1) {
|
2019-10-02 01:54:58 +10:00
|
|
|
uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS;
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2022-07-26 13:09:22 +10:00
|
|
|
if ((ubuf_reference != nullptr) &&
|
|
|
|
|
((ubuf_pre->tiles[i] == nullptr) ||
|
|
|
|
|
/* In this case the paint stroke as has added a tile
|
|
|
|
|
* which we have a duplicate reference available. */
|
|
|
|
|
(ubuf_pre->tiles[i]->users == 1)))
|
|
|
|
|
{
|
2022-07-25 07:56:17 +02:00
|
|
|
if (ubuf_pre->tiles[i] != nullptr) {
|
2019-10-02 00:07:06 +10:00
|
|
|
/* If we have a reference, re-use this single use tile for the post state. */
|
|
|
|
|
BLI_assert(ubuf_pre->tiles[i]->users == 1);
|
|
|
|
|
ubuf_post->tiles[i] = ubuf_pre->tiles[i];
|
2022-07-25 07:56:17 +02:00
|
|
|
ubuf_pre->tiles[i] = nullptr;
|
2019-10-02 00:07:06 +10:00
|
|
|
utile_init_from_imbuf(ubuf_post->tiles[i], x, y, ibuf, tmpibuf);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(ubuf_post->tiles[i] == nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
ubuf_post->tiles[i] = ubuf_reference->tiles[i];
|
|
|
|
|
ubuf_post->tiles[i]->users += 1;
|
|
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(ubuf_pre->tiles[i] == nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
ubuf_pre->tiles[i] = ubuf_reference->tiles[i];
|
|
|
|
|
ubuf_pre->tiles[i]->users += 1;
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(ubuf_pre->tiles[i] != nullptr);
|
|
|
|
|
BLI_assert(ubuf_post->tiles[i] != nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
UndoImageTile *utile = utile_alloc(has_float);
|
|
|
|
|
utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf);
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
if (ubuf_pre->tiles[i] != nullptr) {
|
2019-10-02 00:07:06 +10:00
|
|
|
ubuf_post->tiles[i] = utile;
|
|
|
|
|
utile->users = 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ubuf_pre->tiles[i] = utile;
|
|
|
|
|
ubuf_post->tiles[i] = utile;
|
|
|
|
|
utile->users = 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(ubuf_pre->tiles[i] != nullptr);
|
|
|
|
|
BLI_assert(ubuf_post->tiles[i] != nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
i += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
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
2019-12-12 16:06:08 +01:00
|
|
|
BLI_assert(i == ubuf_pre->tiles_len);
|
|
|
|
|
BLI_assert(i == ubuf_post->tiles_len);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
BKE_image_release_ibuf(uh->image_ref.ptr, ibuf, nullptr);
|
2019-02-05 14:24:11 +11:00
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
IMB_freeImBuf(tmpibuf);
|
|
|
|
|
|
|
|
|
|
/* Useful to debug tiles are stored correctly. */
|
|
|
|
|
if (false) {
|
|
|
|
|
uhandle_restore_list(&us->handles, false);
|
|
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2019-02-05 14:24:11 +11:00
|
|
|
else {
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(C != nullptr);
|
2019-02-05 14:24:11 +11:00
|
|
|
/* Happens when switching modes. */
|
|
|
|
|
ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
|
|
|
|
|
BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D));
|
|
|
|
|
us->paint_mode = paint_mode;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-02-05 14:24:11 +11:00
|
|
|
us_p->is_applied = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-03-19 14:17:59 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static void image_undosys_step_decode_undo_impl(ImageUndoStep *us, bool is_final)
|
2019-02-05 14:24:11 +11:00
|
|
|
{
|
|
|
|
|
BLI_assert(us->step.is_applied == true);
|
2019-10-02 00:07:06 +10:00
|
|
|
uhandle_restore_list(&us->handles, !is_final);
|
2019-02-05 14:24:11 +11:00
|
|
|
us->step.is_applied = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void image_undosys_step_decode_redo_impl(ImageUndoStep *us)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(us->step.is_applied == false);
|
2019-10-02 00:07:06 +10:00
|
|
|
uhandle_restore_list(&us->handles, false);
|
2019-02-05 14:24:11 +11:00
|
|
|
us->step.is_applied = true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-26 13:13:47 +10:00
|
|
|
static void image_undosys_step_decode_undo(ImageUndoStep *us, bool is_final)
|
2019-02-05 14:24:11 +11:00
|
|
|
{
|
2021-03-15 23:29:33 +11:00
|
|
|
/* Walk forward over any applied steps of same type,
|
|
|
|
|
* then walk back in the next loop, un-applying them. */
|
2019-02-05 14:24:11 +11:00
|
|
|
ImageUndoStep *us_iter = us;
|
|
|
|
|
while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) {
|
|
|
|
|
if (us_iter->step.next->is_applied == false) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
us_iter = (ImageUndoStep *)us_iter->step.next;
|
|
|
|
|
}
|
2019-07-26 20:01:51 +10:00
|
|
|
while (us_iter != us || (!is_final && us_iter == us)) {
|
2021-03-15 23:29:33 +11:00
|
|
|
BLI_assert(us_iter->step.type == us->step.type); /* Previous loop ensures this. */
|
2019-10-02 00:07:06 +10:00
|
|
|
image_undosys_step_decode_undo_impl(us_iter, is_final);
|
2019-07-26 20:01:51 +10:00
|
|
|
if (us_iter == us) {
|
2019-07-26 13:13:47 +10:00
|
|
|
break;
|
|
|
|
|
}
|
2019-02-05 14:24:11 +11:00
|
|
|
us_iter = (ImageUndoStep *)us_iter->step.prev;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void image_undosys_step_decode_redo(ImageUndoStep *us)
|
|
|
|
|
{
|
|
|
|
|
ImageUndoStep *us_iter = us;
|
|
|
|
|
while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) {
|
|
|
|
|
if (us_iter->step.prev->is_applied == true) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
us_iter = (ImageUndoStep *)us_iter->step.prev;
|
|
|
|
|
}
|
|
|
|
|
while (us_iter && (us_iter->step.is_applied == false)) {
|
|
|
|
|
image_undosys_step_decode_redo_impl(us_iter);
|
|
|
|
|
if (us_iter == us) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
us_iter = (ImageUndoStep *)us_iter->step.next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-11 09:36:59 +10:00
|
|
|
static void image_undosys_step_decode(
|
2023-06-03 08:36:28 +10:00
|
|
|
bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir dir, bool is_final)
|
2018-03-19 14:17:59 +01:00
|
|
|
{
|
2021-03-15 23:29:33 +11:00
|
|
|
/* NOTE: behavior for undo/redo closely matches sculpt undo. */
|
2021-02-04 22:03:39 +01:00
|
|
|
BLI_assert(dir != STEP_INVALID);
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
|
2021-02-04 22:03:39 +01:00
|
|
|
if (dir == STEP_UNDO) {
|
2019-07-26 13:13:47 +10:00
|
|
|
image_undosys_step_decode_undo(us, is_final);
|
2019-02-05 14:24:11 +11:00
|
|
|
}
|
2021-02-04 22:03:39 +01:00
|
|
|
else if (dir == STEP_REDO) {
|
2019-02-05 14:24:11 +11:00
|
|
|
image_undosys_step_decode_redo(us);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (us->paint_mode == PAINT_MODE_TEXTURE_3D) {
|
2022-07-25 07:56:17 +02:00
|
|
|
ED_object_mode_set_ex(C, OB_MODE_TEXTURE_PAINT, false, nullptr);
|
2019-02-05 14:24:11 +11:00
|
|
|
}
|
2019-01-31 12:01:38 +11:00
|
|
|
|
|
|
|
|
/* Refresh texture slots. */
|
|
|
|
|
ED_editors_init_for_undo(bmain);
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 14:17:59 +01:00
|
|
|
static void image_undosys_step_free(UndoStep *us_p)
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2018-03-19 14:17:59 +01:00
|
|
|
ImageUndoStep *us = (ImageUndoStep *)us_p;
|
2019-10-02 00:07:06 +10:00
|
|
|
uhandle_free_list(&us->handles);
|
|
|
|
|
|
2022-07-25 07:56:17 +02:00
|
|
|
/* Typically this map will have been cleared. */
|
|
|
|
|
MEM_delete(us->paint_tile_map);
|
|
|
|
|
us->paint_tile_map = nullptr;
|
2018-04-03 16:47:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void image_undosys_foreach_ID_ref(UndoStep *us_p,
|
|
|
|
|
UndoTypeForEachIDRefFn foreach_ID_ref_fn,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
2022-07-25 07:56:17 +02:00
|
|
|
ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (UndoImageHandle *, uh, &us->handles) {
|
2019-10-02 00:07:06 +10:00
|
|
|
foreach_ID_ref_fn(user_data, ((UndoRefID *)&uh->image_ref));
|
2018-04-03 16:47:25 +02:00
|
|
|
}
|
2018-03-19 14:17:59 +01:00
|
|
|
}
|
2018-03-23 07:51:56 +01:00
|
|
|
|
2018-03-19 14:17:59 +01:00
|
|
|
void ED_image_undosys_type(UndoType *ut)
|
|
|
|
|
{
|
|
|
|
|
ut->name = "Image";
|
|
|
|
|
ut->poll = image_undosys_poll;
|
|
|
|
|
ut->step_encode_init = image_undosys_step_encode_init;
|
|
|
|
|
ut->step_encode = image_undosys_step_encode;
|
|
|
|
|
ut->step_decode = image_undosys_step_decode;
|
|
|
|
|
ut->step_free = image_undosys_step_free;
|
|
|
|
|
|
2018-04-03 16:47:25 +02:00
|
|
|
ut->step_foreach_ID_ref = image_undosys_foreach_ID_ref;
|
|
|
|
|
|
2022-01-31 10:29:46 +11:00
|
|
|
/* NOTE: this is actually a confusing case, since it expects a valid context, but only in a
|
2021-01-06 18:06:11 +01:00
|
|
|
* specific case, see `image_undosys_step_encode` code. We cannot specify
|
2023-06-04 18:35:12 +10:00
|
|
|
* `UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE` though, as it can be called with a null context by
|
2021-01-06 18:06:11 +01:00
|
|
|
* current code. */
|
2021-07-13 17:28:07 +10:00
|
|
|
ut->flags = UNDOTYPE_FLAG_DECODE_ACTIVE_STEP;
|
2018-03-19 14:17:59 +01:00
|
|
|
|
|
|
|
|
ut->step_size = sizeof(ImageUndoStep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Utilities
|
2020-09-18 14:17:17 +10:00
|
|
|
*
|
|
|
|
|
* \note image undo exposes #ED_image_undo_push_begin, #ED_image_undo_push_end
|
|
|
|
|
* which must be called by the operator directly.
|
|
|
|
|
*
|
|
|
|
|
* Unlike most other undo stacks this is needed:
|
|
|
|
|
* - So we can always access the state before the image was painted onto,
|
|
|
|
|
* which is needed if previous undo states aren't image-type.
|
|
|
|
|
* - So operators can access the pixel-data before the stroke was applied, at run-time.
|
2018-03-19 14:17:59 +01:00
|
|
|
* \{ */
|
|
|
|
|
|
2023-07-02 19:37:22 +10:00
|
|
|
PaintTileMap *ED_image_paint_tile_map_get()
|
2018-03-19 14:17:59 +01:00
|
|
|
{
|
2018-04-03 08:35:42 +02:00
|
|
|
UndoStack *ustack = ED_undo_stack_get();
|
2019-02-05 14:24:11 +11:00
|
|
|
UndoStep *us_prev = ustack->step_init;
|
|
|
|
|
UndoStep *us_p = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE);
|
2022-07-25 07:56:17 +02:00
|
|
|
ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
|
2019-02-05 14:24:11 +11:00
|
|
|
/* We should always have an undo push started when accessing tiles,
|
|
|
|
|
* not doing this means we won't have paint_mode correctly set. */
|
|
|
|
|
BLI_assert(us_p == us_prev);
|
|
|
|
|
if (us_p != us_prev) {
|
|
|
|
|
/* Fallback value until we can be sure this never happens. */
|
|
|
|
|
us->paint_mode = PAINT_MODE_TEXTURE_2D;
|
|
|
|
|
}
|
2022-07-25 07:56:17 +02:00
|
|
|
return us->paint_tile_map;
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 14:17:59 +01:00
|
|
|
void ED_image_undo_restore(UndoStep *us)
|
2018-03-23 07:51:56 +01:00
|
|
|
{
|
2022-07-25 07:56:17 +02:00
|
|
|
PaintTileMap *paint_tile_map = reinterpret_cast<ImageUndoStep *>(us)->paint_tile_map;
|
|
|
|
|
ptile_restore_runtime_map(paint_tile_map);
|
|
|
|
|
ptile_invalidate_map(paint_tile_map);
|
2018-03-23 07:51:56 +01:00
|
|
|
}
|
2018-03-19 14:17:59 +01:00
|
|
|
|
2019-10-02 00:07:06 +10:00
|
|
|
static ImageUndoStep *image_undo_push_begin(const char *name, int paint_mode)
|
2019-02-05 14:24:11 +11:00
|
|
|
{
|
|
|
|
|
UndoStack *ustack = ED_undo_stack_get();
|
2022-07-25 07:56:17 +02:00
|
|
|
bContext *C = nullptr; /* special case, we never read from this. */
|
2019-02-05 14:24:11 +11:00
|
|
|
UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE);
|
2022-07-25 07:56:17 +02:00
|
|
|
ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
|
2022-05-16 15:42:43 +02:00
|
|
|
BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D, PAINT_MODE_SCULPT));
|
2022-07-25 07:56:17 +02:00
|
|
|
us->paint_mode = (ePaintMode)paint_mode;
|
2019-10-02 00:07:06 +10:00
|
|
|
return us;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ED_image_undo_push_begin(const char *name, int paint_mode)
|
|
|
|
|
{
|
|
|
|
|
image_undo_push_begin(name, paint_mode);
|
|
|
|
|
}
|
|
|
|
|
|
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
2019-12-12 16:06:08 +01:00
|
|
|
void ED_image_undo_push_begin_with_image(const char *name,
|
|
|
|
|
Image *image,
|
|
|
|
|
ImBuf *ibuf,
|
2020-03-04 14:40:21 +01:00
|
|
|
ImageUser *iuser)
|
2019-10-02 00:07:06 +10:00
|
|
|
{
|
|
|
|
|
ImageUndoStep *us = image_undo_push_begin(name, PAINT_MODE_TEXTURE_2D);
|
|
|
|
|
|
2020-03-04 14:40:21 +01:00
|
|
|
BLI_assert(BKE_image_get_tile(image, iuser->tile));
|
|
|
|
|
UndoImageHandle *uh = uhandle_ensure(&us->handles, image, iuser);
|
2019-10-02 00:07:06 +10:00
|
|
|
UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, image, ibuf);
|
2022-07-25 07:56:17 +02:00
|
|
|
BLI_assert(ubuf_pre->post == nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
|
2022-07-26 13:09:22 +10:00
|
|
|
ImageUndoStep *us_reference = reinterpret_cast<ImageUndoStep *>(
|
|
|
|
|
ED_undo_stack_get()->step_active);
|
2019-10-02 00:07:06 +10:00
|
|
|
while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) {
|
2022-07-25 07:56:17 +02:00
|
|
|
us_reference = reinterpret_cast<ImageUndoStep *>(us_reference->step.prev);
|
2019-10-02 00:07:06 +10:00
|
|
|
}
|
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
2019-12-12 16:06:08 +01:00
|
|
|
UndoImageBuf *ubuf_reference = (us_reference ? ubuf_lookup_from_reference(
|
2020-03-04 14:40:21 +01:00
|
|
|
us_reference, image, iuser->tile, ubuf_pre) :
|
2022-07-25 07:56:17 +02:00
|
|
|
nullptr);
|
2019-10-02 00:07:06 +10:00
|
|
|
|
|
|
|
|
if (ubuf_reference) {
|
|
|
|
|
memcpy(ubuf_pre->tiles, ubuf_reference->tiles, sizeof(*ubuf_pre->tiles) * ubuf_pre->tiles_len);
|
2022-07-25 07:56:17 +02:00
|
|
|
for (uint32_t i = 0; i < ubuf_pre->tiles_len; i++) {
|
2019-10-02 00:07:06 +10:00
|
|
|
UndoImageTile *utile = ubuf_pre->tiles[i];
|
|
|
|
|
utile->users += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ubuf_from_image_all_tiles(ubuf_pre, ibuf);
|
|
|
|
|
}
|
2019-02-05 14:24:11 +11:00
|
|
|
}
|
|
|
|
|
|
2023-07-02 19:37:22 +10:00
|
|
|
void ED_image_undo_push_end()
|
2019-02-05 14:24:11 +11:00
|
|
|
{
|
|
|
|
|
UndoStack *ustack = ED_undo_stack_get();
|
2022-07-25 07:56:17 +02:00
|
|
|
BKE_undosys_step_push(ustack, nullptr, nullptr);
|
2020-05-14 14:52:07 +10:00
|
|
|
BKE_undosys_stack_limit_steps_and_memory_defaults(ustack);
|
2019-08-03 00:07:32 +10:00
|
|
|
WM_file_tag_modified();
|
2019-02-05 14:24:11 +11:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 14:17:59 +01:00
|
|
|
/** \} */
|