/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup imbuf */ #include #include #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 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 dst, const Span src1, const Span 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(dst_void); const auto *src = static_cast(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(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( 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( 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(dbuf->byte_data_for_write()) + size_t(desty) * dbuf->x + destx; orect = reinterpret_cast(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(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(dr))[0] = (reinterpret_cast(sr))[0]; (reinterpret_cast(dr))[1] = (reinterpret_cast(sr))[1]; (reinterpret_cast(dr))[2] = (reinterpret_cast(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(dr))[3] = (reinterpret_cast(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(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(dr), reinterpret_cast(outr), mask_src, mask / 65535.0f); } else { mask_src[3] = divide_round_i(src[3] * mask, 65535); func(reinterpret_cast(dr), reinterpret_cast(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(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(dr), reinterpret_cast(outr), mask_src, mask / 65535.0f); } else { mask_src[3] = divide_round_i(src[3] * mask, 65535); func(reinterpret_cast(dr), reinterpret_cast(outr), mask_src); } } } } cmaskrect += srcskip; if (texmaskrect) { texmaskrect += srcskip; } } else { /* regular blending */ for (x = width; x > 0; x--, dr++, outr++, sr++) { if ((reinterpret_cast(sr))[3]) { func(reinterpret_cast(dr), reinterpret_cast(outr), reinterpret_cast(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(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(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