2023-05-31 16:19:06 +02:00
|
|
|
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
* SPDX-FileCopyrightText: 2024 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2012-04-30 14:24:11 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup imbuf
|
2011-02-27 20:23:21 +00:00
|
|
|
*/
|
|
|
|
|
|
2023-07-22 11:27:25 +10:00
|
|
|
#include <cmath>
|
|
|
|
|
#include <cstdlib>
|
Merge image related changes from the render branch. This includes the image
tile cache code in imbuf, but it is not hooked up to the render engine.
Imbuf module: some small refactoring and removing a lot of unused or old code
(about 6.5k lines).
* Added a ImFileType struct with callbacks to make adding an file format type,
or making changes to the API easier.
* Move imbuf init/exit code into IMB_init()/IMB_exit() functions.
* Increased mipmap levels from 10 to 20, you run into this limit already with
a 2k image.
* Removed hamx, amiga, anim5 format support.
* Removed colormap saving, only simple colormap code now for reading tga.
* Removed gen_dynlibtiff.py, editing this is almost as much work as just
editing the code directly.
* Functions removed that were only used for sequencer plugin API:
IMB_anim_nextpic, IMB_clever_double, IMB_antialias, IMB_gamwarp,
IMB_scalefieldImBuf, IMB_scalefastfieldImBuf, IMB_onethird, IMB_halflace,
IMB_dit0, IMB_dit2, IMB_cspace
* Write metadata info into OpenEXR images. Can be viewed with the command
line utility 'exrheader'
For the image tile cache code, see this page:
http://wiki.blender.org/index.php/Dev:2.5/Source/Imaging/ImageTileCache
2010-05-07 15:18:04 +00:00
|
|
|
|
2012-08-07 16:47:46 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_task.h"
|
|
|
|
|
#include "BLI_utildefines.h"
|
2012-03-08 14:23:34 +00:00
|
|
|
|
2024-01-18 22:50:23 +02:00
|
|
|
#include "IMB_colormanagement.hh"
|
|
|
|
|
#include "IMB_imbuf.hh"
|
|
|
|
|
#include "IMB_imbuf_types.hh"
|
2007-07-10 19:13:03 +00:00
|
|
|
|
2023-06-03 08:36:28 +10:00
|
|
|
void IMB_convert_rgba_to_abgr(ImBuf *ibuf)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2015-05-29 13:38:20 +02:00
|
|
|
size_t size;
|
2023-05-18 10:19:01 +02:00
|
|
|
uchar rt, *cp = ibuf->byte_buffer.data;
|
|
|
|
|
float rtf, *cpf = ibuf->float_buffer.data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (ibuf->byte_buffer.data) {
|
2008-01-20 18:55:56 +00:00
|
|
|
size = ibuf->x * ibuf->y;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 07:52:14 +00:00
|
|
|
while (size-- > 0) {
|
2012-05-13 22:05:51 +00:00
|
|
|
rt = cp[0];
|
|
|
|
|
cp[0] = cp[3];
|
|
|
|
|
cp[3] = rt;
|
|
|
|
|
rt = cp[1];
|
|
|
|
|
cp[1] = cp[2];
|
|
|
|
|
cp[2] = rt;
|
|
|
|
|
cp += 4;
|
2008-01-20 18:55:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (ibuf->float_buffer.data) {
|
2008-01-20 18:55:56 +00:00
|
|
|
size = ibuf->x * ibuf->y;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 07:52:14 +00:00
|
|
|
while (size-- > 0) {
|
2012-05-13 22:05:51 +00:00
|
|
|
rtf = cpf[0];
|
|
|
|
|
cpf[0] = cpf[3];
|
|
|
|
|
cpf[3] = rtf;
|
|
|
|
|
rtf = cpf[1];
|
|
|
|
|
cpf[1] = cpf[2];
|
|
|
|
|
cpf[2] = rtf;
|
|
|
|
|
cpf += 4;
|
Orange branch: OpenEXR finally in Blender!
Credits go to Gernot Ziegler, who originally coded EXR support, and to
Austin Benesh for bringing it further. Kent Mein provided a lot of code
for integrating float buffers in Blender imbuf and ImBuf API cleanup,
and provided Make and Scons and static linking.
At this moment; the EXR libraries are a *dependency*, so you cannot get
the Orange branch compiled without having OpenEXR installed. Get the
(precompiled or sources) stuff from www.openexr.com. Current default is
that the headers and lib resides in /user/local/
Several changes/additions/fixes were added:
- EXR code only supported 'half' format (16 bits per channel). I've added
float writing, but for reading it I need tomorrow. :)
- Quite some clumsy copying of data happened in EXR code.
- cleaned up the api calls already a bit, preparing for more advanced
support
- Zbuffers were saved 16 bits, now 32 bits
- automatic adding of .exr extensions went wrong
Imbuf:
- added proper imbuf->flags and imbuf->mall support for float buffers, it
was created for *each* imbuf. :)
- found bugs for float buffers in scaling and flipping. Code there will
need more checks still
- imbuf also needs to be verified to behave properly when no 32 bits
rect exists (for saving for example)
TODO:
- support internal float images for textures, backbuf, AO probes, and
display in Image window
Hope this commit won't screwup syncing with bf-blender... :/
2006-01-09 00:40:35 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
2020-09-14 16:11:13 +10:00
|
|
|
|
2012-08-07 16:47:46 +00:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Threaded Image Processing
|
2020-09-14 16:11:13 +10:00
|
|
|
* \{ */
|
2012-08-07 16:47:46 +00:00
|
|
|
|
2020-04-30 07:59:23 +02:00
|
|
|
static void processor_apply_func(TaskPool *__restrict pool, void *taskdata)
|
2013-12-25 20:32:13 +06:00
|
|
|
{
|
2020-04-21 15:36:35 +02:00
|
|
|
void (*do_thread)(void *) = (void (*)(void *))BLI_task_pool_user_data(pool);
|
2013-12-25 20:32:13 +06:00
|
|
|
do_thread(taskdata);
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-07 16:47:46 +00:00
|
|
|
void IMB_processor_apply_threaded(
|
|
|
|
|
int buffer_lines,
|
|
|
|
|
int handle_size,
|
|
|
|
|
void *init_customdata,
|
|
|
|
|
void(init_handle)(void *handle, int start_line, int tot_line, void *customdata),
|
|
|
|
|
void *(do_thread)(void *))
|
|
|
|
|
{
|
2013-12-25 20:32:13 +06:00
|
|
|
const int lines_per_task = 64;
|
|
|
|
|
|
|
|
|
|
TaskPool *task_pool;
|
2012-08-07 16:47:46 +00:00
|
|
|
|
2013-12-25 20:32:13 +06:00
|
|
|
void *handles;
|
|
|
|
|
int total_tasks = (buffer_lines + lines_per_task - 1) / lines_per_task;
|
|
|
|
|
int i, start_line;
|
2012-08-07 16:47:46 +00:00
|
|
|
|
2023-05-02 11:32:27 +02:00
|
|
|
task_pool = BLI_task_pool_create(reinterpret_cast<void *>(do_thread), TASK_PRIORITY_HIGH);
|
2012-08-07 16:47:46 +00:00
|
|
|
|
2013-12-25 20:32:13 +06:00
|
|
|
handles = MEM_callocN(handle_size * total_tasks, "processor apply threaded handles");
|
2012-08-07 16:47:46 +00:00
|
|
|
|
|
|
|
|
start_line = 0;
|
|
|
|
|
|
2013-12-25 20:32:13 +06:00
|
|
|
for (i = 0; i < total_tasks; i++) {
|
|
|
|
|
int lines_per_current_task;
|
2012-08-07 16:47:46 +00:00
|
|
|
void *handle = ((char *)handles) + handle_size * i;
|
|
|
|
|
|
2019-04-23 11:01:30 +10:00
|
|
|
if (i < total_tasks - 1) {
|
2013-12-25 20:32:13 +06:00
|
|
|
lines_per_current_task = lines_per_task;
|
2019-04-23 11:01:30 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2013-12-25 20:32:13 +06:00
|
|
|
lines_per_current_task = buffer_lines - start_line;
|
2019-04-23 11:01:30 +10:00
|
|
|
}
|
2012-08-07 16:47:46 +00:00
|
|
|
|
2013-12-25 20:32:13 +06:00
|
|
|
init_handle(handle, start_line, lines_per_current_task, init_customdata);
|
2012-08-07 16:47:46 +00:00
|
|
|
|
2023-05-02 11:32:27 +02:00
|
|
|
BLI_task_pool_push(task_pool, processor_apply_func, handle, false, nullptr);
|
2012-08-07 16:47:46 +00:00
|
|
|
|
2013-12-25 20:32:13 +06:00
|
|
|
start_line += lines_per_task;
|
2012-08-07 16:47:46 +00:00
|
|
|
}
|
|
|
|
|
|
2013-12-25 20:32:13 +06:00
|
|
|
/* work and wait until tasks are done */
|
|
|
|
|
BLI_task_pool_work_and_wait(task_pool);
|
2012-08-07 16:47:46 +00:00
|
|
|
|
2013-12-25 20:32:13 +06:00
|
|
|
/* Free memory. */
|
2016-05-05 13:15:51 +02:00
|
|
|
MEM_freeN(handles);
|
|
|
|
|
BLI_task_pool_free(task_pool);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-02 19:37:19 +10:00
|
|
|
struct ScanlineGlobalData {
|
2016-05-05 13:15:51 +02:00
|
|
|
void *custom_data;
|
|
|
|
|
ScanlineThreadFunc do_thread;
|
2023-07-02 19:37:19 +10:00
|
|
|
};
|
2016-05-05 13:15:51 +02:00
|
|
|
|
2021-06-11 15:55:09 +02:00
|
|
|
static void processor_apply_parallel(void *__restrict userdata,
|
|
|
|
|
const int scanline,
|
2023-05-02 11:32:27 +02:00
|
|
|
const TaskParallelTLS *__restrict /*tls*/)
|
2016-05-05 13:15:51 +02:00
|
|
|
{
|
2023-05-02 11:32:27 +02:00
|
|
|
ScanlineGlobalData *data = static_cast<ScanlineGlobalData *>(userdata);
|
2021-06-11 15:55:09 +02:00
|
|
|
data->do_thread(data->custom_data, scanline);
|
2016-05-05 13:15:51 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-05 14:18:11 +02:00
|
|
|
void IMB_processor_apply_threaded_scanlines(int total_scanlines,
|
2016-05-05 13:15:51 +02:00
|
|
|
ScanlineThreadFunc do_thread,
|
|
|
|
|
void *custom_data)
|
|
|
|
|
{
|
2021-06-11 15:55:09 +02:00
|
|
|
TaskParallelSettings settings;
|
2023-05-02 11:32:27 +02:00
|
|
|
ScanlineGlobalData data = {};
|
|
|
|
|
data.do_thread = do_thread;
|
|
|
|
|
data.custom_data = custom_data;
|
2021-06-11 15:55:09 +02:00
|
|
|
|
|
|
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
|
|
|
BLI_task_parallel_range(0, total_scanlines, &data, processor_apply_parallel, &settings);
|
2012-08-07 16:47:46 +00:00
|
|
|
}
|
2013-01-05 15:33:18 +00:00
|
|
|
|
|
|
|
|
/** \} */
|
2020-09-14 16:11:13 +10:00
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
2013-01-05 15:33:18 +00:00
|
|
|
/** \name Alpha-under
|
2020-09-14 16:11:13 +10:00
|
|
|
* \{ */
|
2013-01-05 15:33:18 +00:00
|
|
|
|
|
|
|
|
void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3])
|
|
|
|
|
{
|
2023-05-02 20:09:09 +10:00
|
|
|
size_t a = size_t(x) * y;
|
2013-01-05 15:33:18 +00:00
|
|
|
float *fp = rect_float;
|
|
|
|
|
|
|
|
|
|
while (a--) {
|
2020-10-19 11:29:47 +02:00
|
|
|
const float mul = 1.0f - fp[3];
|
|
|
|
|
madd_v3_v3fl(fp, backcol, mul);
|
2013-01-05 15:33:18 +00:00
|
|
|
fp[3] = 1.0f;
|
|
|
|
|
|
|
|
|
|
fp += 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 16:29:06 +10:00
|
|
|
void IMB_alpha_under_color_byte(uchar *rect, int x, int y, const float backcol[3])
|
2013-01-05 15:33:18 +00:00
|
|
|
{
|
2023-05-02 20:09:09 +10:00
|
|
|
size_t a = size_t(x) * y;
|
2022-09-13 16:29:06 +10:00
|
|
|
uchar *cp = rect;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-05 15:33:18 +00:00
|
|
|
while (a--) {
|
2013-08-23 08:27:01 +00:00
|
|
|
if (cp[3] == 255) {
|
|
|
|
|
/* pass */
|
|
|
|
|
}
|
|
|
|
|
else if (cp[3] == 0) {
|
2013-01-05 15:33:18 +00:00
|
|
|
cp[0] = backcol[0] * 255;
|
|
|
|
|
cp[1] = backcol[1] * 255;
|
|
|
|
|
cp[2] = backcol[2] * 255;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2013-08-23 08:27:01 +00:00
|
|
|
float alpha = cp[3] / 255.0;
|
|
|
|
|
float mul = 1.0f - alpha;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-08-23 08:27:01 +00:00
|
|
|
cp[0] = (cp[0] * alpha) + mul * backcol[0];
|
|
|
|
|
cp[1] = (cp[1] * alpha) + mul * backcol[1];
|
|
|
|
|
cp[2] = (cp[2] * alpha) + mul * backcol[2];
|
2013-01-05 15:33:18 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-05 15:33:18 +00:00
|
|
|
cp[3] = 255;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-05 15:33:18 +00:00
|
|
|
cp += 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-09 16:27:24 +01:00
|
|
|
|
2020-09-14 16:11:13 +10:00
|
|
|
/** \} */
|