tornavis/source/blender/imbuf/intern/rectop.c

559 lines
13 KiB
C
Raw Normal View History

2002-10-12 13:37:38 +02:00
/**
*
* ***** BEGIN GPL LICENSE BLOCK *****
2002-10-12 13:37:38 +02:00
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
2002-10-12 13:37:38 +02:00
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
2010-02-12 14:34:04 +01:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2002-10-12 13:37:38 +02:00
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
2002-10-12 13:37:38 +02:00
* allocimbuf.c
*
* $Id$
*/
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
2002-10-12 13:37:38 +02:00
#include "imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
#include "IMB_allocimbuf.h"
#include "BKE_utildefines.h"
2002-10-12 13:37:38 +02:00
/* blend modes */
static void blend_color_mix(char *cp, char *cp1, char *cp2, int fac)
{
/* this and other blending modes previously used >>8 instead of /255. both
are not equivalent (>>8 is /256), and the former results in rounding
errors that can turn colors black fast after repeated blending */
int mfac= 255-fac;
cp[0]= (mfac*cp1[0]+fac*cp2[0])/255;
cp[1]= (mfac*cp1[1]+fac*cp2[1])/255;
cp[2]= (mfac*cp1[2]+fac*cp2[2])/255;
}
static void blend_color_add(char *cp, char *cp1, char *cp2, int fac)
{
int temp;
temp= cp1[0] + ((fac*cp2[0])/255);
if(temp>254) cp[0]= 255; else cp[0]= temp;
temp= cp1[1] + ((fac*cp2[1])/255);
if(temp>254) cp[1]= 255; else cp[1]= temp;
temp= cp1[2] + ((fac*cp2[2])/255);
if(temp>254) cp[2]= 255; else cp[2]= temp;
}
static void blend_color_sub(char *cp, char *cp1, char *cp2, int fac)
{
int temp;
temp= cp1[0] - ((fac*cp2[0])/255);
if(temp<0) cp[0]= 0; else cp[0]= temp;
temp= cp1[1] - ((fac*cp2[1])/255);
if(temp<0) cp[1]= 0; else cp[1]= temp;
temp= cp1[2] - ((fac*cp2[2])/255);
if(temp<0) cp[2]= 0; else cp[2]= temp;
}
static void blend_color_mul(char *cp, char *cp1, char *cp2, int fac)
{
int mfac= 255-fac;
/* first mul, then blend the fac */
cp[0]= (mfac*cp1[0] + fac*((cp1[0]*cp2[0])/255))/255;
cp[1]= (mfac*cp1[1] + fac*((cp1[1]*cp2[1])/255))/255;
cp[2]= (mfac*cp1[2] + fac*((cp1[2]*cp2[2])/255))/255;
}
static void blend_color_lighten(char *cp, char *cp1, char *cp2, int fac)
{
/* See if are lighter, if so mix, else dont do anything.
if the paint col is darker then the original, then ignore */
if (cp1[0]+cp1[1]+cp1[2] > cp2[0]+cp2[1]+cp2[2]) {
cp[0]= cp1[0];
cp[1]= cp1[1];
cp[2]= cp1[2];
}
else
blend_color_mix(cp, cp1, cp2, fac);
}
static void blend_color_darken(char *cp, char *cp1, char *cp2, int fac)
{
/* See if were darker, if so mix, else dont do anything.
if the paint col is brighter then the original, then ignore */
if (cp1[0]+cp1[1]+cp1[2] < cp2[0]+cp2[1]+cp2[2]) {
cp[0]= cp1[0];
cp[1]= cp1[1];
cp[2]= cp1[2];
}
else
blend_color_mix(cp, cp1, cp2, fac);
}
unsigned int IMB_blend_color(unsigned int src1, unsigned int src2, int fac, IMB_BlendMode mode)
{
unsigned int dst;
int temp;
char *cp, *cp1, *cp2;
if (fac==0)
return src1;
cp = (char*)&dst;
cp1 = (char*)&src1;
cp2 = (char*)&src2;
switch (mode) {
case IMB_BLEND_MIX:
blend_color_mix(cp, cp1, cp2, fac); break;
case IMB_BLEND_ADD:
blend_color_add(cp, cp1, cp2, fac); break;
case IMB_BLEND_SUB:
blend_color_sub(cp, cp1, cp2, fac); break;
case IMB_BLEND_MUL:
blend_color_mul(cp, cp1, cp2, fac); break;
case IMB_BLEND_LIGHTEN:
blend_color_lighten(cp, cp1, cp2, fac); break;
case IMB_BLEND_DARKEN:
blend_color_darken(cp, cp1, cp2, fac); break;
default:
cp[0]= cp1[0];
cp[1]= cp1[1];
cp[2]= cp1[2];
}
if (mode == IMB_BLEND_ERASE_ALPHA) {
temp= (cp1[3] - fac*cp2[3]/255);
cp[3]= (temp < 0)? 0: temp;
}
else { /* this does ADD_ALPHA also */
temp= (cp1[3] + fac*cp2[3]/255);
cp[3]= (temp > 255)? 255: temp;
}
return dst;
}
static void blend_color_mix_float(float *cp, float *cp1, float *cp2, float fac)
{
float mfac= 1.0-fac;
cp[0]= mfac*cp1[0] + fac*cp2[0];
cp[1]= mfac*cp1[1] + fac*cp2[1];
cp[2]= mfac*cp1[2] + fac*cp2[2];
}
static void blend_color_add_float(float *cp, float *cp1, float *cp2, float fac)
{
cp[0] = cp1[0] + fac*cp2[0];
cp[1] = cp1[1] + fac*cp2[1];
cp[2] = cp1[2] + fac*cp2[2];
if (cp[0] > 1.0f) cp[0]= 1.0f;
if (cp[1] > 1.0f) cp[1]= 1.0f;
if (cp[2] > 1.0f) cp[2]= 1.0f;
}
static void blend_color_sub_float(float *cp, float *cp1, float *cp2, float fac)
{
cp[0] = cp1[0] - fac*cp2[0];
cp[1] = cp1[1] - fac*cp2[1];
cp[2] = cp1[2] - fac*cp2[2];
if (cp[0] < 0.0f) cp[0]= 0.0f;
if (cp[1] < 0.0f) cp[1]= 0.0f;
if (cp[2] < 0.0f) cp[2]= 0.0f;
}
static void blend_color_mul_float(float *cp, float *cp1, float *cp2, float fac)
{
float mfac= 1.0-fac;
cp[0]= mfac*cp1[0] + fac*(cp1[0]*cp2[0]);
cp[1]= mfac*cp1[1] + fac*(cp1[1]*cp2[1]);
cp[2]= mfac*cp1[2] + fac*(cp1[2]*cp2[2]);
}
static void blend_color_lighten_float(float *cp, float *cp1, float *cp2, float fac)
{
/* See if are lighter, if so mix, else dont do anything.
if the pafloat col is darker then the original, then ignore */
if (cp1[0]+cp1[1]+cp1[2] > cp2[0]+cp2[1]+cp2[2]) {
cp[0]= cp1[0];
cp[1]= cp1[1];
cp[2]= cp1[2];
}
else
blend_color_mix_float(cp, cp1, cp2, fac);
}
static void blend_color_darken_float(float *cp, float *cp1, float *cp2, float fac)
{
/* See if were darker, if so mix, else dont do anything.
if the pafloat col is brighter then the original, then ignore */
if (cp1[0]+cp1[1]+cp1[2] < cp2[0]+cp2[1]+cp2[2]) {
cp[0]= cp1[0];
cp[1]= cp1[1];
cp[2]= cp1[2];
}
else
blend_color_mix_float(cp, cp1, cp2, fac);
}
void IMB_blend_color_float(float *dst, float *src1, float *src2, float fac, IMB_BlendMode mode)
{
if (fac==0) {
dst[0]= src1[0];
dst[1]= src1[1];
dst[2]= src1[2];
dst[3]= src1[3];
return;
}
switch (mode) {
case IMB_BLEND_MIX:
blend_color_mix_float(dst, src1, src2, fac); break;
case IMB_BLEND_ADD:
blend_color_add_float(dst, src1, src2, fac); break;
case IMB_BLEND_SUB:
blend_color_sub_float(dst, src1, src2, fac); break;
case IMB_BLEND_MUL:
blend_color_mul_float(dst, src1, src2, fac); break;
case IMB_BLEND_LIGHTEN:
blend_color_lighten_float(dst, src1, src2, fac); break;
case IMB_BLEND_DARKEN:
blend_color_darken_float(dst, src1, src2, fac); break;
default:
dst[0]= src1[0];
dst[1]= src1[1];
dst[2]= src1[2];
}
if (mode == IMB_BLEND_ERASE_ALPHA) {
dst[3]= (src1[3] - fac*src2[3]);
if (dst[3] < 0.0f) dst[3] = 0.0f;
}
else { /* this does ADD_ALPHA also */
dst[3]= (src1[3] + fac*src2[3]);
if (dst[3] > 1.0f) dst[3] = 1.0f;
}
}
/* clipping */
void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx,
int *desty, int *srcx, int *srcy, int *width, int *height)
{
int tmp;
if (dbuf == NULL) return;
if (*destx < 0) {
*srcx -= *destx;
*width += *destx;
*destx = 0;
}
if (*srcx < 0) {
*destx -= *srcx;
2009-12-08 09:44:18 +01:00
*width += *srcx;
*srcx = 0;
}
if (*desty < 0) {
*srcy -= *desty;
*height += *desty;
*desty = 0;
}
if (*srcy < 0) {
*desty -= *srcy;
2009-12-08 09:44:18 +01:00
*height += *srcy;
*srcy = 0;
}
tmp = dbuf->x - *destx;
if (*width > tmp) *width = tmp;
tmp = dbuf->y - *desty;
if (*height > tmp) *height = tmp;
if (sbuf) {
tmp = sbuf->x - *srcx;
if (*width > tmp) *width = tmp;
tmp = sbuf->y - *srcy;
if (*height > tmp) *height = tmp;
}
if ((*height <= 0) || (*width <= 0)) {
*width = 0;
*height = 0;
}
}
/* copy and blend */
void IMB_rectcpy(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx,
int desty, int srcx, int srcy, int width, int height)
2002-10-12 13:37:38 +02:00
{
IMB_rectblend(dbuf, sbuf, destx, desty, srcx, srcy, width, height,
IMB_BLEND_COPY);
}
void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx,
int desty, int srcx, int srcy, int width, int height, IMB_BlendMode mode)
{
unsigned int *drect = NULL, *srect = NULL, *dr, *sr;
float *drectf = NULL, *srectf = NULL, *drf, *srf;
int do_float, do_char, srcskip, destskip, x;
2002-10-12 13:37:38 +02:00
if (dbuf == NULL) return;
IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &width, &height);
if (width == 0 || height == 0) return;
if (sbuf && sbuf->channels!=4) return;
if (dbuf->channels!=4) return;
do_char = (sbuf && sbuf->rect && dbuf->rect);
do_float = (sbuf && sbuf->rect_float && dbuf->rect_float);
if (do_char) drect = dbuf->rect + desty * dbuf->x + destx;
if (do_float) drectf = dbuf->rect_float + (desty * dbuf->x + destx)*4;
destskip = dbuf->x;
if (sbuf) {
if (do_char) srect = sbuf->rect + srcy * sbuf->x + srcx;
if (do_float) srectf = sbuf->rect_float + (srcy * sbuf->x + srcx)*4;
srcskip = sbuf->x;
} else {
2002-10-12 13:37:38 +02:00
srect = drect;
srectf = drectf;
srcskip = destskip;
}
if (mode == IMB_BLEND_COPY) {
/* copy */
for (;height > 0; height--) {
if (do_char) {
memcpy(drect,srect, width * sizeof(int));
drect += destskip;
srect += srcskip;
}
if (do_float) {
memcpy(drectf,srectf, width * sizeof(float) * 4);
drectf += destskip*4;
srectf += srcskip*4;
}
}
}
else 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++) {
((char*)dr)[0]= ((char*)sr)[0];
((char*)dr)[1]= ((char*)sr)[1];
((char*)dr)[2]= ((char*)sr)[2];
}
drect += destskip;
srect += srcskip;
}
if (do_float) {
drf = drectf;
srf = srectf;
for (x=width; x > 0; x--, drf+=4, srf+=4) {
drf[0]= srf[0];
drf[1]= srf[1];
drf[2]= srf[2];
}
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++)
((char*)dr)[3]= ((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;
}
}
2002-10-12 13:37:38 +02:00
}
else {
/* blend */
for (;height > 0; height--) {
if (do_char) {
dr = drect;
sr = srect;
for (x=width; x > 0; x--, dr++, sr++)
*dr = IMB_blend_color(*dr, *sr, ((char*)sr)[3], mode);
2002-10-12 13:37:38 +02:00
drect += destskip;
srect += srcskip;
}
if (do_float) {
drf = drectf;
srf = srectf;
for (x=width; x > 0; x--, drf+=4, srf+=4)
IMB_blend_color_float(drf, drf, srf, srf[3], mode);
drectf += destskip*4;
srectf += srcskip*4;
}
}
2002-10-12 13:37:38 +02:00
}
}
/* fill */
void IMB_rectfill(struct ImBuf *drect, float col[4])
2002-10-12 13:37:38 +02:00
{
int num;
if(drect->rect) {
unsigned int *rrect = drect->rect;
char ccol[4];
ccol[0]= (int)(col[0]*255);
ccol[1]= (int)(col[1]*255);
ccol[2]= (int)(col[2]*255);
ccol[3]= (int)(col[3]*255);
num = drect->x * drect->y;
for (;num > 0; num--)
*rrect++ = *((unsigned int*)ccol);
}
if(drect->rect_float) {
float *rrectf = drect->rect_float;
num = drect->x * drect->y;
for (;num > 0; num--) {
*rrectf++ = col[0];
*rrectf++ = col[1];
*rrectf++ = col[2];
*rrectf++ = col[3];
}
}
2002-10-12 13:37:38 +02:00
}
void buf_rectfill_area(unsigned char *rect, float *rectf, int width, int height, float *col, int x1, int y1, int x2, int y2)
{
int i, j;
float a; /* alpha */
float ai; /* alpha inverted */
float aich; /* alpha, inverted, ai/255.0 - Convert char to float at the same time */
if ((!rect && !rectf) || (!col) || col[3]==0.0)
return;
/* sanity checks for coords */
CLAMP(x1, 0, width);
CLAMP(x2, 0, width);
CLAMP(y1, 0, height);
CLAMP(y2, 0, height);
if (x1>x2) SWAP(int,x1,x2);
if (y1>y2) SWAP(int,y1,y2);
if (x1==x2 || y1==y2) return;
a = col[3];
ai = 1-a;
aich = ai/255.0f;
if (rect) {
unsigned char *pixel;
unsigned char chr=0, chg=0, chb=0;
float fr=0, fg=0, fb=0;
if (a == 1.0) {
chr = FTOCHAR(col[0]);
chg = FTOCHAR(col[1]);
chb = FTOCHAR(col[2]);
} else {
fr = col[0]*a;
fg = col[1]*a;
fb = col[2]*a;
}
for (j = 0; j < y2-y1; j++) {
for (i = 0; i < x2-x1; i++) {
pixel = rect + 4 * (((y1 + j) * width) + (x1 + i));
if (pixel >= rect && pixel < rect+ (4 * (width * height))) {
if (a == 1.0) {
pixel[0] = chr;
pixel[1] = chg;
pixel[2] = chb;
} else {
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);
}
}
}
}
}
if (rectf) {
float *pixel;
for (j = 0; j < y2-y1; j++) {
for (i = 0; i < x2-x1; i++) {
pixel = rectf + 4 * (((y1 + j) * width) + (x1 + i));
if (a == 1.0) {
pixel[0] = col[0];
pixel[1] = col[1];
pixel[2] = col[2];
} else {
pixel[0] = (col[0]*a) + (pixel[0]*ai);
pixel[1] = (col[1]*a) + (pixel[1]*ai);
pixel[2] = (col[2]*a) + (pixel[2]*ai);
}
}
}
}
}
void IMB_rectfill_area(struct ImBuf *ibuf, float *col, int x1, int y1, int x2, int y2)
{
if (!ibuf) return;
buf_rectfill_area((unsigned char *) ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, col, x1, y1, x2, y2);
}