736 lines
18 KiB
C++
736 lines
18 KiB
C++
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup imbuf
|
|
*/
|
|
|
|
#include <cmath>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_math_base.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "IMB_filter.h"
|
|
#include "IMB_imbuf.h"
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
#include "imbuf.h"
|
|
|
|
static void filtrow(uchar *point, int x)
|
|
{
|
|
uint c1, c2, c3, error;
|
|
|
|
if (x > 1) {
|
|
c1 = c2 = *point;
|
|
error = 2;
|
|
for (x--; x > 0; x--) {
|
|
c3 = point[4];
|
|
c1 += (c2 << 1) + c3 + error;
|
|
error = c1 & 3;
|
|
*point = c1 >> 2;
|
|
point += 4;
|
|
c1 = c2;
|
|
c2 = c3;
|
|
}
|
|
*point = (c1 + (c2 << 1) + c2 + error) >> 2;
|
|
}
|
|
}
|
|
|
|
static void filtrowf(float *point, int x)
|
|
{
|
|
float c1, c2, c3;
|
|
|
|
if (x > 1) {
|
|
c1 = c2 = *point;
|
|
for (x--; x > 0; x--) {
|
|
c3 = point[4];
|
|
c1 += (c2 * 2) + c3;
|
|
*point = 0.25f * c1;
|
|
point += 4;
|
|
c1 = c2;
|
|
c2 = c3;
|
|
}
|
|
*point = 0.25f * (c1 + (c2 * 2) + c2);
|
|
}
|
|
}
|
|
|
|
static void filtcolum(uchar *point, int y, int skip)
|
|
{
|
|
uint c1, c2, c3, error;
|
|
uchar *point2;
|
|
|
|
if (y > 1) {
|
|
c1 = c2 = *point;
|
|
point2 = point;
|
|
error = 2;
|
|
for (y--; y > 0; y--) {
|
|
point2 += skip;
|
|
c3 = *point2;
|
|
c1 += (c2 << 1) + c3 + error;
|
|
error = c1 & 3;
|
|
*point = c1 >> 2;
|
|
point = point2;
|
|
c1 = c2;
|
|
c2 = c3;
|
|
}
|
|
*point = (c1 + (c2 << 1) + c2 + error) >> 2;
|
|
}
|
|
}
|
|
|
|
static void filtcolumf(float *point, int y, int skip)
|
|
{
|
|
float c1, c2, c3, *point2;
|
|
|
|
if (y > 1) {
|
|
c1 = c2 = *point;
|
|
point2 = point;
|
|
for (y--; y > 0; y--) {
|
|
point2 += skip;
|
|
c3 = *point2;
|
|
c1 += (c2 * 2) + c3;
|
|
*point = 0.25f * c1;
|
|
point = point2;
|
|
c1 = c2;
|
|
c2 = c3;
|
|
}
|
|
*point = 0.25f * (c1 + (c2 * 2) + c2);
|
|
}
|
|
}
|
|
|
|
void IMB_filtery(ImBuf *ibuf)
|
|
{
|
|
uchar *point = ibuf->byte_buffer.data;
|
|
float *pointf = ibuf->float_buffer.data;
|
|
|
|
int x = ibuf->x;
|
|
int y = ibuf->y;
|
|
int skip = x << 2;
|
|
|
|
for (; x > 0; x--) {
|
|
if (point) {
|
|
if (ibuf->planes > 24) {
|
|
filtcolum(point, y, skip);
|
|
}
|
|
point++;
|
|
filtcolum(point, y, skip);
|
|
point++;
|
|
filtcolum(point, y, skip);
|
|
point++;
|
|
filtcolum(point, y, skip);
|
|
point++;
|
|
}
|
|
if (pointf) {
|
|
if (ibuf->planes > 24) {
|
|
filtcolumf(pointf, y, skip);
|
|
}
|
|
pointf++;
|
|
filtcolumf(pointf, y, skip);
|
|
pointf++;
|
|
filtcolumf(pointf, y, skip);
|
|
pointf++;
|
|
filtcolumf(pointf, y, skip);
|
|
pointf++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void imb_filterx(ImBuf *ibuf)
|
|
{
|
|
uchar *point = ibuf->byte_buffer.data;
|
|
float *pointf = ibuf->float_buffer.data;
|
|
|
|
int x = ibuf->x;
|
|
int y = ibuf->y;
|
|
int skip = (x << 2) - 3;
|
|
|
|
for (; y > 0; y--) {
|
|
if (point) {
|
|
if (ibuf->planes > 24) {
|
|
filtrow(point, x);
|
|
}
|
|
point++;
|
|
filtrow(point, x);
|
|
point++;
|
|
filtrow(point, x);
|
|
point++;
|
|
filtrow(point, x);
|
|
point += skip;
|
|
}
|
|
if (pointf) {
|
|
if (ibuf->planes > 24) {
|
|
filtrowf(pointf, x);
|
|
}
|
|
pointf++;
|
|
filtrowf(pointf, x);
|
|
pointf++;
|
|
filtrowf(pointf, x);
|
|
pointf++;
|
|
filtrowf(pointf, x);
|
|
pointf += skip;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void imb_filterN(ImBuf *out, ImBuf *in)
|
|
{
|
|
BLI_assert(out->channels == in->channels);
|
|
BLI_assert(out->x == in->x && out->y == in->y);
|
|
|
|
const int channels = in->channels;
|
|
const int rowlen = in->x;
|
|
|
|
if (in->byte_buffer.data && out->byte_buffer.data) {
|
|
for (int y = 0; y < in->y; y++) {
|
|
/* setup rows */
|
|
const char *row2 = (const char *)in->byte_buffer.data + y * channels * rowlen;
|
|
const char *row1 = (y == 0) ? row2 : row2 - channels * rowlen;
|
|
const char *row3 = (y == in->y - 1) ? row2 : row2 + channels * rowlen;
|
|
|
|
char *cp = (char *)out->byte_buffer.data + y * channels * rowlen;
|
|
|
|
for (int x = 0; x < rowlen; x++) {
|
|
const char *r11, *r13, *r21, *r23, *r31, *r33;
|
|
|
|
if (x == 0) {
|
|
r11 = row1;
|
|
r21 = row2;
|
|
r31 = row3;
|
|
}
|
|
else {
|
|
r11 = row1 - channels;
|
|
r21 = row2 - channels;
|
|
r31 = row3 - channels;
|
|
}
|
|
|
|
if (x == rowlen - 1) {
|
|
r13 = row1;
|
|
r23 = row2;
|
|
r33 = row3;
|
|
}
|
|
else {
|
|
r13 = row1 + channels;
|
|
r23 = row2 + channels;
|
|
r33 = row3 + channels;
|
|
}
|
|
|
|
cp[0] = (r11[0] + 2 * row1[0] + r13[0] + 2 * r21[0] + 4 * row2[0] + 2 * r23[0] + r31[0] +
|
|
2 * row3[0] + r33[0]) >>
|
|
4;
|
|
cp[1] = (r11[1] + 2 * row1[1] + r13[1] + 2 * r21[1] + 4 * row2[1] + 2 * r23[1] + r31[1] +
|
|
2 * row3[1] + r33[1]) >>
|
|
4;
|
|
cp[2] = (r11[2] + 2 * row1[2] + r13[2] + 2 * r21[2] + 4 * row2[2] + 2 * r23[2] + r31[2] +
|
|
2 * row3[2] + r33[2]) >>
|
|
4;
|
|
cp[3] = (r11[3] + 2 * row1[3] + r13[3] + 2 * r21[3] + 4 * row2[3] + 2 * r23[3] + r31[3] +
|
|
2 * row3[3] + r33[3]) >>
|
|
4;
|
|
cp += channels;
|
|
row1 += channels;
|
|
row2 += channels;
|
|
row3 += channels;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in->float_buffer.data && out->float_buffer.data) {
|
|
for (int y = 0; y < in->y; y++) {
|
|
/* setup rows */
|
|
const float *row2 = (const float *)in->float_buffer.data + y * channels * rowlen;
|
|
const float *row1 = (y == 0) ? row2 : row2 - channels * rowlen;
|
|
const float *row3 = (y == in->y - 1) ? row2 : row2 + channels * rowlen;
|
|
|
|
float *cp = (float *)out->float_buffer.data + y * channels * rowlen;
|
|
|
|
for (int x = 0; x < rowlen; x++) {
|
|
const float *r11, *r13, *r21, *r23, *r31, *r33;
|
|
|
|
if (x == 0) {
|
|
r11 = row1;
|
|
r21 = row2;
|
|
r31 = row3;
|
|
}
|
|
else {
|
|
r11 = row1 - channels;
|
|
r21 = row2 - channels;
|
|
r31 = row3 - channels;
|
|
}
|
|
|
|
if (x == rowlen - 1) {
|
|
r13 = row1;
|
|
r23 = row2;
|
|
r33 = row3;
|
|
}
|
|
else {
|
|
r13 = row1 + channels;
|
|
r23 = row2 + channels;
|
|
r33 = row3 + channels;
|
|
}
|
|
|
|
cp[0] = (r11[0] + 2 * row1[0] + r13[0] + 2 * r21[0] + 4 * row2[0] + 2 * r23[0] + r31[0] +
|
|
2 * row3[0] + r33[0]) *
|
|
(1.0f / 16.0f);
|
|
cp[1] = (r11[1] + 2 * row1[1] + r13[1] + 2 * r21[1] + 4 * row2[1] + 2 * r23[1] + r31[1] +
|
|
2 * row3[1] + r33[1]) *
|
|
(1.0f / 16.0f);
|
|
cp[2] = (r11[2] + 2 * row1[2] + r13[2] + 2 * r21[2] + 4 * row2[2] + 2 * r23[2] + r31[2] +
|
|
2 * row3[2] + r33[2]) *
|
|
(1.0f / 16.0f);
|
|
cp[3] = (r11[3] + 2 * row1[3] + r13[3] + 2 * r21[3] + 4 * row2[3] + 2 * r23[3] + r31[3] +
|
|
2 * row3[3] + r33[3]) *
|
|
(1.0f / 16.0f);
|
|
cp += channels;
|
|
row1 += channels;
|
|
row2 += channels;
|
|
row3 += channels;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IMB_filter(ImBuf *ibuf)
|
|
{
|
|
IMB_filtery(ibuf);
|
|
imb_filterx(ibuf);
|
|
}
|
|
|
|
void IMB_mask_filter_extend(char *mask, int width, int height)
|
|
{
|
|
const char *row1, *row2, *row3;
|
|
int rowlen, x, y;
|
|
char *temprect;
|
|
|
|
rowlen = width;
|
|
|
|
/* make a copy, to prevent flooding */
|
|
temprect = static_cast<char *>(MEM_dupallocN(mask));
|
|
|
|
for (y = 1; y <= height; y++) {
|
|
/* setup rows */
|
|
row1 = (char *)(temprect + (y - 2) * rowlen);
|
|
row2 = row1 + rowlen;
|
|
row3 = row2 + rowlen;
|
|
if (y == 1) {
|
|
row1 = row2;
|
|
}
|
|
else if (y == height) {
|
|
row3 = row2;
|
|
}
|
|
|
|
for (x = 0; x < rowlen; x++) {
|
|
if (mask[((y - 1) * rowlen) + x] == 0) {
|
|
if (*row1 || *row2 || *row3 || *(row1 + 1) || *(row3 + 1)) {
|
|
mask[((y - 1) * rowlen) + x] = FILTER_MASK_MARGIN;
|
|
}
|
|
else if ((x != rowlen - 1) && (*(row1 + 2) || *(row2 + 2) || *(row3 + 2))) {
|
|
mask[((y - 1) * rowlen) + x] = FILTER_MASK_MARGIN;
|
|
}
|
|
}
|
|
|
|
if (x != 0) {
|
|
row1++;
|
|
row2++;
|
|
row3++;
|
|
}
|
|
}
|
|
}
|
|
|
|
MEM_freeN(temprect);
|
|
}
|
|
|
|
void IMB_mask_clear(ImBuf *ibuf, const char *mask, int val)
|
|
{
|
|
int x, y;
|
|
if (ibuf->float_buffer.data) {
|
|
for (x = 0; x < ibuf->x; x++) {
|
|
for (y = 0; y < ibuf->y; y++) {
|
|
if (mask[ibuf->x * y + x] == val) {
|
|
float *col = ibuf->float_buffer.data + 4 * (ibuf->x * y + x);
|
|
col[0] = col[1] = col[2] = col[3] = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* char buffer */
|
|
for (x = 0; x < ibuf->x; x++) {
|
|
for (y = 0; y < ibuf->y; y++) {
|
|
if (mask[ibuf->x * y + x] == val) {
|
|
char *col = (char *)(ibuf->byte_buffer.data + 4 * ibuf->x * y + x);
|
|
col[0] = col[1] = col[2] = col[3] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int filter_make_index(const int x, const int y, const int w, const int h)
|
|
{
|
|
if (x < 0 || x >= w || y < 0 || y >= h) {
|
|
return -1; /* return bad index */
|
|
}
|
|
|
|
return y * w + x;
|
|
}
|
|
|
|
static int check_pixel_assigned(
|
|
const void *buffer, const char *mask, const int index, const int depth, const bool is_float)
|
|
{
|
|
int res = 0;
|
|
|
|
if (index >= 0) {
|
|
const int alpha_index = depth * index + (depth - 1);
|
|
|
|
if (mask != nullptr) {
|
|
res = mask[index] != 0 ? 1 : 0;
|
|
}
|
|
else if ((is_float && ((const float *)buffer)[alpha_index] != 0.0f) ||
|
|
(!is_float && ((const uchar *)buffer)[alpha_index] != 0))
|
|
{
|
|
res = 1;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void IMB_filter_extend(ImBuf *ibuf, char *mask, int filter)
|
|
{
|
|
const int width = ibuf->x;
|
|
const int height = ibuf->y;
|
|
const int depth = 4; /* always 4 channels */
|
|
const int chsize = ibuf->float_buffer.data ? sizeof(float) : sizeof(uchar);
|
|
const size_t bsize = size_t(width) * height * depth * chsize;
|
|
const bool is_float = (ibuf->float_buffer.data != nullptr);
|
|
void *dstbuf = MEM_dupallocN(ibuf->float_buffer.data ? (void *)ibuf->float_buffer.data :
|
|
(void *)ibuf->byte_buffer.data);
|
|
char *dstmask = mask == nullptr ? nullptr : (char *)MEM_dupallocN(mask);
|
|
void *srcbuf = ibuf->float_buffer.data ? (void *)ibuf->float_buffer.data :
|
|
(void *)ibuf->byte_buffer.data;
|
|
char *srcmask = mask;
|
|
int cannot_early_out = 1, r, n, k, i, j, c;
|
|
float weight[25];
|
|
|
|
/* build a weights buffer */
|
|
n = 1;
|
|
|
|
#if 0
|
|
k = 0;
|
|
for (i = -n; i <= n; i++) {
|
|
for (j = -n; j <= n; j++) {
|
|
weight[k++] = sqrt(float(i) * i + j * j);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
weight[0] = 1;
|
|
weight[1] = 2;
|
|
weight[2] = 1;
|
|
weight[3] = 2;
|
|
weight[4] = 0;
|
|
weight[5] = 2;
|
|
weight[6] = 1;
|
|
weight[7] = 2;
|
|
weight[8] = 1;
|
|
|
|
/* run passes */
|
|
for (r = 0; cannot_early_out == 1 && r < filter; r++) {
|
|
int x, y;
|
|
cannot_early_out = 0;
|
|
|
|
for (y = 0; y < height; y++) {
|
|
for (x = 0; x < width; x++) {
|
|
const int index = filter_make_index(x, y, width, height);
|
|
|
|
/* only update unassigned pixels */
|
|
if (!check_pixel_assigned(srcbuf, srcmask, index, depth, is_float)) {
|
|
float tmp[4];
|
|
float wsum = 0;
|
|
float acc[4] = {0, 0, 0, 0};
|
|
k = 0;
|
|
|
|
if (check_pixel_assigned(
|
|
srcbuf, srcmask, filter_make_index(x - 1, y, width, height), depth, is_float) ||
|
|
check_pixel_assigned(
|
|
srcbuf, srcmask, filter_make_index(x + 1, y, width, height), depth, is_float) ||
|
|
check_pixel_assigned(
|
|
srcbuf, srcmask, filter_make_index(x, y - 1, width, height), depth, is_float) ||
|
|
check_pixel_assigned(
|
|
srcbuf, srcmask, filter_make_index(x, y + 1, width, height), depth, is_float))
|
|
{
|
|
for (i = -n; i <= n; i++) {
|
|
for (j = -n; j <= n; j++) {
|
|
if (i != 0 || j != 0) {
|
|
const int tmpindex = filter_make_index(x + i, y + j, width, height);
|
|
|
|
if (check_pixel_assigned(srcbuf, srcmask, tmpindex, depth, is_float)) {
|
|
if (is_float) {
|
|
for (c = 0; c < depth; c++) {
|
|
tmp[c] = ((const float *)srcbuf)[depth * tmpindex + c];
|
|
}
|
|
}
|
|
else {
|
|
for (c = 0; c < depth; c++) {
|
|
tmp[c] = float(((const uchar *)srcbuf)[depth * tmpindex + c]);
|
|
}
|
|
}
|
|
|
|
wsum += weight[k];
|
|
|
|
for (c = 0; c < depth; c++) {
|
|
acc[c] += weight[k] * tmp[c];
|
|
}
|
|
}
|
|
}
|
|
k++;
|
|
}
|
|
}
|
|
|
|
if (wsum != 0) {
|
|
for (c = 0; c < depth; c++) {
|
|
acc[c] /= wsum;
|
|
}
|
|
|
|
if (is_float) {
|
|
for (c = 0; c < depth; c++) {
|
|
((float *)dstbuf)[depth * index + c] = acc[c];
|
|
}
|
|
}
|
|
else {
|
|
for (c = 0; c < depth; c++) {
|
|
((uchar *)dstbuf)[depth * index + c] = acc[c] > 255 ?
|
|
255 :
|
|
(acc[c] < 0 ? 0 :
|
|
uchar(roundf(acc[c])));
|
|
}
|
|
}
|
|
|
|
if (dstmask != nullptr) {
|
|
dstmask[index] = FILTER_MASK_MARGIN; /* assigned */
|
|
}
|
|
cannot_early_out = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* keep the original buffer up to date. */
|
|
memcpy(srcbuf, dstbuf, bsize);
|
|
if (dstmask != nullptr) {
|
|
memcpy(srcmask, dstmask, size_t(width) * height);
|
|
}
|
|
}
|
|
|
|
/* free memory */
|
|
MEM_freeN(dstbuf);
|
|
if (dstmask != nullptr) {
|
|
MEM_freeN(dstmask);
|
|
}
|
|
}
|
|
|
|
void IMB_remakemipmap(ImBuf *ibuf, int use_filter)
|
|
{
|
|
ImBuf *hbuf = ibuf;
|
|
int curmap = 0;
|
|
|
|
ibuf->miptot = 1;
|
|
|
|
while (curmap < IMB_MIPMAP_LEVELS) {
|
|
|
|
if (ibuf->mipmap[curmap]) {
|
|
|
|
if (use_filter) {
|
|
ImBuf *nbuf = IMB_allocImBuf(hbuf->x, hbuf->y, hbuf->planes, hbuf->flags);
|
|
imb_filterN(nbuf, hbuf);
|
|
imb_onehalf_no_alloc(ibuf->mipmap[curmap], nbuf);
|
|
IMB_freeImBuf(nbuf);
|
|
}
|
|
else {
|
|
imb_onehalf_no_alloc(ibuf->mipmap[curmap], hbuf);
|
|
}
|
|
}
|
|
|
|
ibuf->miptot = curmap + 2;
|
|
hbuf = ibuf->mipmap[curmap];
|
|
if (hbuf) {
|
|
hbuf->miplevel = curmap + 1;
|
|
}
|
|
|
|
if (!hbuf || (hbuf->x <= 2 && hbuf->y <= 2)) {
|
|
break;
|
|
}
|
|
|
|
curmap++;
|
|
}
|
|
}
|
|
|
|
void IMB_makemipmap(ImBuf *ibuf, int use_filter)
|
|
{
|
|
ImBuf *hbuf = ibuf;
|
|
int curmap = 0;
|
|
|
|
imb_freemipmapImBuf(ibuf);
|
|
|
|
/* no mipmap for non RGBA images */
|
|
if (ibuf->float_buffer.data && ibuf->channels < 4) {
|
|
return;
|
|
}
|
|
|
|
ibuf->miptot = 1;
|
|
|
|
while (curmap < IMB_MIPMAP_LEVELS) {
|
|
if (use_filter) {
|
|
ImBuf *nbuf = IMB_allocImBuf(hbuf->x, hbuf->y, hbuf->planes, hbuf->flags);
|
|
imb_filterN(nbuf, hbuf);
|
|
ibuf->mipmap[curmap] = IMB_onehalf(nbuf);
|
|
IMB_freeImBuf(nbuf);
|
|
}
|
|
else {
|
|
ibuf->mipmap[curmap] = IMB_onehalf(hbuf);
|
|
}
|
|
|
|
ibuf->miptot = curmap + 2;
|
|
hbuf = ibuf->mipmap[curmap];
|
|
hbuf->miplevel = curmap + 1;
|
|
|
|
if (hbuf->x < 2 && hbuf->y < 2) {
|
|
break;
|
|
}
|
|
|
|
curmap++;
|
|
}
|
|
}
|
|
|
|
ImBuf *IMB_getmipmap(ImBuf *ibuf, int level)
|
|
{
|
|
CLAMP(level, 0, ibuf->miptot - 1);
|
|
return (level == 0) ? ibuf : ibuf->mipmap[level - 1];
|
|
}
|
|
|
|
void IMB_premultiply_rect(uint8_t *rect, char planes, int w, int h)
|
|
{
|
|
uint8_t *cp;
|
|
int x, y, val;
|
|
|
|
if (planes == 24) { /* put alpha at 255 */
|
|
cp = rect;
|
|
|
|
for (y = 0; y < h; y++) {
|
|
for (x = 0; x < w; x++, cp += 4) {
|
|
cp[3] = 255;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
cp = rect;
|
|
|
|
for (y = 0; y < h; y++) {
|
|
for (x = 0; x < w; x++, cp += 4) {
|
|
val = cp[3];
|
|
cp[0] = (cp[0] * val) >> 8;
|
|
cp[1] = (cp[1] * val) >> 8;
|
|
cp[2] = (cp[2] * val) >> 8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IMB_premultiply_rect_float(float *rect_float, int channels, int w, int h)
|
|
{
|
|
float val, *cp;
|
|
int x, y;
|
|
|
|
if (channels == 4) {
|
|
cp = rect_float;
|
|
for (y = 0; y < h; y++) {
|
|
for (x = 0; x < w; x++, cp += 4) {
|
|
val = cp[3];
|
|
cp[0] = cp[0] * val;
|
|
cp[1] = cp[1] * val;
|
|
cp[2] = cp[2] * val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IMB_premultiply_alpha(ImBuf *ibuf)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (ibuf->byte_buffer.data) {
|
|
IMB_premultiply_rect(ibuf->byte_buffer.data, ibuf->planes, ibuf->x, ibuf->y);
|
|
}
|
|
|
|
if (ibuf->float_buffer.data) {
|
|
IMB_premultiply_rect_float(ibuf->float_buffer.data, ibuf->channels, ibuf->x, ibuf->y);
|
|
}
|
|
}
|
|
|
|
void IMB_unpremultiply_rect(uint8_t *rect, char planes, int w, int h)
|
|
{
|
|
uchar *cp;
|
|
int x, y;
|
|
float val;
|
|
|
|
if (planes == 24) { /* put alpha at 255 */
|
|
cp = rect;
|
|
|
|
for (y = 0; y < h; y++) {
|
|
for (x = 0; x < w; x++, cp += 4) {
|
|
cp[3] = 255;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
cp = rect;
|
|
|
|
for (y = 0; y < h; y++) {
|
|
for (x = 0; x < w; x++, cp += 4) {
|
|
val = cp[3] != 0 ? 1.0f / float(cp[3]) : 1.0f;
|
|
cp[0] = unit_float_to_uchar_clamp(cp[0] * val);
|
|
cp[1] = unit_float_to_uchar_clamp(cp[1] * val);
|
|
cp[2] = unit_float_to_uchar_clamp(cp[2] * val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IMB_unpremultiply_rect_float(float *rect_float, int channels, int w, int h)
|
|
{
|
|
float val, *fp;
|
|
int x, y;
|
|
|
|
if (channels == 4) {
|
|
fp = rect_float;
|
|
for (y = 0; y < h; y++) {
|
|
for (x = 0; x < w; x++, fp += 4) {
|
|
val = fp[3] != 0.0f ? 1.0f / fp[3] : 1.0f;
|
|
fp[0] = fp[0] * val;
|
|
fp[1] = fp[1] * val;
|
|
fp[2] = fp[2] * val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IMB_unpremultiply_alpha(ImBuf *ibuf)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (ibuf->byte_buffer.data) {
|
|
IMB_unpremultiply_rect(ibuf->byte_buffer.data, ibuf->planes, ibuf->x, ibuf->y);
|
|
}
|
|
|
|
if (ibuf->float_buffer.data) {
|
|
IMB_unpremultiply_rect_float(ibuf->float_buffer.data, ibuf->channels, ibuf->x, ibuf->y);
|
|
}
|
|
}
|