projection painting clone tool - gives a similar work flow to cloning in the gimp, Ctrl+Click to set the cursor source, then paint from this location.

todo...
* pixel interpolation.
* clone option can currently only be set from the image paint panel.
* only initialize clone pixels under the mouse.
* overlap between source/target while painting could cause problems. need to look into this.

also fixed some cashes in painting normally.
This commit is contained in:
Campbell Barton
2008-11-05 14:45:54 +00:00
parent ea168748eb
commit b98b4e8be8

View File

@@ -145,8 +145,9 @@ typedef struct ImagePaintState {
#define PROJ_FACE_SEAM4 1<<5
#define PROJ_BUCKET_NULL 0
#define PROJ_BUCKET_INIT 1
#define PROJ_BUCKET_NULL 0
#define PROJ_BUCKET_INIT 1<<0
// #define PROJ_BUCKET_CLONE_INIT 1<<1
/* only for readability */
#define PROJ_BUCKET_LEFT 0
@@ -217,8 +218,10 @@ typedef struct ProjectPixel {
} ProjectPixel;
typedef struct ProjectPixelClone {
struct ProjectPixel;
void *source;
struct ProjectPixel __pp;
char backbuf[4]; /* TODO - float buffer? */
char clonebuf[4];
//void *source; /* pointer to source pixels */
} ProjectPixelClone;
/* Finish projection painting structs */
@@ -396,15 +399,144 @@ static int project_paint_BucketOffset(ProjectPaintState *ps, float *projCo2D)
( ( (int)(( (projCo2D[1] - ps->viewMin2D[1]) / ps->viewHeight) * ps->bucketsY)) * ps->bucketsX );
}
static int project_paint_BucketOffsetSafe(ProjectPaintState *ps, float *projCo2D)
{
int bucket_index = project_paint_BucketOffset(ps, projCo2D);
if (bucket_index < 0 || bucket_index >= ps->bucketsX*ps->bucketsY) {
return -1;
} else {
return bucket_index;
}
}
/* assume they intersect */
static void BarryCentricWeights2f(float v1[2], float v2[2], float v3[2], float pt[2], float w[3]) {
float wtot;
w[0] = AreaF2Dfl(v2, v3, pt);
w[1] = AreaF2Dfl(v3, v1, pt);
w[2] = AreaF2Dfl(v1, v2, pt);
wtot = w[0]+w[1]+w[2];
w[0]/=wtot;
w[1]/=wtot;
w[2]/=wtot;
}
static float tri_depth_2d(float v1[3], float v2[3], float v3[3], float pt[2], float w[3])
{
BarryCentricWeights2f(v1,v2,v3,pt,w);
return (v1[2]*w[0]) + (v2[2]*w[1]) + (v3[2]*w[2]);
}
/* return the topmost face in screen coords index or -1
* bucket_index can be -1 if we dont know it to begin with */
static int screenco_pickface(ProjectPaintState *ps, float pt[2], float w[3], int *side) {
LinkNode *node;
float w_tmp[3];
float *v1, *v2, *v3, *v4;
int bucket_index;
int face_index;
int best_side = -1;
int best_face_index = -1;
float z_depth_best = MAXFLOAT, z_depth;
MFace *mf;
bucket_index = project_paint_BucketOffsetSafe(ps, pt);
if (bucket_index==-1)
return -1;
node = ps->projectFaces[bucket_index];
/* we could return 0 for 1 face buckets, as long as this function assumes
* that the point its testing is only every originated from an existing face */
while (node) {
face_index = (int)node->link;
mf = ps->dm_mface + face_index;
v1 = ps->projectVertScreenCos[mf->v1];
v2 = ps->projectVertScreenCos[mf->v2];
v3 = ps->projectVertScreenCos[mf->v3];
if ( IsectPT2Df(pt, v1, v2, v3) ) {
z_depth = tri_depth_2d(v1,v2,v3,pt,w_tmp);
if (z_depth < z_depth_best) {
best_face_index = face_index;
best_side = 0;
z_depth_best = z_depth;
VECCOPY(w, w_tmp);
}
} else if (mf->v4) {
v4 = ps->projectVertScreenCos[mf->v4];
if ( IsectPT2Df(pt, v1, v3, v4) ) {
z_depth = tri_depth_2d(v1,v3,v4,pt,w_tmp);
if (z_depth < z_depth_best) {
best_face_index = face_index;
best_side = 1;
z_depth_best = z_depth;
VECCOPY(w, w_tmp);
}
}
}
node = node->next;
}
*side = best_side;
return best_face_index; /* will be -1 or a valid face */
}
/* bucket_index is optional, since in some cases we know it */
static int screenco_pickcol(ProjectPaintState *ps, int bucket_index, float pt[2], char rgba[4])
{
float w[3], uv[2];
int side;
int face_index;
MTFace *tf;
ImBuf *ibuf;
int x,y;
char *pixel;
face_index = screenco_pickface(ps,pt,w, &side);
if (face_index == -1)
return 0;
tf = ps->dm_mtface + face_index;
if (side==0) {
uv[0] = tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[2][0]*w[2];
uv[1] = tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[2][1]*w[2];
} else { /* QUAD */
uv[0] = tf->uv[0][0]*w[0] + tf->uv[2][0]*w[1] + tf->uv[3][0]*w[2];
uv[1] = tf->uv[0][1]*w[0] + tf->uv[2][1]*w[1] + tf->uv[3][1]*w[2];
}
ibuf = BKE_image_get_ibuf((Image *)tf->tpage, NULL); /* TODO - this may be slow */
x = uv[0]*ibuf->x;
y = uv[1]*ibuf->y;
if (x<0 || x>=ibuf->x || y<0 || y>=ibuf->y) return 0;
pixel = (( char * ) ibuf->rect) + (( x + y * ibuf->x ) * 4);
rgba[0] = pixel[0];
rgba[1] = pixel[1];
rgba[2] = pixel[2];
rgba[3] = pixel[3];
return 1;
}
/* return...
* 0 : no occlusion
* -1 : no occlusion but 2D intersection is true (avoid testing the other half of a quad)
* 1 : occluded */
static int screenco_tri_pt_occlude(float *pt, float *v1, float *v2, float *v3)
static int screenco_tri_pt_occlude(float pt[3], float v1[3], float v2[3], float v3[3])
{
float w1, w2, w3, wtot; /* weights for converting the pixel into 3d worldspace coords */
/* if all are behind us, return false */
if(v1[2] > pt[2] && v2[2] > pt[2] && v3[2] > pt[2])
return 0;
@@ -419,13 +551,9 @@ static int screenco_tri_pt_occlude(float *pt, float *v1, float *v2, float *v3)
if( v1[2] < pt[2] && v2[2] < pt[2] && v3[2] < pt[2]) {
return 1;
} else {
float w[3];
/* we intersect? - find the exact depth at the point of intersection */
w1 = AreaF2Dfl(v2, v3, pt);
w2 = AreaF2Dfl(v3, v1, pt);
w3 = AreaF2Dfl(v1, v2, pt);
wtot = w1 + w2 + w3;
if ((v1[2]*w1/wtot) + (v2[2]*w2/wtot) + (v3[2]*w3/wtot) < pt[2]) {
if (tri_depth_2d(v1,v2,v3,pt,w) < pt[2]) {
return 1; /* This point is occluded by another face */
}
}
@@ -497,7 +625,7 @@ static int project_bucket_point_occluded(ProjectPaintState *ps, int bucket_index
#define ISECT_TRUE 1
#define ISECT_TRUE_P1 2
#define ISECT_TRUE_P2 3
static int project_scanline_isect(float *p1, float *p2, float y_level, float *x_isect)
static int project_scanline_isect(float p1[2], float p2[2], float y_level, float *x_isect)
{
if (y_level==p1[1]) {
*x_isect = p1[0];
@@ -519,7 +647,7 @@ static int project_scanline_isect(float *p1, float *p2, float y_level, float *x_
}
}
static int project_face_scanline(ProjectScanline *sc, float y_level, float *v1, float *v2, float *v3, float *v4)
static int project_face_scanline(ProjectScanline *sc, float y_level, float v1[2], float v2[2], float v3[2], float v4[2])
{
/* Create a scanlines for the face at this Y level
* triangles will only ever have 1 scanline, quads may have 2 */
@@ -614,7 +742,7 @@ static int project_face_scanline(ProjectScanline *sc, float y_level, float *v1,
return totscanlines;
}
static int cmp_uv(float *vec2a, float *vec2b)
static int cmp_uv(float vec2a[2], float vec2b[2])
{
return ((fabs(vec2a[0]-vec2b[0]) < 0.0001) && (fabs(vec2a[1]-vec2b[1]) < 0.0001)) ? 1:0;
}
@@ -712,7 +840,7 @@ static float angleToLength(float angle)
}
/* return zero if there is no area in the returned rectangle */
static int uv_image_rect(float *uv1, float *uv2, float *uv3, float *uv4, int *min_px, int *max_px, int x_px, int y_px, int is_quad)
static int uv_image_rect(float uv1[2], float uv2[2], float uv3[2], float uv4[2], int min_px[2], int max_px[2], int x_px, int y_px, int is_quad)
{
float min_uv[2], max_uv[2]; /* UV bounds */
int i;
@@ -862,41 +990,29 @@ static float lambda_cp_line2(float p[2], float l1[2], float l2[2])
}
static screen_px_from_ortho(
ProjectPaintState *ps, float *uv,
float *v1co, float *v2co, float *v3co, /* Screenspace coords */
float *uv1co, float *uv2co, float *uv3co,
float *pixelScreenCo )
ProjectPaintState *ps, float uv[2],
float v1co[3], float v2co[3], float v3co[3], /* Screenspace coords */
float uv1co[2], float uv2co[2], float uv3co[2],
float pixelScreenCo[4] )
{
float w1, w2, w3, wtot; /* weights for converting the pixel into 3d screenspace coords */
w1 = AreaF2Dfl(uv2co, uv3co, uv);
w2 = AreaF2Dfl(uv3co, uv1co, uv);
w3 = AreaF2Dfl(uv1co, uv2co, uv);
wtot = w1 + w2 + w3;
w1 /= wtot; w2 /= wtot; w3 /= wtot;
pixelScreenCo[0] = v1co[0]*w1 + v2co[0]*w2 + v3co[0]*w3;
pixelScreenCo[1] = v1co[1]*w1 + v2co[1]*w2 + v3co[1]*w3;
pixelScreenCo[2] = v1co[2]*w1 + v2co[2]*w2 + v3co[2]*w3;
float w[3];
BarryCentricWeights2f(uv1co,uv2co,uv3co,uv,w);
pixelScreenCo[0] = v1co[0]*w[0] + v2co[0]*w[1] + v3co[0]*w[2];
pixelScreenCo[1] = v1co[1]*w[0] + v2co[1]*w[1] + v3co[1]*w[2];
pixelScreenCo[2] = v1co[2]*w[0] + v2co[2]*w[1] + v3co[2]*w[2];
}
static screen_px_from_persp(
ProjectPaintState *ps, float *uv,
float *v1co, float *v2co, float *v3co, /* Worldspace coords */
float *uv1co, float *uv2co, float *uv3co,
float *pixelScreenCo )
ProjectPaintState *ps, float uv[2],
float v1co[3], float v2co[3], float v3co[3], /* Worldspace coords */
float uv1co[2], float uv2co[2], float uv3co[2],
float pixelScreenCo[4])
{
float w1, w2, w3, wtot; /* weights for converting the pixel into 3d screenspace coords */
w1 = AreaF2Dfl(uv2co, uv3co, uv);
w2 = AreaF2Dfl(uv3co, uv1co, uv);
w3 = AreaF2Dfl(uv1co, uv2co, uv);
wtot = w1 + w2 + w3;
w1 /= wtot; w2 /= wtot; w3 /= wtot;
pixelScreenCo[0] = v1co[0]*w1 + v2co[0]*w2 + v3co[0]*w3;
pixelScreenCo[1] = v1co[1]*w1 + v2co[1]*w2 + v3co[1]*w3;
pixelScreenCo[2] = v1co[2]*w1 + v2co[2]*w2 + v3co[2]*w3;
float w[3];
BarryCentricWeights2f(uv1co,uv2co,uv3co,uv,w);
pixelScreenCo[0] = v1co[0]*w[0] + v2co[0]*w[1] + v3co[0]*w[2];
pixelScreenCo[1] = v1co[1]*w[0] + v2co[1]*w[1] + v3co[1]*w[2];
pixelScreenCo[2] = v1co[2]*w[0] + v2co[2]*w[1] + v3co[2]*w[2];
pixelScreenCo[3] = 1.0;
Mat4MulVec4fl(ps->projectMat, pixelScreenCo);
@@ -908,10 +1024,13 @@ static screen_px_from_persp(
pixelScreenCo[2] = pixelScreenCo[2]/pixelScreenCo[3]; /* Use the depth for bucket point occlusion */
}
/* can provide own own coords, use for seams when we want to bleed our from the original location */
static void project_paint_bucket_init(ProjectPaintState *ps, int bucket_index);
/* Only run this function once for new ProjectPixelClone's */
#define pixel_size 4
static void project_paint_uvpixel_init(ProjectPaintState *ps, ImBuf *ibuf, float *uv, int x, int y, int face_index, float *pixelScreenCo)
static void project_paint_uvpixel_init(ProjectPaintState *ps, ImBuf *ibuf, float uv[2], int x, int y, int face_index, float pixelScreenCo[4])
{
int bucket_index;
@@ -919,10 +1038,10 @@ static void project_paint_uvpixel_init(ProjectPaintState *ps, ImBuf *ibuf, float
ProjectPixel *projPixel;
bucket_index = project_paint_BucketOffset(ps, pixelScreenCo);
bucket_index = project_paint_BucketOffsetSafe(ps, pixelScreenCo);
/* even though it should be clamped, in some cases it can still run over */
if (bucket_index < 0 || bucket_index >= ps->bucketsX * ps->bucketsY)
if (bucket_index==-1)
return;
/* Use viewMin2D to make (0,0) the bottom left of the bounds
@@ -931,12 +1050,38 @@ static void project_paint_uvpixel_init(ProjectPaintState *ps, ImBuf *ibuf, float
/* Is this UV visible from the view? - raytrace */
if (ps->projectIsOcclude==0 || !project_bucket_point_occluded(ps, bucket_index, face_index, pixelScreenCo)) {
/* done with view3d_project_float inline */
projPixel = (ProjectPixel *)BLI_memarena_alloc( ps->projectArena, sizeof(ProjectPixel) );
if (ps->tool==PAINT_TOOL_CLONE) {
float co[2];
projPixel = (ProjectPixel *)BLI_memarena_alloc(ps->projectArena, sizeof(ProjectPixelClone));
projPixel->pixel = (( char * ) ibuf->rect) + (( x + y * ibuf->x ) * pixel_size);
VECCOPY2D(projPixel->projCo2D, pixelScreenCo);
/* copy pixel color to backbuf */
memcpy( &(((ProjectPixelClone *)projPixel)->backbuf), projPixel->pixel, pixel_size);
//((ProjectPixelClone *)projPixel)->source = NULL; /* must be set later */
/* Initialize clone pixels - note that this is a bit of a waste since some of these are being indirectly initialized :/ */
/* TODO - possibly only run this for directly ativated buckets when cloning */
Vec2Subf(co, projPixel->projCo2D, ps->cloneOfs);
/* no need to initialize the bucket, we're only checking buckets faces and for this
* the faces are alredy initialized in project_paint_delayed_face_init(...) */
if (!screenco_pickcol(ps, bucket_index, co, ((ProjectPixelClone *)projPixel)->clonebuf)) {
((ProjectPixelClone *)projPixel)->clonebuf[3] = 0; /* zero alpha - ignore */
}
} else {
projPixel = (ProjectPixel *)BLI_memarena_alloc(ps->projectArena, sizeof(ProjectPixel));
projPixel->pixel = (( char * ) ibuf->rect) + (( x + y * ibuf->x ) * pixel_size);
VECCOPY2D(projPixel->projCo2D, pixelScreenCo);
}
/* screenspace unclamped */
VECCOPY2D(projPixel->projCo2D, pixelScreenCo);
projPixel->pixel = (( char * ) ibuf->rect) + (( x + y * ibuf->x ) * pixel_size);
#ifdef PROJ_DEBUG_PAINT
projPixel->pixel[1] = 0;
@@ -1235,11 +1380,11 @@ static void project_paint_face_init(ProjectPaintState *ps, int face_index, ImBuf
static void project_paint_rect(ProjectPaintState *ps, float min[2], float max[2], int bucket_min[2], int bucket_max[2])
{
/* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */
bucket_min[0] = (int)(((float)(min[0] - ps->viewMin2D[0]) / ps->viewWidth) * ps->bucketsX) + 0.5;
bucket_min[0] = (int)(((float)(min[0] - ps->viewMin2D[0]) / ps->viewWidth) * ps->bucketsX) + 0.5; /* these offsets of 0.5 and 1.5 seem odd but they are correct */
bucket_min[1] = (int)(((float)(min[1] - ps->viewMin2D[1]) / ps->viewHeight) * ps->bucketsY) + 0.5;
bucket_max[0] = (int)(((float)(max[0] - ps->viewMin2D[0]) / ps->viewWidth) * ps->bucketsX) + 0.5;
bucket_max[1] = (int)(((float)(max[1] - ps->viewMin2D[1]) / ps->viewHeight) * ps->bucketsY) + 0.5;
bucket_max[0] = (int)(((float)(max[0] - ps->viewMin2D[0]) / ps->viewWidth) * ps->bucketsX) + 1.5;
bucket_max[1] = (int)(((float)(max[1] - ps->viewMin2D[1]) / ps->viewHeight) * ps->bucketsY) + 1.5;
/* incase the rect is outside the mesh 2d bounds */
CLAMP(bucket_min[0], 0, ps->bucketsX);
@@ -1252,7 +1397,7 @@ static void project_paint_rect(ProjectPaintState *ps, float min[2], float max[2]
static void project_bucket_bounds(ProjectPaintState *ps, int bucket_x, int bucket_y, float bucket_bounds[4])
{
bucket_bounds[ PROJ_BUCKET_LEFT ] = ps->viewMin2D[0]+((bucket_x)*(ps->viewWidth / ps->bucketsX)); /* left */
bucket_bounds[ PROJ_BUCKET_RIGHT ] = ps->viewMin2D[0]+((bucket_x+1)*(ps->viewWidth / ps->bucketsX)); /* right */
bucket_bounds[ PROJ_BUCKET_RIGHT ] = ps->viewMin2D[0]+((bucket_x+1)*(ps->viewWidth / ps->bucketsX)); /* right */
bucket_bounds[ PROJ_BUCKET_BOTTOM ] = ps->viewMin2D[1]+((bucket_y)*(ps->viewHeight / ps->bucketsY)); /* bottom */
bucket_bounds[ PROJ_BUCKET_TOP ] = ps->viewMin2D[1]+((bucket_y+1)*(ps->viewHeight / ps->bucketsY)); /* top */
@@ -1260,7 +1405,6 @@ static void project_bucket_bounds(ProjectPaintState *ps, int bucket_x, int bucke
static void project_paint_bucket_init(ProjectPaintState *ps, int bucket_index)
{
LinkNode *node;
int face_index;
ImBuf *ibuf;
@@ -1268,7 +1412,7 @@ static void project_paint_bucket_init(ProjectPaintState *ps, int bucket_index)
/*printf("\tinit bucket %d\n", bucket_index);*/
ps->projectBucketFlags[bucket_index] = PROJ_BUCKET_INIT;
ps->projectBucketFlags[bucket_index] |= PROJ_BUCKET_INIT;
if ((node = ps->projectFaces[bucket_index])) {
do {
@@ -1288,6 +1432,7 @@ static void project_paint_bucket_init(ProjectPaintState *ps, int bucket_index)
}
}
/* We want to know if a bucket and a face overlap in screenspace
*
* Note, if this ever returns false positives its not that bad, since a face in the bounding area will have its pixels
@@ -1519,10 +1664,14 @@ static void project_paint_begin( ProjectPaintState *ps, short mval[2])
if (ps->tool == PAINT_TOOL_CLONE) {
float *curs= give_cursor();
VECCOPY(projCo, curs); /* TODO - what if were in local view? - get some better way */
Mat4MulVec4fl(ps->ob->imat, projCo);
projCo[3] = 1.0;
Mat4MulVec4fl(ps->projectMat, projCo);
ps->cloneOfs[0] = mval[0] - ((float)(curarea->winx/2.0)+(curarea->winx/2.0)*projCo[0]/projCo[3]);
ps->cloneOfs[0] = mval[0] - ((float)(curarea->winx/2.0)+(curarea->winx/2.0)*projCo[0]/projCo[3]);
ps->cloneOfs[1] = mval[1] - ((float)(curarea->winy/2.0)+(curarea->winy/2.0)*projCo[1]/projCo[3]);
// printf("%f %f %f %f %f\n", ps->cloneOfs[0], ps->cloneOfs[1], curs[0], curs[1], curs[2]);
}
/* If this border is not added we get artifacts for faces that
@@ -2080,7 +2229,7 @@ static int project_bucket_circle_isect(ProjectPaintState *ps, int bucket_x, int
{
return 1;
}
/* out of bounds left */
if (cent[0] < bucket_bounds[PROJ_BUCKET_LEFT]) {
/* lower left out of radius test */
@@ -2165,6 +2314,9 @@ static int imapaint_paint_sub_stroke_project(ProjectPaintState *ps, BrushPainter
/* This bucket may hold some uninitialized faces, initialize it */
project_paint_bucket_init(ps, bucket_index);
}
/* TODO - we may want to init clone data in a seperate to project_paint_bucket_init
* so we dont go overboard and init too many clone pixels */
if ((node = ps->projectBuckets[bucket_index])) {
@@ -2179,26 +2331,52 @@ static int imapaint_paint_sub_stroke_project(ProjectPaintState *ps, BrushPainter
/*if (dist < s->brush->size) {*/ /* correct but uses a sqrt */
if (dist_nosqrt < brush_size_sqared) {
brush_sample_tex(ps->brush, projPixel->projCo2D, rgba);
dist = (float)sqrt(dist_nosqrt);
alpha = rgba[3]*brush_sample_falloff(ps->brush, dist);
if (alpha <= 0.0) {
/* do nothing */
} else {
if (alpha >= 1.0) {
projPixel->pixel[0] = FTOCHAR(rgba[0]*ps->brush->rgb[0]);
projPixel->pixel[1] = FTOCHAR(rgba[1]*ps->brush->rgb[1]);
projPixel->pixel[2] = FTOCHAR(rgba[2]*ps->brush->rgb[2]);
} else {
projPixel->pixel[0] = FTOCHAR(((rgba[0]*ps->brush->rgb[0])*alpha) + (((projPixel->pixel[0])/255.0)*(1.0-alpha)));
projPixel->pixel[1] = FTOCHAR(((rgba[1]*ps->brush->rgb[1])*alpha) + (((projPixel->pixel[1])/255.0)*(1.0-alpha)));
projPixel->pixel[2] = FTOCHAR(((rgba[2]*ps->brush->rgb[2])*alpha) + (((projPixel->pixel[2])/255.0)*(1.0-alpha)));
if (ps->tool==PAINT_TOOL_CLONE) { //&& ((ProjectPixelClone*)projPixel)->source ) {
alpha = brush_sample_falloff(ps->brush, dist);
if (((char *)((ProjectPixelClone*)projPixel)->clonebuf)[3]) {
projPixel->pixel[0] = FTOCHAR((((((char *)((ProjectPixelClone*)projPixel)->clonebuf)[0]/255.0) * alpha) + (((projPixel->pixel[0])/255.0)*(1.0-alpha))));
projPixel->pixel[1] = FTOCHAR((((((char *)((ProjectPixelClone*)projPixel)->clonebuf)[1]/255.0) * alpha) + (((projPixel->pixel[1])/255.0)*(1.0-alpha))));
projPixel->pixel[2] = FTOCHAR((((((char *)((ProjectPixelClone*)projPixel)->clonebuf)[2]/255.0) * alpha) + (((projPixel->pixel[2])/255.0)*(1.0-alpha))));
}
}
#if 0
if (alpha <= 0.0) {
/* do nothing */
} else {
if (alpha >= 1.0) {
projPixel->pixel[0] = FTOCHAR(rgba[0]*ps->brush->rgb[0]);
projPixel->pixel[1] = FTOCHAR(rgba[1]*ps->brush->rgb[1]);
projPixel->pixel[2] = FTOCHAR(rgba[2]*ps->brush->rgb[2]);
} else {
projPixel->pixel[0] = FTOCHAR(((rgba[0]*ps->brush->rgb[0])*alpha) + (((projPixel->pixel[0])/255.0)*(1.0-alpha)));
projPixel->pixel[1] = FTOCHAR(((rgba[1]*ps->brush->rgb[1])*alpha) + (((projPixel->pixel[1])/255.0)*(1.0-alpha)));
projPixel->pixel[2] = FTOCHAR(((rgba[2]*ps->brush->rgb[2])*alpha) + (((projPixel->pixel[2])/255.0)*(1.0-alpha)));
}
}
#endif
} else {
brush_sample_tex(ps->brush, projPixel->projCo2D, rgba);
alpha = rgba[3]*brush_sample_falloff(ps->brush, dist);
if (alpha <= 0.0) {
/* do nothing */
} else {
if (alpha >= 1.0) {
projPixel->pixel[0] = FTOCHAR(rgba[0]*ps->brush->rgb[0]);
projPixel->pixel[1] = FTOCHAR(rgba[1]*ps->brush->rgb[1]);
projPixel->pixel[2] = FTOCHAR(rgba[2]*ps->brush->rgb[2]);
} else {
projPixel->pixel[0] = FTOCHAR(((rgba[0]*ps->brush->rgb[0])*alpha) + (((projPixel->pixel[0])/255.0)*(1.0-alpha)));
projPixel->pixel[1] = FTOCHAR(((rgba[1]*ps->brush->rgb[1])*alpha) + (((projPixel->pixel[1])/255.0)*(1.0-alpha)));
projPixel->pixel[2] = FTOCHAR(((rgba[2]*ps->brush->rgb[2])*alpha) + (((projPixel->pixel[2])/255.0)*(1.0-alpha)));
}
}
}
if (last_index != projPixel->image_index) {
last_index = projPixel->image_index;
@@ -2336,13 +2514,19 @@ void imagepaint_paint(short mousebutton, short texpaint)
if(!settings->imapaint.brush)
return;
project = texpaint;
if (G.qual & LR_CTRLKEY) {
mouse_cursor();
return;
}
/* initialize state */
memset(&s, 0, sizeof(s));
memset(&ps, 0, sizeof(ps));
project = texpaint;
s.brush = settings->imapaint.brush;
s.tool = settings->imapaint.tool;
if(texpaint && (project==0) && (s.tool == PAINT_TOOL_CLONE))