/* update the brush image by trying to reuse the cached texture result. this * can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2]) { const Scene *scene = painter->scene; Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; ImBuf *oldtexibuf, *ibuf; int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; int diameter = 2 * BKE_brush_size_get(scene, brush); /* create brush image buffer if it didn't exist yet */ imbflag = (cache->use_float) ? IB_rectfloat : IB_rect; if (!cache->ibuf) cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); ibuf = cache->ibuf; /* create new texture image buffer with coordinates relative to old */ oldtexibuf = cache->texibuf; cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); if (oldtexibuf) { srcx = srcy = 0; destx = (int)painter->lastpaintpos[0] - (int)pos[0]; desty = (int)painter->lastpaintpos[1] - (int)pos[1]; w = oldtexibuf->x; h = oldtexibuf->y; IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); } else { srcx = srcy = 0; destx = desty = 0; w = h = 0; } x1 = destx; y1 = desty; x2 = min_ii(destx + w, ibuf->x); y2 = min_ii(desty + h, ibuf->y); /* blend existing texture in new position */ if ((x1 < x2) && (y1 < y2)) brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy); if (oldtexibuf) IMB_freeImBuf(oldtexibuf); /* sample texture in new areas */ if ((0 < x1) && (0 < ibuf->y)) brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0); if ((x2 < ibuf->x) && (0 < ibuf->y)) brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); if ((x1 < x2) && (0 < y1)) brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0); if ((x1 < x2) && (y2 < ibuf->y)) brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); }
static void brush_painter_fixed_tex_partial_update(BrushPainter *painter, const float pos[2]) { const Scene *scene = painter->scene; Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; ImBuf *oldtexibuf, *ibuf; int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; const int diameter = 2 * BKE_brush_size_get(scene, brush); imbflag = (cache->flt) ? IB_rectfloat : IB_rect; if (!cache->ibuf) cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); ibuf = cache->ibuf; oldtexibuf = cache->texibuf; cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); if (oldtexibuf) { srcx = srcy = 0; destx = (int)painter->lastpaintpos[0] - (int)pos[0]; desty = (int)painter->lastpaintpos[1] - (int)pos[1]; w = oldtexibuf->x; h = oldtexibuf->y; IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); } else { srcx = srcy = 0; destx = desty = 0; w = h = 0; } x1 = destx; y1 = desty; x2 = destx + w; y2 = desty + h; /* blend existing texture in new position */ if ((x1 < x2) && (y1 < y2)) brush_painter_do_partial(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy, pos); if (oldtexibuf) IMB_freeImBuf(oldtexibuf); /* sample texture in new areas */ if ((0 < x1) && (0 < ibuf->y)) brush_painter_do_partial(painter, NULL, 0, 0, x1, ibuf->y, 0, 0, pos); if ((x2 < ibuf->x) && (0 < ibuf->y)) brush_painter_do_partial(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0, pos); if ((x1 < x2) && (0 < y1)) brush_painter_do_partial(painter, NULL, x1, 0, x2, y1, 0, 0, pos); if ((x1 < x2) && (y2 < ibuf->y)) brush_painter_do_partial(painter, NULL, x1, y2, x2, ibuf->y, 0, 0, pos); }
static void paint_2d_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, const bool is_torus) { int x, y, count, xi, yi, xo, yo; int out_off[2], in_off[2], dim[2]; float outrgb[4]; 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 (!is_torus) { 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; } 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 = 1; paint_2d_ibuf_rgb_get(ibuf, xi, yi, is_torus, outrgb); count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi - 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi + 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi, yi - 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi, yi + 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi - 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi + 1, outrgb, is_torus); mul_v4_fl(outrgb, 1.0f / (float)count); /* write into brush buffer */ xo = out_off[0] + x; yo = out_off[1] + y; paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); } } }
static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) { /* note: allocImbuf returns zero'd memory, so regions outside image will * have zero alpha, and hence not be blended onto the image */ int w = ibufb->x, h = ibufb->y, destx = 0, desty = 0, srcx = pos[0], srcy = pos[1]; ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags); IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h); IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, IMB_BLEND_COPY_ALPHA); IMB_rectblend(clonebuf, clonebuf, ibuf, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, IMB_BLEND_COPY_RGB); return clonebuf; }
static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf, short tile) { int destx = region->destx; int desty = region->desty; int srcx = region->srcx; int srcy = region->srcy; int width = region->width; int height = region->height; int origw, origh, w, h, tot = 0; /* convert destination and source coordinates to be within image */ if (tile & PAINT_TILE_X) { destx = destx % dbuf->x; if (destx < 0) destx += dbuf->x; srcx = srcx % sbuf->x; if (srcx < 0) srcx += sbuf->x; } if (tile & PAINT_TILE_Y) { desty = desty % dbuf->y; if (desty < 0) desty += dbuf->y; srcy = srcy % sbuf->y; if (srcy < 0) srcy += sbuf->y; } /* clip width of blending area to destination imbuf, to avoid writing the * same pixel twice */ origw = w = (width > dbuf->x) ? dbuf->x : width; origh = h = (height > dbuf->y) ? dbuf->y : height; /* clip within image */ IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &w, &h); paint_2d_set_region(®ion[tot++], destx, desty, srcx, srcy, w, h); /* do 3 other rects if needed */ if ((tile & PAINT_TILE_X) && w < origw) paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h); if ((tile & PAINT_TILE_Y) && h < origh) paint_2d_set_region(®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h); if ((tile & PAINT_TILE_X) && (tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, (desty + h) % dbuf->y, (srcx + w) % sbuf->x, (srcy + h) % sbuf->y, origw - w, origh - h); return tot; }
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); } } }
/** * Update the brush mask image by trying to reuse the cached texture result. * This can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) { BrushPainterCache *cache = &painter->cache; unsigned short *tex_mask_old; int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; /* create brush image buffer if it didn't exist yet */ if (!cache->tex_mask) cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); /* create new texture image buffer with coordinates relative to old */ tex_mask_old = cache->tex_mask_old; cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); if (tex_mask_old) { ImBuf maskibuf; ImBuf maskibuf_old; maskibuf.x = maskibuf.y = diameter; maskibuf_old.x = cache->tex_mask_old_w; maskibuf_old.y = cache->tex_mask_old_h; srcx = srcy = 0; w = cache->tex_mask_old_w; h = cache->tex_mask_old_h; destx = (int)painter->lastpaintpos[0] - (int)pos[0] + (diameter / 2 - w / 2); desty = (int)painter->lastpaintpos[1] - (int)pos[1] + (diameter / 2 - h / 2); /* hack, use temporary rects so that clipping works */ IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); } else { srcx = srcy = 0; destx = desty = 0; w = h = 0; } x1 = min_ii(destx, diameter); y1 = min_ii(desty, diameter); x2 = min_ii(destx + w, diameter); y2 = min_ii(desty + h, diameter); /* blend existing texture in new position */ if ((x1 < x2) && (y1 < y2)) brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); if (tex_mask_old) MEM_freeN(tex_mask_old); /* sample texture in new areas */ if ((0 < x1) && (0 < diameter)) brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter); if ((x2 < diameter) && (0 < diameter)) brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter); if ((x1 < x2) && (0 < y1)) brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter); if ((x1 < x2) && (y2 < diameter)) brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter); /* through with sampling, now update sizes */ cache->tex_mask_old_w = diameter; cache->tex_mask_old_h = diameter; }
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; 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 { 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; } } } 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); 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; } } } }