예제 #1
0
/* create a mask with the falloff strength and optionally brush alpha */
static unsigned short *brush_painter_mask_new(BrushPainter *painter, int size)
{
    Scene *scene = painter->scene;
    Brush *brush = painter->brush;
    bool use_masking = painter->cache.use_masking;

    float alpha = (use_masking) ? 1.0f : BKE_brush_alpha_get(scene, brush);
    int radius = BKE_brush_size_get(scene, brush);
    int xoff = -size * 0.5f + 0.5f;
    int yoff = -size * 0.5f + 0.5f;

    unsigned short *mask, *m;
    int x, y;

    mask = MEM_callocN(sizeof(unsigned short) * size * size, "brush_painter_mask");
    m = mask;

    for (y = 0; y < size; y++) {
        for (x = 0; x < size; x++, m++) {
            float xy[2] = {x + xoff, y + yoff};
            float len = len_v2(xy);
            float strength = alpha;

            strength *= BKE_brush_curve_strength_clamp(brush, len, radius);

            *m = (unsigned short)(65535.0f * strength);
        }
    }

    return mask;
}
예제 #2
0
BrushPainter *BKE_brush_painter_new(Scene *scene, Brush *brush)
{
	BrushPainter *painter = MEM_callocN(sizeof(BrushPainter), "BrushPainter");

	painter->brush = brush;
	painter->scene = scene;
	painter->firsttouch = 1;
	painter->cache.lastsize = -1; /* force ibuf create in refresh */

	painter->startsize = BKE_brush_size_get(scene, brush);
	painter->startalpha = BKE_brush_alpha_get(scene, brush);
	painter->startjitter = brush->jitter;
	painter->startspacing = brush->spacing;

	return painter;
}
예제 #3
0
static void brush_painter_refresh_cache(BrushPainter *painter, const float pos[2], int use_color_correction)
{
	const Scene *scene = painter->scene;
	Brush *brush = painter->brush;
	BrushPainterCache *cache = &painter->cache;
	MTex *mtex = &brush->mtex;
	int size;
	short flt;
	const int diameter = 2 * BKE_brush_size_get(scene, brush);
	const float alpha = BKE_brush_alpha_get(scene, brush);

	if (diameter != cache->lastsize ||
	    alpha != cache->lastalpha ||
	    brush->jitter != cache->lastjitter)
	{
		if (cache->ibuf) {
			IMB_freeImBuf(cache->ibuf);
			cache->ibuf = NULL;
		}
		if (cache->maskibuf) {
			IMB_freeImBuf(cache->maskibuf);
			cache->maskibuf = NULL;
		}

		flt = cache->flt;
		size = (cache->size) ? cache->size : diameter;

		if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) {
			BKE_brush_imbuf_new(scene, brush, flt, 3, size, &cache->maskibuf, use_color_correction);
			brush_painter_fixed_tex_partial_update(painter, pos);
		}
		else
			BKE_brush_imbuf_new(scene, brush, flt, 2, size, &cache->ibuf, use_color_correction);

		cache->lastsize = diameter;
		cache->lastalpha = alpha;
		cache->lastjitter = brush->jitter;
	}
	else if ((brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) && mtex && mtex->tex) {
		int dx = (int)painter->lastpaintpos[0] - (int)pos[0];
		int dy = (int)painter->lastpaintpos[1] - (int)pos[1];

		if ((dx != 0) || (dy != 0))
			brush_painter_fixed_tex_partial_update(painter, pos);
	}
}
예제 #4
0
static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const float lastpos[2], const float pos[2])
{
    ImagePaintState *s = ((ImagePaintState *)state);
    ImBuf *clonebuf = NULL, *frombuf, *tmpbuf = NULL;
    ImagePaintRegion region[4];
    short torus = s->brush->flag & BRUSH_TORUS;
    short blend = s->blend;
    float *offset = s->brush->clone.offset;
    float liftpos[2];
    float brush_alpha = BKE_brush_alpha_get(s->scene, s->brush);
    unsigned short mask_max = (unsigned short)(brush_alpha * 65535.0f);
    int bpos[2], blastpos[2], bliftpos[2];
    int a, tot;

    paint_2d_convert_brushco(ibufb, pos, bpos);

    /* lift from canvas */
    if (s->tool == PAINT_TOOL_SOFTEN) {
        paint_2d_lift_soften(s->canvas, ibufb, bpos, torus);
    }
    else if (s->tool == PAINT_TOOL_SMEAR) {
        if (lastpos[0] == pos[0] && lastpos[1] == pos[1])
            return 0;

        paint_2d_convert_brushco(ibufb, lastpos, blastpos);
        paint_2d_lift_smear(s->canvas, ibufb, blastpos);
    }
    else if (s->tool == PAINT_TOOL_CLONE && s->clonecanvas) {
        liftpos[0] = pos[0] - offset[0] * s->canvas->x;
        liftpos[1] = pos[1] - offset[1] * s->canvas->y;

        paint_2d_convert_brushco(ibufb, liftpos, bliftpos);
        clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos);
    }

    frombuf = (clonebuf) ? clonebuf : ibufb;

    if (torus) {
        paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
        tot = paint_2d_torus_split_region(region, s->canvas, frombuf);
    }
    else {
        paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
        tot = 1;
    }

    if (s->do_masking)
        tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);

    /* blend into canvas */
    for (a = 0; a < tot; a++) {
        ED_imapaint_dirty_region(s->image, s->canvas,
                                 region[a].destx, region[a].desty,
                                 region[a].width, region[a].height);

        if (s->do_masking) {
            /* masking, find original pixels tiles from undo buffer to composite over */
            int tilex, tiley, tilew, tileh, tx, ty;

            imapaint_region_tiles(s->canvas, region[a].destx, region[a].desty,
                                  region[a].width, region[a].height,
                                  &tilex, &tiley, &tilew, &tileh);

            for (ty = tiley; ty <= tileh; ty++) {
                for (tx = tilex; tx <= tilew; tx++) {
                    /* retrieve original pixels + mask from undo buffer */
                    unsigned short *mask;
                    int origx = region[a].destx - tx * IMAPAINT_TILE_SIZE;
                    int origy = region[a].desty - ty * IMAPAINT_TILE_SIZE;

                    if (s->canvas->rect_float)
                        tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask);
                    else
                        tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask);

                    IMB_rectblend(s->canvas, tmpbuf, frombuf, mask,
                                  maskb, mask_max,
                                  region[a].destx, region[a].desty,
                                  origx, origy,
                                  region[a].srcx, region[a].srcy,
                                  region[a].width, region[a].height, blend);
                }
            }
        }
        else {
            /* no masking, composite brush directly onto canvas */
            IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, NULL, 0,
                          region[a].destx, region[a].desty,
                          region[a].destx, region[a].desty,
                          region[a].srcx, region[a].srcy,
                          region[a].width, region[a].height, blend);
        }
    }

    if (clonebuf) IMB_freeImBuf(clonebuf);
    if (tmpbuf) IMB_freeImBuf(tmpbuf);

    return 1;
}
예제 #5
0
static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2])
{
    const Scene *scene = painter->scene;
    UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
    Brush *brush = painter->brush;
    BrushPainterCache *cache = &painter->cache;
    const int diameter = 2 * BKE_brush_size_get(scene, brush);
    const int size = (cache->size) ? cache->size : diameter;
    const float alpha = BKE_brush_alpha_get(scene, brush);
    const bool use_masking = painter->cache.use_masking;

    bool do_random = false;
    bool do_partial_update = false;
    bool do_view = false;
    float tex_rotation = -brush->mtex.rot;
    float mask_rotation = -brush->mask_mtex.rot;

    /* determine how can update based on textures used */
    if (painter->cache.is_texbrush) {
        if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) {
            do_view = true;
            tex_rotation += ups->brush_rotation;
        }
        else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM)
            do_random = true;
        else
            do_partial_update = true;

        brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, pos, mouse,
                                     brush->mtex.brush_map_mode, &painter->tex_mapping);
    }

    if (painter->cache.is_maskbrush) {
        if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) {
            do_view = true;
            mask_rotation += ups->brush_rotation;
        }
        else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM)
            do_random = true;
        else
            do_partial_update = true;

        brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, pos, mouse,
                                     brush->mask_mtex.brush_map_mode, &painter->mask_mapping);
    }

    if (do_view || do_random)
        do_partial_update = false;

    painter->pool = BKE_image_pool_new();

    /* detect if we need to recreate image brush buffer */
    if (diameter != cache->lastsize ||
            alpha != cache->lastalpha ||
            brush->jitter != cache->lastjitter ||
            tex_rotation != cache->last_tex_rotation ||
            mask_rotation != cache->last_mask_rotation ||
            do_random)
    {
        if (cache->ibuf) {
            IMB_freeImBuf(cache->ibuf);
            cache->ibuf = NULL;
        }
        if (cache->mask) {
            MEM_freeN(cache->mask);
            cache->mask = NULL;
        }

        if (do_partial_update) {
            /* do partial update of texture + recreate mask */
            cache->mask = brush_painter_mask_new(painter, size);
            brush_painter_imbuf_partial_update(painter, pos);
        }
        else {
            /* create brush and mask from scratch */
            if (use_masking)
                cache->mask = brush_painter_mask_new(painter, size);
            cache->ibuf = brush_painter_imbuf_new(painter, size);
        }

        cache->lastsize = diameter;
        cache->lastalpha = alpha;
        cache->lastjitter = brush->jitter;
        cache->last_tex_rotation = tex_rotation;
        cache->last_mask_rotation = mask_rotation;
    }
    else if (do_partial_update) {
        /* do only partial update of texture */
        int dx = (int)painter->lastpaintpos[0] - (int)pos[0];
        int dy = (int)painter->lastpaintpos[1] - (int)pos[1];

        if ((dx != 0) || (dy != 0)) {
            brush_painter_imbuf_partial_update(painter, pos);
        }
    }

    BKE_image_pool_free(painter->pool);
    painter->pool = NULL;
}
예제 #6
0
/* create imbuf with brush color */
static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size)
{
    Scene *scene = painter->scene;
    Brush *brush = painter->brush;

    rctf tex_mapping = painter->tex_mapping;
    rctf mask_mapping = painter->mask_mapping;
    struct ImagePool *pool = painter->pool;

    bool use_masking = painter->cache.use_masking;
    bool use_color_correction = painter->cache.use_color_correction;
    bool use_float = painter->cache.use_float;
    bool is_texbrush = painter->cache.is_texbrush;
    bool is_maskbrush = painter->cache.is_maskbrush;

    float alpha = (use_masking) ? 1.0f : BKE_brush_alpha_get(scene, brush);
    int radius = BKE_brush_size_get(scene, brush);
    int xoff = -size * 0.5f + 0.5f;
    int yoff = -size * 0.5f + 0.5f;

    int x, y, thread = 0;
    float brush_rgb[3];

    /* allocate image buffer */
    ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_rectfloat : IB_rect);

    /* get brush color */
    if (brush->imagepaint_tool == PAINT_TOOL_DRAW) {
        copy_v3_v3(brush_rgb, brush->rgb);

        if (use_color_correction)
            srgb_to_linearrgb_v3_v3(brush_rgb, brush_rgb);
    }
    else {
        brush_rgb[0] = 1.0f;
        brush_rgb[1] = 1.0f;
        brush_rgb[2] = 1.0f;
    }

    /* fill image buffer */
    for (y = 0; y < size; y++) {
        for (x = 0; x < size; x++) {
            /* sample texture and multiply with brush color */
            float texco[3], rgba[4];

            if (is_texbrush) {
                brush_imbuf_tex_co(&tex_mapping, x, y, texco);
                BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, pool);
                /* TODO(sergey): Support texture paint color space. */
                if (!use_float) {
                    linearrgb_to_srgb_v3_v3(rgba, rgba);
                }
                mul_v3_v3(rgba, brush_rgb);
            }
            else {
                copy_v3_v3(rgba, brush_rgb);
                rgba[3] = 1.0f;
            }

            if (is_maskbrush) {
                brush_imbuf_tex_co(&mask_mapping, x, y, texco);
                rgba[3] *= BKE_brush_sample_masktex(scene, brush, texco, thread, pool);
            }

            /* when not using masking, multiply in falloff and strength */
            if (!use_masking) {
                float xy[2] = {x + xoff, y + yoff};
                float len = len_v2(xy);

                rgba[3] *= alpha * BKE_brush_curve_strength_clamp(brush, len, radius);
            }

            if (use_float) {
                /* write to float pixel */
                float *dstf = ibuf->rect_float + (y * size + x) * 4;
                mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */
                dstf[3] = rgba[3];
            }
            else {
                /* write to byte pixel */
                unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4;

                rgb_float_to_uchar(dst, rgba);
                dst[3] = FTOCHAR(rgba[3]);
            }
        }
    }

    return ibuf;
}
예제 #7
0
static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile)
{
	bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0));
	float threshold = s->brush->sharp_threshold;
	int x, y, xi, yi, xo, yo, xk, yk;
	float count;
	int out_off[2], in_off[2], dim[2];
	int diff_pos[2];
	float outrgb[4];
	float rgba[4];
	BlurKernel *kernel = s->blurkernel;

	dim[0] = ibufb->x;
	dim[1] = ibufb->y;
	in_off[0] = pos[0];
	in_off[1] = pos[1];
	out_off[0] = out_off[1] = 0;

	if (!tile) {
		IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0],
		             &out_off[1], &dim[0], &dim[1]);

		if ((dim[0] == 0) || (dim[1] == 0))
			return;
	}

	/* find offset inside mask buffers to sample them */
	sub_v2_v2v2_int(diff_pos, out_off, in_off);

	for (y = 0; y < dim[1]; y++) {
		for (x = 0; x < dim[0]; x++) {
			/* get input pixel */
			xi = in_off[0] + x;
			yi = in_off[1] + y;

			count = 0.0;
			if (tile) {
				paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile);
				if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0)
					paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba);
				else
					zero_v4(rgba);
			}
			else {
				/* coordinates have been clipped properly here, it should be safe to do this */
				paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba);
			}
			zero_v4(outrgb);

			for (yk = 0; yk < kernel->side; yk++) {
				for (xk = 0; xk < kernel->side; xk++) {
					count += paint_2d_ibuf_add_if(ibuf, xi + xk - kernel->pixel_len,
					                               yi + yk - kernel->pixel_len, outrgb, tile,
					                               kernel->wdata[xk + yk * kernel->side]);
				}
			}

			if (count > 0.0f) {
				mul_v4_fl(outrgb, 1.0f / (float)count);

				if (sharpen) {
					/* subtract blurred image from normal image gives high pass filter */
					sub_v3_v3v3(outrgb, rgba, outrgb);

					/* now rgba_ub contains the edge result, but this should be converted to luminance to avoid
					 * colored speckles appearing in final image, and also to check for threshold */
					outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb);
					if (fabsf(outrgb[0]) > threshold) {
						float mask = BKE_brush_alpha_get(s->scene, s->brush);
						float alpha = rgba[3];
						rgba[3] = outrgb[3] = mask;

						/* add to enhance edges */
						blend_color_add_float(outrgb, rgba, outrgb);
						outrgb[3] = alpha;
					}
					else
						copy_v4_v4(outrgb, rgba);
				}
			}
			else
				copy_v4_v4(outrgb, rgba);
			/* write into brush buffer */
			xo = out_off[0] + x;
			yo = out_off[1] + y;
			paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb);
		}
	}
}
예제 #8
0
/* TODO, use define for 'texfall' arg */
void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texfall, int bufsize, ImBuf **outbuf, int use_color_correction)
{
    ImBuf *ibuf;
    float xy[2], rgba[4], *dstf;
    int x, y, rowbytes, xoff, yoff, imbflag;
    const int radius = BKE_brush_size_get(scene, brush);
    unsigned char *dst, crgb[3];
    const float alpha = BKE_brush_alpha_get(scene, brush);
    float brush_rgb[3];

    imbflag = (flt) ? IB_rectfloat : IB_rect;
    xoff = -bufsize / 2.0f + 0.5f;
    yoff = -bufsize / 2.0f + 0.5f;
    rowbytes = bufsize * 4;

    if (*outbuf)
        ibuf = *outbuf;
    else
        ibuf = IMB_allocImBuf(bufsize, bufsize, 32, imbflag);

    if (flt) {
        copy_v3_v3(brush_rgb, brush->rgb);
        if (use_color_correction) {
            srgb_to_linearrgb_v3_v3(brush_rgb, brush_rgb);
        }

        for (y = 0; y < ibuf->y; y++) {
            dstf = ibuf->rect_float + y * rowbytes;

            for (x = 0; x < ibuf->x; x++, dstf += 4) {
                xy[0] = x + xoff;
                xy[1] = y + yoff;

                if (texfall == 0) {
                    copy_v3_v3(dstf, brush_rgb);
                    dstf[3] = alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius);
                }
                else if (texfall == 1) {
                    BKE_brush_sample_tex(scene, brush, xy, dstf, 0);
                }
                else {
                    BKE_brush_sample_tex(scene, brush, xy, rgba, 0);
                    mul_v3_v3v3(dstf, rgba, brush_rgb);
                    dstf[3] = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius);
                }
            }
        }
    }
    else {
        float alpha_f; /* final float alpha to convert to char */
        rgb_float_to_uchar(crgb, brush->rgb);

        for (y = 0; y < ibuf->y; y++) {
            dst = (unsigned char *)ibuf->rect + y * rowbytes;

            for (x = 0; x < ibuf->x; x++, dst += 4) {
                xy[0] = x + xoff;
                xy[1] = y + yoff;

                if (texfall == 0) {
                    alpha_f = alpha * BKE_brush_curve_strength(brush, len_v2(xy), radius);

                    dst[0] = crgb[0];
                    dst[1] = crgb[1];
                    dst[2] = crgb[2];
                    dst[3] = FTOCHAR(alpha_f);
                }
                else if (texfall == 1) {
                    BKE_brush_sample_tex(scene, brush, xy, rgba, 0);
                    rgba_float_to_uchar(dst, rgba);
                }
                else if (texfall == 2) {
                    BKE_brush_sample_tex(scene, brush, xy, rgba, 0);
                    mul_v3_v3(rgba, brush->rgb);
                    alpha_f = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius);

                    rgb_float_to_uchar(dst, rgba);

                    dst[3] = FTOCHAR(alpha_f);
                }
                else {
                    BKE_brush_sample_tex(scene, brush, xy, rgba, 0);
                    alpha_f = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius);

                    dst[0] = crgb[0];
                    dst[1] = crgb[1];
                    dst[2] = crgb[2];
                    dst[3] = FTOCHAR(alpha_f);
                }
            }
        }
    }

    *outbuf = ibuf;
}