Files
workinf_Blender_Wasm/blender-5.2.0/source/blender/imbuf/intern/rectop.cc
2026-08-12 04:47:48 -04:00

1324 lines
38 KiB
C++

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup imbuf
*/
#include <algorithm>
#include <cstdlib>
#include "BLI_math_base.h"
#include "BLI_math_color_blend.h"
#include "BLI_math_vector.h"
#include "BLI_rect.h"
#include "BLI_task.hh"
#include "BLI_utildefines.h"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_colormanagement.hh"
#include "MEM_guardedalloc.h"
#include <cstring>
namespace blender {
void IMB_blend_color_byte(uchar dst[4],
const uchar src1[4],
const uchar src2[4],
IMB_BlendMode mode)
{
switch (mode) {
case IMB_BLEND_MIX:
blend_color_mix_byte(dst, src1, src2);
break;
case IMB_BLEND_ADD:
blend_color_add_byte(dst, src1, src2);
break;
case IMB_BLEND_SUB:
blend_color_sub_byte(dst, src1, src2);
break;
case IMB_BLEND_MUL:
blend_color_mul_byte(dst, src1, src2);
break;
case IMB_BLEND_LIGHTEN:
blend_color_lighten_byte(dst, src1, src2);
break;
case IMB_BLEND_DARKEN:
blend_color_darken_byte(dst, src1, src2);
break;
case IMB_BLEND_ERASE_ALPHA:
blend_color_erase_alpha_byte(dst, src1, src2);
break;
case IMB_BLEND_ADD_ALPHA:
blend_color_add_alpha_byte(dst, src1, src2);
break;
case IMB_BLEND_OVERLAY:
blend_color_overlay_byte(dst, src1, src2);
break;
case IMB_BLEND_HARDLIGHT:
blend_color_hardlight_byte(dst, src1, src2);
break;
case IMB_BLEND_COLORBURN:
blend_color_burn_byte(dst, src1, src2);
break;
case IMB_BLEND_LINEARBURN:
blend_color_linearburn_byte(dst, src1, src2);
break;
case IMB_BLEND_COLORDODGE:
blend_color_dodge_byte(dst, src1, src2);
break;
case IMB_BLEND_SCREEN:
blend_color_screen_byte(dst, src1, src2);
break;
case IMB_BLEND_SOFTLIGHT:
blend_color_softlight_byte(dst, src1, src2);
break;
case IMB_BLEND_PINLIGHT:
blend_color_pinlight_byte(dst, src1, src2);
break;
case IMB_BLEND_LINEARLIGHT:
blend_color_linearlight_byte(dst, src1, src2);
break;
case IMB_BLEND_VIVIDLIGHT:
blend_color_vividlight_byte(dst, src1, src2);
break;
case IMB_BLEND_DIFFERENCE:
blend_color_difference_byte(dst, src1, src2);
break;
case IMB_BLEND_EXCLUSION:
blend_color_exclusion_byte(dst, src1, src2);
break;
case IMB_BLEND_COLOR:
blend_color_color_byte(dst, src1, src2);
break;
case IMB_BLEND_HUE:
blend_color_hue_byte(dst, src1, src2);
break;
case IMB_BLEND_SATURATION:
blend_color_saturation_byte(dst, src1, src2);
break;
case IMB_BLEND_LUMINOSITY:
blend_color_luminosity_byte(dst, src1, src2);
break;
default:
dst[0] = src1[0];
dst[1] = src1[1];
dst[2] = src1[2];
dst[3] = src1[3];
break;
}
}
void IMB_blend_color_float(float dst[4],
const float src1[4],
const float src2[4],
IMB_BlendMode mode)
{
switch (mode) {
case IMB_BLEND_MIX:
blend_color_mix_float(dst, src1, src2);
break;
case IMB_BLEND_ADD:
blend_color_add_float(dst, src1, src2);
break;
case IMB_BLEND_SUB:
blend_color_sub_float(dst, src1, src2);
break;
case IMB_BLEND_MUL:
blend_color_mul_float(dst, src1, src2);
break;
case IMB_BLEND_LIGHTEN:
blend_color_lighten_float(dst, src1, src2);
break;
case IMB_BLEND_DARKEN:
blend_color_darken_float(dst, src1, src2);
break;
case IMB_BLEND_ERASE_ALPHA:
blend_color_erase_alpha_float(dst, src1, src2);
break;
case IMB_BLEND_ADD_ALPHA:
blend_color_add_alpha_float(dst, src1, src2);
break;
case IMB_BLEND_OVERLAY:
blend_color_overlay_float(dst, src1, src2);
break;
case IMB_BLEND_HARDLIGHT:
blend_color_hardlight_float(dst, src1, src2);
break;
case IMB_BLEND_COLORBURN:
blend_color_burn_float(dst, src1, src2);
break;
case IMB_BLEND_LINEARBURN:
blend_color_linearburn_float(dst, src1, src2);
break;
case IMB_BLEND_COLORDODGE:
blend_color_dodge_float(dst, src1, src2);
break;
case IMB_BLEND_SCREEN:
blend_color_screen_float(dst, src1, src2);
break;
case IMB_BLEND_SOFTLIGHT:
blend_color_softlight_float(dst, src1, src2);
break;
case IMB_BLEND_PINLIGHT:
blend_color_pinlight_float(dst, src1, src2);
break;
case IMB_BLEND_LINEARLIGHT:
blend_color_linearlight_float(dst, src1, src2);
break;
case IMB_BLEND_VIVIDLIGHT:
blend_color_vividlight_float(dst, src1, src2);
break;
case IMB_BLEND_DIFFERENCE:
blend_color_difference_float(dst, src1, src2);
break;
case IMB_BLEND_EXCLUSION:
blend_color_exclusion_float(dst, src1, src2);
break;
case IMB_BLEND_COLOR:
blend_color_color_float(dst, src1, src2);
break;
case IMB_BLEND_HUE:
blend_color_hue_float(dst, src1, src2);
break;
case IMB_BLEND_SATURATION:
blend_color_saturation_float(dst, src1, src2);
break;
case IMB_BLEND_LUMINOSITY:
blend_color_luminosity_float(dst, src1, src2);
break;
default:
dst[0] = src1[0];
dst[1] = src1[1];
dst[2] = src1[2];
dst[3] = src1[3];
break;
}
}
void IMB_blend_color_float(const MutableSpan<float4> dst,
const Span<float4> src1,
const Span<float4> src2,
const IMB_BlendMode mode)
{
BLI_assert(dst.size() == src1.size());
BLI_assert(dst.size() == src2.size());
switch (mode) {
case IMB_BLEND_MIX:
for (const int i : dst.index_range()) {
blend_color_mix_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_ADD:
for (const int i : dst.index_range()) {
blend_color_add_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_SUB:
for (const int i : dst.index_range()) {
blend_color_sub_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_MUL:
for (const int i : dst.index_range()) {
blend_color_mul_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_LIGHTEN:
for (const int i : dst.index_range()) {
blend_color_lighten_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_DARKEN:
for (const int i : dst.index_range()) {
blend_color_darken_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_ERASE_ALPHA:
for (const int i : dst.index_range()) {
blend_color_erase_alpha_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_ADD_ALPHA:
for (const int i : dst.index_range()) {
blend_color_add_alpha_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_OVERLAY:
for (const int i : dst.index_range()) {
blend_color_overlay_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_HARDLIGHT:
for (const int i : dst.index_range()) {
blend_color_hardlight_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_COLORBURN:
for (const int i : dst.index_range()) {
blend_color_burn_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_LINEARBURN:
for (const int i : dst.index_range()) {
blend_color_linearburn_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_COLORDODGE:
for (const int i : dst.index_range()) {
blend_color_dodge_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_SCREEN:
for (const int i : dst.index_range()) {
blend_color_screen_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_SOFTLIGHT:
for (const int i : dst.index_range()) {
blend_color_softlight_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_PINLIGHT:
for (const int i : dst.index_range()) {
blend_color_pinlight_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_LINEARLIGHT:
for (const int i : dst.index_range()) {
blend_color_linearlight_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_VIVIDLIGHT:
for (const int i : dst.index_range()) {
blend_color_vividlight_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_DIFFERENCE:
for (const int i : dst.index_range()) {
blend_color_difference_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_EXCLUSION:
for (const int i : dst.index_range()) {
blend_color_exclusion_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_COLOR:
for (const int i : dst.index_range()) {
blend_color_color_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_HUE:
for (const int i : dst.index_range()) {
blend_color_hue_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_SATURATION:
for (const int i : dst.index_range()) {
blend_color_saturation_float(dst[i], src1[i], src2[i]);
}
break;
case IMB_BLEND_LUMINOSITY:
for (const int i : dst.index_range()) {
blend_color_luminosity_float(dst[i], src1[i], src2[i]);
}
break;
default:
for (const int i : dst.index_range()) {
dst[i] = src1[i];
}
break;
}
}
/* -------------------------------------------------------------------- */
/** \name Crop
* \{ */
static void copy_to_cropped_bufffer(void *dst_void,
const int2 &dst_size,
const void *src_void,
const int2 &src_size,
const int stride,
const int2 &src_rect_pos,
const int2 &dst_rect_pos,
const int2 &rect_size)
{
BLI_assert(src_size.x > 0 && src_size.y > 0);
BLI_assert(dst_size.x > 0 && dst_size.y > 0);
BLI_assert(rect_size.x > 0 && rect_size.y > 0);
BLI_assert(src_rect_pos.x >= 0 && src_rect_pos.y >= 0);
BLI_assert(dst_rect_pos.x >= 0 && dst_rect_pos.y >= 0);
BLI_assert((src_rect_pos.x + rect_size.x) <= src_size.x &&
(src_rect_pos.y + rect_size.y) <= src_size.y);
BLI_assert((dst_rect_pos.x + rect_size.x) <= dst_size.x &&
(dst_rect_pos.y + rect_size.y) <= dst_size.y);
auto *dst = static_cast<std::byte *>(dst_void);
const auto *src = static_cast<const std::byte *>(src_void);
for (const int rect_y : IndexRange(rect_size.y)) {
const int src_y = src_rect_pos.y + rect_y;
const int dst_y = dst_rect_pos.y + rect_y;
const std::byte *row_src = src + size_t(src_size.x) * stride * src_y;
std::byte *row_dst = dst + size_t(dst_size.x) * stride * dst_y;
std::copy_n(row_src + size_t(src_rect_pos.x) * stride,
size_t(rect_size.x) * stride,
row_dst + size_t(dst_rect_pos.x) * stride);
}
}
static void *create_cropped_buffer_impl(const void *src_void,
const int2 &src_size,
const int stride,
const int2 &src_rect_pos,
const int2 &rect_size)
{
BLI_assert(rect_size.x > 0 && rect_size.y > 0);
const size_t dst_buffer_size = size_t(rect_size.x) * size_t(rect_size.y);
auto *dst = MEM_new_array_uninitialized<std::byte>(dst_buffer_size * stride, __func__);
copy_to_cropped_bufffer(
dst, rect_size, src_void, src_size, stride, src_rect_pos, int2(0, 0), rect_size);
return dst;
}
static float *create_cropped_buffer(const float *src,
const int2 &src_size,
const int channels,
const int2 &src_rect_pos,
const int2 &rect_size)
{
/* For some reason channels == 0 means 4-channel default. */
const int stride = (channels == 0 ? 4 : channels) * sizeof(float);
return static_cast<float *>(
create_cropped_buffer_impl(src, src_size, stride, src_rect_pos, rect_size));
}
static uchar *create_cropped_buffer(const uchar *src,
const int2 &src_size,
const int2 &src_rect_pos,
const int2 &rect_size)
{
/* Byte buffers always have 4 channels. */
const int stride = 4 * sizeof(uchar);
return static_cast<uchar *>(
create_cropped_buffer_impl(src, src_size, stride, src_rect_pos, rect_size));
}
void IMB_copy_rect(float *dst,
const int2 &dst_size,
const float *src,
const int2 &src_size,
const int channels,
const int2 &src_rect_pos,
const int2 &dst_rect_pos,
const int2 &rect_size)
{
/* For some reason channels == 0 means 4-channel default. */
const int stride = (channels == 0 ? 4 : channels) * sizeof(float);
copy_to_cropped_bufffer(
dst, dst_size, src, src_size, stride, src_rect_pos, dst_rect_pos, rect_size);
}
void IMB_copy_rect(uchar *dst,
const int2 &dst_size,
const uchar *src,
const int2 &src_size,
const int2 &src_rect_pos,
const int2 &dst_rect_pos,
const int2 &rect_size)
{
/* Byte buffers always have 4 channels. */
const int stride = 4 * sizeof(uchar);
copy_to_cropped_bufffer(
dst, dst_size, src, src_size, stride, src_rect_pos, dst_rect_pos, rect_size);
}
void IMB_copy_rect(ImBuf *dst,
const ImBuf *src,
const int2 &src_rect_pos,
const int2 &dst_rect_pos,
const int2 &rect_size)
{
if (src->byte_data() && dst->byte_data()) {
IMB_copy_rect(dst->byte_data_for_write(),
int2(dst->x, dst->y),
src->byte_data(),
int2(src->x, src->y),
src_rect_pos,
dst_rect_pos,
rect_size);
}
if (src->float_data() && dst->float_data()) {
IMB_copy_rect(dst->float_data_for_write(),
int2(dst->x, dst->y),
src->float_data(),
int2(src->x, src->y),
src->channels,
src_rect_pos,
dst_rect_pos,
rect_size);
}
}
void IMB_crop(ImBuf *ibuf, const int2 &rect_pos, const int2 &rect_size)
{
const int2 src_size(ibuf->x, ibuf->y);
if (src_size == rect_size) {
return;
}
const ColorSpace *byte_colorspace = ibuf->byte_buffer.colorspace;
const ColorSpace *float_colorspace = ibuf->float_buffer.colorspace;
if (const uchar *byte_data = ibuf->byte_data()) {
ibuf->assign_byte_data(create_cropped_buffer(byte_data, src_size, rect_pos, rect_size));
ibuf->byte_buffer.colorspace = byte_colorspace;
}
if (const float *float_data = ibuf->float_data()) {
ibuf->assign_float_data(
create_cropped_buffer(float_data, src_size, ibuf->channels, rect_pos, rect_size));
ibuf->float_buffer.colorspace = float_colorspace;
}
ibuf->x = rect_size.x;
ibuf->y = rect_size.y;
}
void IMB_rect_size_set(ImBuf *ibuf, const uint size[2])
{
BLI_assert(size[0] > 0 && size[1] > 0);
if ((size[0] == ibuf->x) && (size[1] == ibuf->y)) {
return;
}
ibuf->x = size[0];
ibuf->y = size[1];
if (ibuf->float_data()) {
IMB_alloc_float_pixels(ibuf, ibuf->channels, false);
}
if (ibuf->byte_data()) {
IMB_alloc_byte_pixels(ibuf, false);
}
}
/** \} */
/* clipping */
void IMB_rectclip(ImBuf *dbuf,
const ImBuf *sbuf,
int *destx,
int *desty,
int *srcx,
int *srcy,
int *width,
int *height)
{
int tmp;
if (dbuf == nullptr) {
return;
}
if (*destx < 0) {
*srcx -= *destx;
*width += *destx;
*destx = 0;
}
if (*srcx < 0) {
*destx -= *srcx;
*width += *srcx;
*srcx = 0;
}
if (*desty < 0) {
*srcy -= *desty;
*height += *desty;
*desty = 0;
}
if (*srcy < 0) {
*desty -= *srcy;
*height += *srcy;
*srcy = 0;
}
tmp = dbuf->x - *destx;
*width = std::min(*width, tmp);
tmp = dbuf->y - *desty;
*height = std::min(*height, tmp);
if (sbuf) {
tmp = sbuf->x - *srcx;
*width = std::min(*width, tmp);
tmp = sbuf->y - *srcy;
*height = std::min(*height, tmp);
}
if ((*height <= 0) || (*width <= 0)) {
*width = 0;
*height = 0;
}
}
static void imb_rectclip3(ImBuf *dbuf,
const ImBuf *obuf,
const ImBuf *sbuf,
int *destx,
int *desty,
int *origx,
int *origy,
int *srcx,
int *srcy,
int *width,
int *height)
{
int tmp;
if (dbuf == nullptr) {
return;
}
if (*destx < 0) {
*srcx -= *destx;
*origx -= *destx;
*width += *destx;
*destx = 0;
}
if (*origx < 0) {
*destx -= *origx;
*srcx -= *origx;
*width += *origx;
*origx = 0;
}
if (*srcx < 0) {
*destx -= *srcx;
*origx -= *srcx;
*width += *srcx;
*srcx = 0;
}
if (*desty < 0) {
*srcy -= *desty;
*origy -= *desty;
*height += *desty;
*desty = 0;
}
if (*origy < 0) {
*desty -= *origy;
*srcy -= *origy;
*height += *origy;
*origy = 0;
}
if (*srcy < 0) {
*desty -= *srcy;
*origy -= *srcy;
*height += *srcy;
*srcy = 0;
}
tmp = dbuf->x - *destx;
*width = std::min(*width, tmp);
tmp = dbuf->y - *desty;
*height = std::min(*height, tmp);
if (obuf) {
tmp = obuf->x - *origx;
*width = std::min(*width, tmp);
tmp = obuf->y - *origy;
*height = std::min(*height, tmp);
}
if (sbuf) {
tmp = sbuf->x - *srcx;
*width = std::min(*width, tmp);
tmp = sbuf->y - *srcy;
*height = std::min(*height, tmp);
}
if ((*height <= 0) || (*width <= 0)) {
*width = 0;
*height = 0;
}
}
using IMB_blend_func = void (*)(uchar *dst, const uchar *src1, const uchar *src2);
using IMB_blend_func_float = void (*)(float *dst, const float *src1, const float *src2);
void IMB_rectblend(ImBuf *dbuf,
const ImBuf *obuf,
const ImBuf *sbuf,
ushort *dmask,
const ushort *curvemask,
const ushort *texmask,
float mask_max,
int destx,
int desty,
int origx,
int origy,
int srcx,
int srcy,
int width,
int height,
IMB_BlendMode mode,
bool accumulate)
{
uint *drect = nullptr;
const uint *orect = nullptr;
const uint *srect = nullptr;
uint *dr;
const uint *outr;
const uint *sr;
float *drectf = nullptr;
const float *orectf = nullptr;
const float *srectf = nullptr;
float *drf;
const float *orf;
const float *srf;
const ushort *cmaskrect = curvemask, *cmr;
ushort *dmaskrect = dmask, *dmr;
const ushort *texmaskrect = texmask, *tmr;
int srcskip, destskip, origskip, x;
IMB_blend_func func = nullptr;
IMB_blend_func_float func_float = nullptr;
if (dbuf == nullptr || obuf == nullptr) {
return;
}
imb_rectclip3(dbuf, obuf, sbuf, &destx, &desty, &origx, &origy, &srcx, &srcy, &width, &height);
if (width == 0 || height == 0) {
return;
}
if (sbuf && sbuf->channels != 4) {
return;
}
if (dbuf->channels != 4) {
return;
}
const bool do_char = (sbuf && sbuf->byte_data() && dbuf->byte_data() && obuf->byte_data());
const bool do_float = (sbuf && sbuf->float_data() && dbuf->float_data() && obuf->float_data());
if (do_char) {
drect = reinterpret_cast<uint *>(dbuf->byte_data_for_write()) + size_t(desty) * dbuf->x +
destx;
orect = reinterpret_cast<const uint *>(obuf->byte_data()) + size_t(origy) * obuf->x + origx;
}
if (do_float) {
drectf = dbuf->float_data_for_write() + (size_t(desty) * dbuf->x + destx) * 4;
orectf = obuf->float_data() + (size_t(origy) * obuf->x + origx) * 4;
}
if (dmaskrect) {
dmaskrect += size_t(origy) * obuf->x + origx;
}
destskip = dbuf->x;
origskip = obuf->x;
if (sbuf) {
if (do_char) {
srect = reinterpret_cast<const uint *>(sbuf->byte_data()) + size_t(srcy) * sbuf->x + srcx;
}
if (do_float) {
srectf = sbuf->float_data() + (size_t(srcy) * sbuf->x + srcx) * 4;
}
srcskip = sbuf->x;
if (cmaskrect) {
cmaskrect += size_t(srcy) * sbuf->x + srcx;
}
if (texmaskrect) {
texmaskrect += size_t(srcy) * sbuf->x + srcx;
}
}
else {
srect = drect;
srectf = drectf;
srcskip = destskip;
}
if (mode == IMB_BLEND_COPY_RGB) {
/* copy rgb only */
for (; height > 0; height--) {
if (do_char) {
dr = drect;
sr = srect;
for (x = width; x > 0; x--, dr++, sr++) {
(reinterpret_cast<char *>(dr))[0] = (reinterpret_cast<const char *>(sr))[0];
(reinterpret_cast<char *>(dr))[1] = (reinterpret_cast<const char *>(sr))[1];
(reinterpret_cast<char *>(dr))[2] = (reinterpret_cast<const char *>(sr))[2];
}
drect += destskip;
srect += srcskip;
}
if (do_float) {
drf = drectf;
srf = srectf;
for (x = width; x > 0; x--, drf += 4, srf += 4) {
float map_alpha = (srf[3] == 0.0f) ? drf[3] : drf[3] / srf[3];
drf[0] = srf[0] * map_alpha;
drf[1] = srf[1] * map_alpha;
drf[2] = srf[2] * map_alpha;
}
drectf += destskip * 4;
srectf += srcskip * 4;
}
}
}
else if (mode == IMB_BLEND_COPY_ALPHA) {
/* copy alpha only */
for (; height > 0; height--) {
if (do_char) {
dr = drect;
sr = srect;
for (x = width; x > 0; x--, dr++, sr++) {
(reinterpret_cast<char *>(dr))[3] = (reinterpret_cast<const char *>(sr))[3];
}
drect += destskip;
srect += srcskip;
}
if (do_float) {
drf = drectf;
srf = srectf;
for (x = width; x > 0; x--, drf += 4, srf += 4) {
drf[3] = srf[3];
}
drectf += destskip * 4;
srectf += srcskip * 4;
}
}
}
else {
switch (mode) {
case IMB_BLEND_MIX:
case IMB_BLEND_INTERPOLATE:
func = blend_color_mix_byte;
func_float = blend_color_mix_float;
break;
case IMB_BLEND_ADD:
func = blend_color_add_byte;
func_float = blend_color_add_float;
break;
case IMB_BLEND_SUB:
func = blend_color_sub_byte;
func_float = blend_color_sub_float;
break;
case IMB_BLEND_MUL:
func = blend_color_mul_byte;
func_float = blend_color_mul_float;
break;
case IMB_BLEND_LIGHTEN:
func = blend_color_lighten_byte;
func_float = blend_color_lighten_float;
break;
case IMB_BLEND_DARKEN:
func = blend_color_darken_byte;
func_float = blend_color_darken_float;
break;
case IMB_BLEND_ERASE_ALPHA:
func = blend_color_erase_alpha_byte;
func_float = blend_color_erase_alpha_float;
break;
case IMB_BLEND_ADD_ALPHA:
func = blend_color_add_alpha_byte;
func_float = blend_color_add_alpha_float;
break;
case IMB_BLEND_OVERLAY:
func = blend_color_overlay_byte;
func_float = blend_color_overlay_float;
break;
case IMB_BLEND_HARDLIGHT:
func = blend_color_hardlight_byte;
func_float = blend_color_hardlight_float;
break;
case IMB_BLEND_COLORBURN:
func = blend_color_burn_byte;
func_float = blend_color_burn_float;
break;
case IMB_BLEND_LINEARBURN:
func = blend_color_linearburn_byte;
func_float = blend_color_linearburn_float;
break;
case IMB_BLEND_COLORDODGE:
func = blend_color_dodge_byte;
func_float = blend_color_dodge_float;
break;
case IMB_BLEND_SCREEN:
func = blend_color_screen_byte;
func_float = blend_color_screen_float;
break;
case IMB_BLEND_SOFTLIGHT:
func = blend_color_softlight_byte;
func_float = blend_color_softlight_float;
break;
case IMB_BLEND_PINLIGHT:
func = blend_color_pinlight_byte;
func_float = blend_color_pinlight_float;
break;
case IMB_BLEND_LINEARLIGHT:
func = blend_color_linearlight_byte;
func_float = blend_color_linearlight_float;
break;
case IMB_BLEND_VIVIDLIGHT:
func = blend_color_vividlight_byte;
func_float = blend_color_vividlight_float;
break;
case IMB_BLEND_DIFFERENCE:
func = blend_color_difference_byte;
func_float = blend_color_difference_float;
break;
case IMB_BLEND_EXCLUSION:
func = blend_color_exclusion_byte;
func_float = blend_color_exclusion_float;
break;
case IMB_BLEND_COLOR:
func = blend_color_color_byte;
func_float = blend_color_color_float;
break;
case IMB_BLEND_HUE:
func = blend_color_hue_byte;
func_float = blend_color_hue_float;
break;
case IMB_BLEND_SATURATION:
func = blend_color_saturation_byte;
func_float = blend_color_saturation_float;
break;
case IMB_BLEND_LUMINOSITY:
func = blend_color_luminosity_byte;
func_float = blend_color_luminosity_float;
break;
default:
break;
}
/* blend */
for (; height > 0; height--) {
if (do_char) {
dr = drect;
outr = orect;
sr = srect;
if (cmaskrect) {
/* mask accumulation for painting */
cmr = cmaskrect;
tmr = texmaskrect;
/* destination mask present, do max alpha masking */
if (dmaskrect) {
dmr = dmaskrect;
for (x = width; x > 0; x--, dr++, outr++, sr++, dmr++, cmr++) {
const uchar *src = reinterpret_cast<const uchar *>(sr);
float mask_lim = mask_max * (*cmr);
if (texmaskrect) {
mask_lim *= ((*tmr++) / 65535.0f);
}
if (src[3] && mask_lim) {
float mask;
if (accumulate) {
mask = *dmr + mask_lim;
}
else {
mask = *dmr + mask_lim - (*dmr * (*cmr / 65535.0f));
}
mask = min_ff(mask, 65535.0);
if (mask > *dmr) {
uchar mask_src[4];
*dmr = mask;
mask_src[0] = src[0];
mask_src[1] = src[1];
mask_src[2] = src[2];
if (mode == IMB_BLEND_INTERPOLATE) {
mask_src[3] = src[3];
blend_color_interpolate_byte(reinterpret_cast<uchar *>(dr),
reinterpret_cast<const uchar *>(outr),
mask_src,
mask / 65535.0f);
}
else {
mask_src[3] = divide_round_i(src[3] * mask, 65535);
func(reinterpret_cast<uchar *>(dr),
reinterpret_cast<const uchar *>(outr),
mask_src);
}
}
}
}
dmaskrect += origskip;
}
/* No destination mask buffer, do regular blend with mask-texture if present. */
else {
for (x = width; x > 0; x--, dr++, outr++, sr++, cmr++) {
const uchar *src = reinterpret_cast<const uchar *>(sr);
float mask = mask_max * float(*cmr);
if (texmaskrect) {
mask *= (float(*tmr++) / 65535.0f);
}
mask = min_ff(mask, 65535.0);
if (src[3] && (mask > 0.0f)) {
uchar mask_src[4];
mask_src[0] = src[0];
mask_src[1] = src[1];
mask_src[2] = src[2];
if (mode == IMB_BLEND_INTERPOLATE) {
mask_src[3] = src[3];
blend_color_interpolate_byte(reinterpret_cast<uchar *>(dr),
reinterpret_cast<const uchar *>(outr),
mask_src,
mask / 65535.0f);
}
else {
mask_src[3] = divide_round_i(src[3] * mask, 65535);
func(reinterpret_cast<uchar *>(dr),
reinterpret_cast<const uchar *>(outr),
mask_src);
}
}
}
}
cmaskrect += srcskip;
if (texmaskrect) {
texmaskrect += srcskip;
}
}
else {
/* regular blending */
for (x = width; x > 0; x--, dr++, outr++, sr++) {
if ((reinterpret_cast<const uchar *>(sr))[3]) {
func(reinterpret_cast<uchar *>(dr),
reinterpret_cast<const uchar *>(outr),
reinterpret_cast<const uchar *>(sr));
}
}
}
drect += destskip;
orect += origskip;
srect += srcskip;
}
if (do_float) {
drf = drectf;
orf = orectf;
srf = srectf;
if (cmaskrect) {
/* mask accumulation for painting */
cmr = cmaskrect;
tmr = texmaskrect;
/* destination mask present, do max alpha masking */
if (dmaskrect) {
dmr = dmaskrect;
for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, dmr++, cmr++) {
float mask_lim = mask_max * (*cmr);
if (texmaskrect) {
mask_lim *= ((*tmr++) / 65535.0f);
}
if (srf[3] && mask_lim) {
float mask;
if (accumulate) {
mask = min_ff(*dmr + mask_lim, 65535.0);
}
else {
mask = *dmr + mask_lim - (*dmr * (*cmr / 65535.0f));
}
mask = min_ff(mask, 65535.0);
if (mask > *dmr) {
*dmr = mask;
if (mode == IMB_BLEND_INTERPOLATE) {
blend_color_interpolate_float(drf, orf, srf, mask / 65535.0f);
}
else {
float mask_srf[4];
mul_v4_v4fl(mask_srf, srf, mask / 65535.0f);
func_float(drf, orf, mask_srf);
}
}
}
}
dmaskrect += origskip;
}
/* No destination mask buffer, do regular blend with mask-texture if present. */
else {
for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, cmr++) {
float mask = mask_max * float(*cmr);
if (texmaskrect) {
mask *= (float(*tmr++) / 65535.0f);
}
mask = min_ff(mask, 65535.0);
if (srf[3] && (mask > 0.0f)) {
if (mode == IMB_BLEND_INTERPOLATE) {
blend_color_interpolate_float(drf, orf, srf, mask / 65535.0f);
}
else {
float mask_srf[4];
mul_v4_v4fl(mask_srf, srf, mask / 65535.0f);
func_float(drf, orf, mask_srf);
}
}
}
}
cmaskrect += srcskip;
if (texmaskrect) {
texmaskrect += srcskip;
}
}
else {
/* regular blending */
for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4) {
if (srf[3] != 0) {
func_float(drf, orf, srf);
}
}
}
drectf += destskip * 4;
orectf += origskip * 4;
srectf += srcskip * 4;
}
}
}
}
void IMB_rectblend_threaded(ImBuf *dbuf,
const ImBuf *obuf,
const ImBuf *sbuf,
ushort *dmask,
const ushort *curvemask,
const ushort *texmask,
float mask_max,
int destx,
int desty,
int origx,
int origy,
int srcx,
int srcy,
int width,
int height,
IMB_BlendMode mode,
bool accumulate)
{
threading::parallel_for(IndexRange(height), 16, [&](const IndexRange y_range) {
IMB_rectblend(dbuf,
obuf,
sbuf,
dmask,
curvemask,
texmask,
mask_max,
destx,
desty + y_range.first(),
origx,
origy + y_range.first(),
srcx,
srcy + y_range.first(),
width,
y_range.size(),
mode,
accumulate);
});
}
void IMB_rectfill(ImBuf *drect, const float col[4])
{
size_t num;
if (drect->byte_data()) {
uint *rrect = reinterpret_cast<uint *>(drect->byte_data_for_write());
char ccol[4];
unit_float_to_uchar_clamp_v4(ccol, col);
num = IMB_get_pixel_count(drect);
for (; num > 0; num--) {
*rrect++ = *(reinterpret_cast<uint *>(ccol));
}
}
if (drect->float_data()) {
float *rrectf = drect->float_data_for_write();
num = IMB_get_pixel_count(drect);
for (; num > 0; num--) {
*rrectf++ = col[0];
*rrectf++ = col[1];
*rrectf++ = col[2];
*rrectf++ = col[3];
}
}
}
void IMB_rectfill_area(
ImBuf *ibuf, const float scene_linear_color[4], int x1, int y1, int x2, int y2)
{
if (!ibuf) {
return;
}
uchar *rect = ibuf->byte_data_for_write();
float *rectf = ibuf->float_data_for_write();
const int width = ibuf->x;
const int height = ibuf->y;
if ((!rect && !rectf) || scene_linear_color[3] == 0.0f) {
return;
}
/* sanity checks for coords */
CLAMP(x1, 0, width);
CLAMP(x2, 0, width);
CLAMP(y1, 0, height);
CLAMP(y2, 0, height);
if (x1 > x2) {
std::swap(x1, x2);
}
if (y1 > y2) {
std::swap(y1, y2);
}
if (x1 == x2 || y1 == y2) {
return;
}
const int x_span = x2 - x1;
const int y_span = y2 - y1;
/* Alpha. */
const float a = scene_linear_color[3];
/* Alpha inverted. */
const float ai = 1 - a;
/* Alpha, inverted, ai/255.0 - Convert char to float at the same time. */
const float aich = ai / 255.0f;
if (rect) {
uchar *pixel;
uchar chr = 0, chg = 0, chb = 0;
float fr = 0, fg = 0, fb = 0;
const int alphaint = unit_float_to_uchar_clamp(a);
float col[3];
copy_v3_v3(col, scene_linear_color);
if (ibuf->byte_buffer.colorspace) {
IMB_colormanagement_scene_linear_to_colorspace_v3(col, ibuf->byte_buffer.colorspace);
}
else {
IMB_colormanagement_scene_linear_to_srgb_v3(col, scene_linear_color);
}
if (a == 1.0f) {
chr = unit_float_to_uchar_clamp(col[0]);
chg = unit_float_to_uchar_clamp(col[1]);
chb = unit_float_to_uchar_clamp(col[2]);
}
else {
fr = col[0] * a;
fg = col[1] * a;
fb = col[2] * a;
}
for (int j = 0; j < y_span; j++) {
pixel = rect + (4 * (((size_t(y1) + size_t(j)) * size_t(width)) + size_t(x1)));
for (int i = 0; i < x_span; i++) {
BLI_assert(pixel >= rect && pixel < rect + (4 * (size_t(width) * size_t(height))));
if (a == 1.0f) {
pixel[0] = chr;
pixel[1] = chg;
pixel[2] = chb;
pixel[3] = 255;
}
else {
int alphatest;
pixel[0] = char((fr + (float(pixel[0]) * aich)) * 255.0f);
pixel[1] = char((fg + (float(pixel[1]) * aich)) * 255.0f);
pixel[2] = char((fb + (float(pixel[2]) * aich)) * 255.0f);
pixel[3] = char((alphatest = (int(pixel[3]) + alphaint)) < 255 ? alphatest : 255);
}
pixel += 4;
}
}
}
if (rectf) {
float *pixel;
for (int j = 0; j < y_span; j++) {
pixel = rectf + (4 * (((size_t(y1) + j) * size_t(width)) + size_t(x1)));
for (int i = 0; i < x_span; i++) {
BLI_assert(pixel >= rectf && pixel < rectf + (4 * (size_t(width) * size_t(height))));
if (a == 1.0f) {
pixel[0] = scene_linear_color[0];
pixel[1] = scene_linear_color[1];
pixel[2] = scene_linear_color[2];
pixel[3] = 1.0f;
}
else {
float alphatest;
pixel[0] = (scene_linear_color[0] * a) + (pixel[0] * ai);
pixel[1] = (scene_linear_color[1] * a) + (pixel[1] * ai);
pixel[2] = (scene_linear_color[2] * a) + (pixel[2] * ai);
pixel[3] = (alphatest = (pixel[3] + a)) < 1.0f ? alphatest : 1.0f;
}
pixel += 4;
}
}
}
}
void IMB_rectfill_alpha(ImBuf *ibuf, const float value)
{
size_t i;
if (ibuf->float_data() && (ibuf->channels == 4)) {
float *fbuf = ibuf->float_data_for_write() + 3;
for (i = IMB_get_pixel_count(ibuf); i > 0; i--, fbuf += 4) {
*fbuf = value;
}
}
if (uchar *byte_data = ibuf->byte_data_for_write()) {
const uchar cvalue = value * 255;
uchar *cbuf = byte_data + 3;
for (i = IMB_get_pixel_count(ibuf); i > 0; i--, cbuf += 4) {
*cbuf = cvalue;
}
}
}
} // namespace blender