2013-01-01 21:23:12 +00:00
|
|
|
/*
|
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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,
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* 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 *****
|
|
|
|
*/
|
|
|
|
|
2013-01-02 01:49:07 +00:00
|
|
|
/** \file blender/editors/sculpt_paint/paint_image_2d.c
|
2013-01-01 21:23:12 +00:00
|
|
|
* \ingroup bke
|
|
|
|
*/
|
|
|
|
//#include <math.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "DNA_brush_types.h"
|
|
|
|
#include "DNA_scene_types.h"
|
2013-03-07 12:11:38 +00:00
|
|
|
#include "DNA_space_types.h"
|
|
|
|
#include "DNA_object_types.h"
|
2013-01-01 21:23:12 +00:00
|
|
|
|
2013-03-09 05:35:49 +00:00
|
|
|
#include "BLI_math.h"
|
2013-04-30 09:59:40 +00:00
|
|
|
#include "BLI_rect.h"
|
2013-03-09 05:35:49 +00:00
|
|
|
|
2013-03-07 12:11:38 +00:00
|
|
|
#include "BKE_context.h"
|
2013-01-01 21:23:12 +00:00
|
|
|
#include "BKE_brush.h"
|
2013-03-07 12:11:38 +00:00
|
|
|
#include "BKE_main.h"
|
|
|
|
#include "BKE_image.h"
|
|
|
|
#include "BKE_paint.h"
|
|
|
|
#include "BKE_report.h"
|
|
|
|
|
|
|
|
#include "ED_screen.h"
|
2013-01-01 21:23:12 +00:00
|
|
|
|
|
|
|
#include "IMB_imbuf.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
2013-03-07 12:11:38 +00:00
|
|
|
#include "IMB_colormanagement.h"
|
|
|
|
|
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
|
|
|
#include "UI_view2d.h"
|
2013-01-01 21:23:12 +00:00
|
|
|
|
|
|
|
#include "RE_shader_ext.h"
|
|
|
|
|
2013-03-07 12:11:38 +00:00
|
|
|
#include "GPU_draw.h"
|
|
|
|
|
|
|
|
#include "paint_intern.h"
|
|
|
|
|
|
|
|
/* Brush Painting for 2D image editor */
|
|
|
|
|
|
|
|
/* Defines and Structs */
|
|
|
|
/* FTOCHAR as inline function */
|
|
|
|
BLI_INLINE unsigned char f_to_char(const float val)
|
|
|
|
{
|
|
|
|
return FTOCHAR(val);
|
|
|
|
}
|
|
|
|
#define IMAPAINT_FLOAT_RGB_TO_CHAR(c, f) { \
|
|
|
|
(c)[0] = f_to_char((f)[0]); \
|
|
|
|
(c)[1] = f_to_char((f)[1]); \
|
|
|
|
(c)[2] = f_to_char((f)[2]); \
|
|
|
|
} (void)0
|
|
|
|
|
|
|
|
#define IMAPAINT_CHAR_RGB_TO_FLOAT(f, c) { \
|
2013-03-24 01:19:55 +00:00
|
|
|
(f)[0] = IMAPAINT_CHAR_TO_FLOAT((c)[0]); \
|
|
|
|
(f)[1] = IMAPAINT_CHAR_TO_FLOAT((c)[1]); \
|
|
|
|
(f)[2] = IMAPAINT_CHAR_TO_FLOAT((c)[2]); \
|
2013-03-07 12:11:38 +00:00
|
|
|
} (void)0
|
|
|
|
|
|
|
|
#define IMAPAINT_FLOAT_RGB_COPY(a, b) copy_v3_v3(a, b)
|
|
|
|
|
2013-03-10 18:05:13 +00:00
|
|
|
typedef struct BrushPainterCache {
|
|
|
|
int size; /* size override, if 0 uses 2*BKE_brush_size_get(brush) */
|
|
|
|
short flt; /* need float imbuf? */
|
|
|
|
|
|
|
|
int lastsize;
|
|
|
|
float lastalpha;
|
|
|
|
float lastjitter;
|
2013-03-15 09:48:51 +00:00
|
|
|
float last_rotation;
|
2013-03-10 18:05:13 +00:00
|
|
|
|
|
|
|
ImBuf *ibuf;
|
|
|
|
ImBuf *texibuf;
|
|
|
|
ImBuf *maskibuf;
|
|
|
|
} BrushPainterCache;
|
|
|
|
|
|
|
|
typedef struct BrushPainter {
|
|
|
|
Scene *scene;
|
|
|
|
Brush *brush;
|
|
|
|
|
|
|
|
float lastpaintpos[2]; /* position of last paint op */
|
|
|
|
float startpaintpos[2]; /* position of first paint */
|
|
|
|
|
|
|
|
short firsttouch; /* first paint op */
|
|
|
|
|
2013-04-30 09:59:40 +00:00
|
|
|
struct ImagePool *pool; /* image pool */
|
|
|
|
rctf mapping; /* texture coordinate mapping */
|
|
|
|
|
2013-03-10 18:05:13 +00:00
|
|
|
BrushPainterCache cache;
|
|
|
|
} BrushPainter;
|
|
|
|
|
2013-03-07 12:11:38 +00:00
|
|
|
typedef struct ImagePaintRegion {
|
|
|
|
int destx, desty;
|
|
|
|
int srcx, srcy;
|
|
|
|
int width, height;
|
|
|
|
} ImagePaintRegion;
|
|
|
|
|
|
|
|
typedef struct ImagePaintState {
|
|
|
|
BrushPainter *painter;
|
|
|
|
SpaceImage *sima;
|
|
|
|
View2D *v2d;
|
|
|
|
Scene *scene;
|
|
|
|
bScreen *screen;
|
2013-04-30 09:59:40 +00:00
|
|
|
struct ImagePool *image_pool;
|
2013-03-07 12:11:38 +00:00
|
|
|
|
|
|
|
Brush *brush;
|
|
|
|
short tool, blend;
|
|
|
|
Image *image;
|
|
|
|
ImBuf *canvas;
|
|
|
|
ImBuf *clonecanvas;
|
|
|
|
char *warnpackedfile;
|
|
|
|
char *warnmultifile;
|
|
|
|
|
2013-04-30 06:07:42 +00:00
|
|
|
bool do_masking;
|
|
|
|
|
2013-03-07 12:11:38 +00:00
|
|
|
/* viewport texture paint only, but _not_ project paint */
|
|
|
|
Object *ob;
|
|
|
|
int faceindex;
|
|
|
|
float uv[2];
|
|
|
|
int do_facesel;
|
|
|
|
} ImagePaintState;
|
2013-01-01 21:23:12 +00:00
|
|
|
|
|
|
|
|
2013-03-10 18:05:13 +00:00
|
|
|
static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush)
|
2013-01-01 21:23:12 +00:00
|
|
|
{
|
|
|
|
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 */
|
|
|
|
|
|
|
|
return painter;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-29 15:18:22 +00:00
|
|
|
static void brush_painter_2d_require_imbuf(BrushPainter *painter, short flt, int size)
|
2013-01-01 21:23:12 +00:00
|
|
|
{
|
2013-03-30 09:57:35 +00:00
|
|
|
if ((painter->cache.flt != flt) || (painter->cache.size != size)) {
|
2013-01-01 21:23:12 +00:00
|
|
|
if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf);
|
|
|
|
if (painter->cache.maskibuf) IMB_freeImBuf(painter->cache.maskibuf);
|
|
|
|
painter->cache.ibuf = painter->cache.maskibuf = NULL;
|
|
|
|
painter->cache.lastsize = -1; /* force ibuf create in refresh */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (painter->cache.flt != flt) {
|
|
|
|
if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf);
|
|
|
|
painter->cache.texibuf = NULL;
|
|
|
|
painter->cache.lastsize = -1; /* force ibuf create in refresh */
|
|
|
|
}
|
|
|
|
|
|
|
|
painter->cache.size = size;
|
|
|
|
painter->cache.flt = flt;
|
|
|
|
}
|
|
|
|
|
2013-03-10 18:05:13 +00:00
|
|
|
static void brush_painter_2d_free(BrushPainter *painter)
|
2013-01-01 21:23:12 +00:00
|
|
|
{
|
|
|
|
if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf);
|
|
|
|
if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf);
|
|
|
|
if (painter->cache.maskibuf) IMB_freeImBuf(painter->cache.maskibuf);
|
|
|
|
MEM_freeN(painter);
|
|
|
|
}
|
|
|
|
|
2013-03-07 12:11:38 +00:00
|
|
|
static void brush_painter_2d_do_partial(BrushPainter *painter, ImBuf *oldtexibuf,
|
2013-01-01 21:23:12 +00:00
|
|
|
int x, int y, int w, int h, int xt, int yt,
|
|
|
|
const float pos[2])
|
|
|
|
{
|
|
|
|
Scene *scene = painter->scene;
|
|
|
|
Brush *brush = painter->brush;
|
|
|
|
ImBuf *ibuf, *maskibuf, *texibuf;
|
2013-04-30 09:59:40 +00:00
|
|
|
float *bf, *mf, *tf, *otf = NULL, texco[3], rgba[4];
|
2013-01-01 21:23:12 +00:00
|
|
|
unsigned char *b, *m, *t, *ot = NULL;
|
|
|
|
int dotexold, origx = x, origy = y;
|
2013-04-30 09:59:40 +00:00
|
|
|
int thread = 0;
|
|
|
|
rctf mapping = painter->mapping;
|
2013-01-01 21:23:12 +00:00
|
|
|
|
2013-04-30 09:59:40 +00:00
|
|
|
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) {
|
|
|
|
mapping.xmin += (int)pos[0] - (int)painter->startpaintpos[0];
|
|
|
|
mapping.ymin += (int)pos[1] - (int)painter->startpaintpos[1];
|
|
|
|
}
|
2013-01-01 21:23:12 +00:00
|
|
|
|
|
|
|
ibuf = painter->cache.ibuf;
|
|
|
|
texibuf = painter->cache.texibuf;
|
|
|
|
maskibuf = painter->cache.maskibuf;
|
|
|
|
|
|
|
|
dotexold = (oldtexibuf != NULL);
|
|
|
|
|
|
|
|
/* not sure if it's actually needed or it's a mistake in coords/sizes
|
|
|
|
* calculation in brush_painter_fixed_tex_partial_update(), but without this
|
|
|
|
* limitation memory gets corrupted at fast strokes with quite big spacing (sergey) */
|
|
|
|
w = min_ii(w, ibuf->x);
|
|
|
|
h = min_ii(h, ibuf->y);
|
|
|
|
|
|
|
|
if (painter->cache.flt) {
|
|
|
|
for (; y < h; y++) {
|
|
|
|
bf = ibuf->rect_float + (y * ibuf->x + origx) * 4;
|
|
|
|
tf = texibuf->rect_float + (y * texibuf->x + origx) * 4;
|
|
|
|
mf = maskibuf->rect_float + (y * maskibuf->x + origx) * 4;
|
|
|
|
|
|
|
|
if (dotexold)
|
|
|
|
otf = oldtexibuf->rect_float + ((y - origy + yt) * oldtexibuf->x + xt) * 4;
|
|
|
|
|
|
|
|
for (x = origx; x < w; x++, bf += 4, mf += 4, tf += 4) {
|
|
|
|
if (dotexold) {
|
2013-02-14 15:44:09 +00:00
|
|
|
copy_v4_v4(tf, otf);
|
2013-01-01 21:23:12 +00:00
|
|
|
otf += 4;
|
|
|
|
}
|
|
|
|
else {
|
2013-04-30 09:59:40 +00:00
|
|
|
texco[0] = mapping.xmin + x*mapping.xmax;
|
|
|
|
texco[1] = mapping.ymin + y*mapping.ymax;
|
|
|
|
texco[2] = 0.0f;
|
2013-01-01 21:23:12 +00:00
|
|
|
|
2013-04-30 09:59:40 +00:00
|
|
|
BKE_brush_sample_tex_3D(scene, brush, texco, tf, thread, painter->pool);
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
|
|
|
|
2013-04-27 12:51:23 +00:00
|
|
|
/* output premultiplied float image, mf was already premultiplied */
|
|
|
|
bf[0] = tf[0] * tf[3] * mf[0];
|
|
|
|
bf[1] = tf[1] * tf[3] * mf[1];
|
|
|
|
bf[2] = tf[2] * tf[3] * mf[2];
|
|
|
|
bf[3] = tf[3] * tf[3] * mf[3];
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (; y < h; y++) {
|
|
|
|
b = (unsigned char *)ibuf->rect + (y * ibuf->x + origx) * 4;
|
|
|
|
t = (unsigned char *)texibuf->rect + (y * texibuf->x + origx) * 4;
|
|
|
|
m = (unsigned char *)maskibuf->rect + (y * maskibuf->x + origx) * 4;
|
|
|
|
|
|
|
|
if (dotexold)
|
|
|
|
ot = (unsigned char *)oldtexibuf->rect + ((y - origy + yt) * oldtexibuf->x + xt) * 4;
|
|
|
|
|
|
|
|
for (x = origx; x < w; x++, b += 4, m += 4, t += 4) {
|
|
|
|
if (dotexold) {
|
|
|
|
t[0] = ot[0];
|
|
|
|
t[1] = ot[1];
|
|
|
|
t[2] = ot[2];
|
|
|
|
t[3] = ot[3];
|
|
|
|
ot += 4;
|
|
|
|
}
|
|
|
|
else {
|
2013-04-30 09:59:40 +00:00
|
|
|
texco[0] = mapping.xmin + x*mapping.xmax;
|
|
|
|
texco[1] = mapping.ymin + y*mapping.ymax;
|
|
|
|
texco[2] = 0.0f;
|
2013-01-01 21:23:12 +00:00
|
|
|
|
2013-04-30 09:59:40 +00:00
|
|
|
BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, painter->pool);
|
2013-01-01 21:23:12 +00:00
|
|
|
rgba_float_to_uchar(t, rgba);
|
|
|
|
}
|
|
|
|
|
|
|
|
b[0] = t[0] * m[0] / 255;
|
|
|
|
b[1] = t[1] * m[1] / 255;
|
|
|
|
b[2] = t[2] * m[2] / 255;
|
|
|
|
b[3] = t[3] * m[3] / 255;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-07 12:11:38 +00:00
|
|
|
static void brush_painter_2d_tiled_tex_partial_update(BrushPainter *painter, const float pos[2])
|
2013-01-01 21:23:12 +00:00
|
|
|
{
|
|
|
|
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))
|
2013-03-07 12:11:38 +00:00
|
|
|
brush_painter_2d_do_partial(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy, pos);
|
2013-01-01 21:23:12 +00:00
|
|
|
|
|
|
|
if (oldtexibuf)
|
|
|
|
IMB_freeImBuf(oldtexibuf);
|
|
|
|
|
|
|
|
/* sample texture in new areas */
|
|
|
|
if ((0 < x1) && (0 < ibuf->y))
|
2013-03-07 12:11:38 +00:00
|
|
|
brush_painter_2d_do_partial(painter, NULL, 0, 0, x1, ibuf->y, 0, 0, pos);
|
2013-01-01 21:23:12 +00:00
|
|
|
if ((x2 < ibuf->x) && (0 < ibuf->y))
|
2013-03-07 12:11:38 +00:00
|
|
|
brush_painter_2d_do_partial(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0, pos);
|
2013-01-01 21:23:12 +00:00
|
|
|
if ((x1 < x2) && (0 < y1))
|
2013-03-07 12:11:38 +00:00
|
|
|
brush_painter_2d_do_partial(painter, NULL, x1, 0, x2, y1, 0, 0, pos);
|
2013-01-01 21:23:12 +00:00
|
|
|
if ((x1 < x2) && (y2 < ibuf->y))
|
2013-03-07 12:11:38 +00:00
|
|
|
brush_painter_2d_do_partial(painter, NULL, x1, y2, x2, ibuf->y, 0, 0, pos);
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
|
|
|
|
2013-04-30 09:59:40 +00:00
|
|
|
static void brush_painter_2d_tex_mapping(ImagePaintState *s, int bufsize, const float pos[2], bool do_stencil, bool do_3D, rctf *mapping)
|
|
|
|
{
|
|
|
|
float invw = 1.0f/(float)s->canvas->x;
|
|
|
|
float invh = 1.0f/(float)s->canvas->y;
|
|
|
|
int xmin, ymin, xmax, ymax;
|
|
|
|
int ipos[2];
|
|
|
|
|
|
|
|
/* find start coordinate of brush in canvas */
|
|
|
|
ipos[0] = (int)floorf((pos[0] - bufsize / 2) + 1.0f);
|
|
|
|
ipos[1] = (int)floorf((pos[1] - bufsize / 2) + 1.0f);
|
|
|
|
|
|
|
|
if (do_stencil) {
|
|
|
|
/* map from view coordinates of brush to region coordinates */
|
|
|
|
UI_view2d_to_region_no_clip(s->v2d, ipos[0]*invw, ipos[1]*invh, &xmin, &ymin);
|
|
|
|
UI_view2d_to_region_no_clip(s->v2d, (ipos[0] + bufsize)*invw, (ipos[1] + bufsize)*invh, &xmax, &ymax);
|
|
|
|
|
|
|
|
/* output mapping from brush ibuf x/y to region coordinates */
|
|
|
|
mapping->xmin = xmin;
|
|
|
|
mapping->ymin = ymin;
|
|
|
|
mapping->xmax = (xmax - xmin)/(float)bufsize;
|
|
|
|
mapping->ymax = (ymax - ymin)/(float)bufsize;
|
|
|
|
}
|
|
|
|
else if (do_3D) {
|
|
|
|
/* 3D mapping, just mapping to canvas 0..1 */
|
|
|
|
mapping->xmin = ipos[0]*invw;
|
|
|
|
mapping->ymin = ipos[1]*invh;
|
|
|
|
mapping->xmax = bufsize*invw/(float)bufsize;
|
|
|
|
mapping->ymax = bufsize*invh/(float)bufsize;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* other mapping */
|
|
|
|
mapping->xmin = -bufsize * 0.5f + 0.5f;
|
|
|
|
mapping->ymin = -bufsize * 0.5f + 0.5f;
|
|
|
|
mapping->xmax = 1.0f;
|
|
|
|
mapping->ymax = 1.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], bool use_color_correction)
|
2013-01-01 21:23:12 +00:00
|
|
|
{
|
|
|
|
const Scene *scene = painter->scene;
|
2013-03-15 09:48:51 +00:00
|
|
|
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
|
2013-01-01 21:23:12 +00:00
|
|
|
Brush *brush = painter->brush;
|
|
|
|
BrushPainterCache *cache = &painter->cache;
|
|
|
|
MTex *mtex = &brush->mtex;
|
|
|
|
const int diameter = 2 * BKE_brush_size_get(scene, brush);
|
2013-04-30 09:59:40 +00:00
|
|
|
const int size = (cache->size) ? cache->size : diameter;
|
|
|
|
const short flt = cache->flt;
|
2013-01-01 21:23:12 +00:00
|
|
|
const float alpha = BKE_brush_alpha_get(scene, brush);
|
2013-04-30 09:59:40 +00:00
|
|
|
const bool do_3D = brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D;
|
|
|
|
const bool do_tiled = brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED;
|
|
|
|
const bool do_stencil = brush->mtex.brush_map_mode == MTEX_MAP_MODE_STENCIL;
|
2013-03-26 21:34:39 +00:00
|
|
|
const bool do_random = brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM;
|
2013-04-30 09:59:40 +00:00
|
|
|
const bool do_view = brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW;
|
|
|
|
const bool use_brush_alpha = !s->do_masking;
|
2013-03-15 09:48:51 +00:00
|
|
|
float rotation = -mtex->rot;
|
|
|
|
|
2013-04-30 09:59:40 +00:00
|
|
|
if (do_view) {
|
2013-03-15 09:48:51 +00:00
|
|
|
rotation += ups->brush_rotation;
|
|
|
|
}
|
2013-01-01 21:23:12 +00:00
|
|
|
|
2013-04-30 09:59:40 +00:00
|
|
|
brush_painter_2d_tex_mapping(s, size, pos, do_stencil, do_3D, &painter->mapping);
|
|
|
|
|
|
|
|
painter->pool = BKE_image_pool_new();
|
|
|
|
|
2013-01-01 21:23:12 +00:00
|
|
|
if (diameter != cache->lastsize ||
|
2013-04-30 06:07:42 +00:00
|
|
|
(use_brush_alpha && alpha != cache->lastalpha) ||
|
2013-03-15 09:48:51 +00:00
|
|
|
brush->jitter != cache->lastjitter ||
|
2013-03-26 21:34:39 +00:00
|
|
|
rotation != cache->last_rotation ||
|
|
|
|
do_random)
|
2013-01-01 21:23:12 +00:00
|
|
|
{
|
|
|
|
if (cache->ibuf) {
|
|
|
|
IMB_freeImBuf(cache->ibuf);
|
|
|
|
cache->ibuf = NULL;
|
|
|
|
}
|
|
|
|
if (cache->maskibuf) {
|
|
|
|
IMB_freeImBuf(cache->maskibuf);
|
|
|
|
cache->maskibuf = NULL;
|
|
|
|
}
|
|
|
|
|
2013-04-30 09:59:40 +00:00
|
|
|
if (do_tiled || do_3D || do_stencil) {
|
2013-04-30 06:07:42 +00:00
|
|
|
BKE_brush_imbuf_new(scene, brush, flt, 3, size, &cache->maskibuf,
|
2013-04-30 09:59:40 +00:00
|
|
|
use_color_correction, use_brush_alpha,
|
|
|
|
painter->pool, &painter->mapping);
|
2013-03-07 12:11:38 +00:00
|
|
|
brush_painter_2d_tiled_tex_partial_update(painter, pos);
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
|
|
|
else
|
2013-04-30 06:07:42 +00:00
|
|
|
BKE_brush_imbuf_new(scene, brush, flt, 2, size, &cache->ibuf,
|
2013-04-30 09:59:40 +00:00
|
|
|
use_color_correction, use_brush_alpha,
|
|
|
|
painter->pool, &painter->mapping);
|
2013-01-01 21:23:12 +00:00
|
|
|
|
|
|
|
cache->lastsize = diameter;
|
|
|
|
cache->lastalpha = alpha;
|
|
|
|
cache->lastjitter = brush->jitter;
|
2013-03-15 09:48:51 +00:00
|
|
|
cache->last_rotation = rotation;
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
2013-04-30 09:59:40 +00:00
|
|
|
else if ((do_tiled || do_3D || do_stencil) && mtex && mtex->tex) {
|
2013-01-01 21:23:12 +00:00
|
|
|
int dx = (int)painter->lastpaintpos[0] - (int)pos[0];
|
|
|
|
int dy = (int)painter->lastpaintpos[1] - (int)pos[1];
|
|
|
|
|
|
|
|
if ((dx != 0) || (dy != 0))
|
2013-03-07 12:11:38 +00:00
|
|
|
brush_painter_2d_tiled_tex_partial_update(painter, pos);
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
2013-04-30 09:59:40 +00:00
|
|
|
|
|
|
|
BKE_image_pool_free(painter->pool);
|
|
|
|
painter->pool = NULL;
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
|
|
|
|
2013-03-07 12:11:38 +00:00
|
|
|
/* keep these functions in sync */
|
|
|
|
static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, const short is_torus, float r_rgb[3])
|
2013-01-01 21:23:12 +00:00
|
|
|
{
|
2013-03-07 12:11:38 +00:00
|
|
|
if (is_torus) {
|
|
|
|
x %= ibuf->x;
|
|
|
|
if (x < 0) x += ibuf->x;
|
|
|
|
y %= ibuf->y;
|
|
|
|
if (y < 0) y += ibuf->y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ibuf->rect_float) {
|
|
|
|
float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4;
|
|
|
|
IMAPAINT_FLOAT_RGB_COPY(r_rgb, rrgbf);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
char *rrgb = (char *)ibuf->rect + (ibuf->x * y + x) * 4;
|
|
|
|
IMAPAINT_CHAR_RGB_TO_FLOAT(r_rgb, rrgb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void paint_2d_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const short is_torus, const float rgb[3])
|
|
|
|
{
|
|
|
|
if (is_torus) {
|
|
|
|
x %= ibuf->x;
|
|
|
|
if (x < 0) x += ibuf->x;
|
|
|
|
y %= ibuf->y;
|
|
|
|
if (y < 0) y += ibuf->y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ibuf->rect_float) {
|
|
|
|
float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4;
|
|
|
|
IMAPAINT_FLOAT_RGB_COPY(rrgbf, rgb);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
char *rrgb = (char *)ibuf->rect + (ibuf->x * y + x) * 4;
|
|
|
|
IMAPAINT_FLOAT_RGB_TO_CHAR(rrgb, rgb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int paint_2d_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, float *outrgb, short torus)
|
|
|
|
{
|
|
|
|
float inrgb[3];
|
|
|
|
|
|
|
|
// XXX: signed unsigned mismatch
|
|
|
|
if ((x >= (unsigned int)(ibuf->x)) || (y >= (unsigned int)(ibuf->y))) {
|
|
|
|
if (torus) paint_2d_ibuf_rgb_get(ibuf, x, y, 1, inrgb);
|
|
|
|
else return 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
paint_2d_ibuf_rgb_get(ibuf, x, y, 0, inrgb);
|
|
|
|
}
|
|
|
|
|
|
|
|
outrgb[0] += inrgb[0];
|
|
|
|
outrgb[1] += inrgb[1];
|
|
|
|
outrgb[2] += inrgb[2];
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void paint_2d_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, const short is_torus)
|
|
|
|
{
|
|
|
|
int x, y, count, xi, yi, xo, yo;
|
|
|
|
int out_off[2], in_off[2], dim[2];
|
|
|
|
float outrgb[3];
|
|
|
|
|
|
|
|
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_v3_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 void paint_2d_set_region(ImagePaintRegion *region, int destx, int desty, int srcx, int srcy, int width, int height)
|
|
|
|
{
|
|
|
|
region->destx = destx;
|
|
|
|
region->desty = desty;
|
|
|
|
region->srcx = srcx;
|
|
|
|
region->srcy = srcy;
|
|
|
|
region->width = width;
|
|
|
|
region->height = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf)
|
|
|
|
{
|
|
|
|
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 */
|
|
|
|
destx = destx % dbuf->x;
|
|
|
|
if (destx < 0) destx += dbuf->x;
|
|
|
|
desty = desty % dbuf->y;
|
|
|
|
if (desty < 0) desty += dbuf->y;
|
|
|
|
srcx = srcx % sbuf->x;
|
|
|
|
if (srcx < 0) srcx += sbuf->x;
|
|
|
|
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 (w < origw)
|
|
|
|
paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h);
|
|
|
|
if (h < origh)
|
|
|
|
paint_2d_set_region(®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h);
|
|
|
|
if ((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_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos)
|
|
|
|
{
|
|
|
|
ImagePaintRegion region[4];
|
|
|
|
int a, tot;
|
|
|
|
|
|
|
|
paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y);
|
|
|
|
tot = paint_2d_torus_split_region(region, ibufb, ibuf);
|
|
|
|
|
|
|
|
for (a = 0; a < tot; a++)
|
2013-04-30 06:07:42 +00:00
|
|
|
IMB_rectblend(ibufb, ibufb, ibuf, NULL, 0, region[a].destx, region[a].desty,
|
|
|
|
region[a].destx, region[a].desty,
|
2013-03-07 12:11:38 +00:00
|
|
|
region[a].srcx, region[a].srcy,
|
|
|
|
region[a].width, region[a].height, IMB_BLEND_COPY_RGB);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2013-04-30 06:07:42 +00:00
|
|
|
IMB_rectblend(clonebuf, clonebuf, ibuf, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h,
|
2013-03-07 12:11:38 +00:00
|
|
|
IMB_BLEND_COPY_RGB);
|
2013-04-30 06:07:42 +00:00
|
|
|
IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, 0, destx, desty, destx, desty, destx, desty, w, h,
|
2013-03-07 12:11:38 +00:00
|
|
|
IMB_BLEND_COPY_ALPHA);
|
|
|
|
|
|
|
|
return clonebuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2])
|
|
|
|
{
|
|
|
|
ipos[0] = (int)floorf((pos[0] - ibufb->x / 2) + 1.0f);
|
|
|
|
ipos[1] = (int)floorf((pos[1] - ibufb->y / 2) + 1.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int paint_2d_op(void *state, ImBuf *ibufb, const float lastpos[2], const float pos[2])
|
|
|
|
{
|
|
|
|
ImagePaintState *s = ((ImagePaintState *)state);
|
2013-04-30 06:07:42 +00:00
|
|
|
ImBuf *clonebuf = NULL, *frombuf, *tmpbuf = NULL;
|
2013-03-07 12:11:38 +00:00
|
|
|
ImagePaintRegion region[4];
|
|
|
|
short torus = s->brush->flag & BRUSH_TORUS;
|
|
|
|
short blend = s->blend;
|
|
|
|
float *offset = s->brush->clone.offset;
|
|
|
|
float liftpos[2];
|
2013-04-30 06:07:42 +00:00
|
|
|
float brush_alpha = BKE_brush_alpha_get(s->scene, s->brush);
|
|
|
|
unsigned short mask_max = (unsigned short)(brush_alpha * 65535.0f);
|
2013-03-07 12:11:38 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-04-30 06:07:42 +00:00
|
|
|
if (s->do_masking)
|
|
|
|
tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);
|
|
|
|
|
2013-03-07 12:11:38 +00:00
|
|
|
/* blend into canvas */
|
|
|
|
for (a = 0; a < tot; a++) {
|
|
|
|
imapaint_dirty_region(s->image, s->canvas,
|
|
|
|
region[a].destx, region[a].desty,
|
|
|
|
region[a].width, region[a].height);
|
2013-04-30 06:07:42 +00:00
|
|
|
|
|
|
|
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, 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, 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);
|
|
|
|
}
|
2013-03-07 12:11:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (clonebuf) IMB_freeImBuf(clonebuf);
|
2013-04-30 06:07:42 +00:00
|
|
|
if (tmpbuf) IMB_freeImBuf(tmpbuf);
|
2013-03-07 12:11:38 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int paint_2d_canvas_set(ImagePaintState *s, Image *ima)
|
|
|
|
{
|
|
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL);
|
|
|
|
|
|
|
|
/* verify that we can paint and set canvas */
|
|
|
|
if (ima == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (ima->packedfile && ima->rr) {
|
|
|
|
s->warnpackedfile = ima->id.name + 2;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (ibuf && ibuf->channels != 4) {
|
|
|
|
s->warnmultifile = ima->id.name + 2;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (!ibuf || !(ibuf->rect || ibuf->rect_float))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
s->image = ima;
|
|
|
|
s->canvas = ibuf;
|
|
|
|
|
|
|
|
/* set clone canvas */
|
|
|
|
if (s->tool == PAINT_TOOL_CLONE) {
|
|
|
|
ima = s->brush->clone.image;
|
|
|
|
ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL);
|
|
|
|
|
|
|
|
if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) {
|
|
|
|
BKE_image_release_ibuf(ima, ibuf, NULL);
|
|
|
|
BKE_image_release_ibuf(s->image, s->canvas, NULL);
|
|
|
|
return 0;
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
2013-03-07 12:11:38 +00:00
|
|
|
|
|
|
|
s->clonecanvas = ibuf;
|
|
|
|
|
|
|
|
/* temporarily add float rect for cloning */
|
|
|
|
if (s->canvas->rect_float && !s->clonecanvas->rect_float) {
|
|
|
|
IMB_float_from_rect(s->clonecanvas);
|
|
|
|
}
|
|
|
|
else if (!s->canvas->rect_float && !s->clonecanvas->rect)
|
|
|
|
IMB_rect_from_float(s->clonecanvas);
|
|
|
|
}
|
|
|
|
|
2013-04-30 06:07:42 +00:00
|
|
|
/* set masking */
|
|
|
|
s->do_masking = (s->brush->flag & BRUSH_AIRBRUSH || (s->brush->mtex.tex &&
|
|
|
|
!ELEM(s->brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL)))
|
|
|
|
? false : true;
|
|
|
|
|
2013-03-07 12:11:38 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void paint_2d_canvas_free(ImagePaintState *s)
|
|
|
|
{
|
|
|
|
BKE_image_release_ibuf(s->image, s->canvas, NULL);
|
|
|
|
BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL);
|
2013-04-30 06:07:42 +00:00
|
|
|
|
|
|
|
if (s->do_masking)
|
|
|
|
image_undo_remove_masks();
|
2013-03-07 12:11:38 +00:00
|
|
|
}
|
|
|
|
|
2013-03-10 02:30:53 +00:00
|
|
|
int paint_2d_stroke(void *ps, const int prev_mval[2], const int mval[2], int eraser)
|
2013-03-07 12:11:38 +00:00
|
|
|
{
|
2013-03-10 02:30:53 +00:00
|
|
|
float newuv[2], olduv[2];
|
|
|
|
int redraw = 0;
|
|
|
|
ImagePaintState *s = ps;
|
|
|
|
BrushPainter *painter = s->painter;
|
|
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL);
|
2013-03-17 10:15:06 +00:00
|
|
|
const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA);
|
2013-03-07 12:11:38 +00:00
|
|
|
|
|
|
|
if (!ibuf)
|
|
|
|
return 0;
|
|
|
|
|
2013-03-10 02:30:53 +00:00
|
|
|
s->blend = s->brush->blend;
|
|
|
|
if (eraser)
|
|
|
|
s->blend = IMB_BLEND_ERASE_ALPHA;
|
2013-03-07 12:11:38 +00:00
|
|
|
|
2013-03-10 02:30:53 +00:00
|
|
|
UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]);
|
|
|
|
UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &olduv[0], &olduv[1]);
|
2013-03-07 12:11:38 +00:00
|
|
|
|
2013-03-10 02:30:53 +00:00
|
|
|
newuv[0] *= ibuf->x;
|
|
|
|
newuv[1] *= ibuf->y;
|
2013-03-07 12:11:38 +00:00
|
|
|
|
2013-03-10 02:30:53 +00:00
|
|
|
olduv[0] *= ibuf->x;
|
|
|
|
olduv[1] *= ibuf->y;
|
|
|
|
|
|
|
|
if (painter->firsttouch) {
|
|
|
|
/* paint exactly once on first touch */
|
|
|
|
painter->startpaintpos[0] = newuv[0];
|
|
|
|
painter->startpaintpos[1] = newuv[1];
|
|
|
|
|
|
|
|
painter->firsttouch = 0;
|
|
|
|
copy_v2_v2(painter->lastpaintpos, newuv);
|
2013-03-11 16:23:33 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-10 02:30:53 +00:00
|
|
|
copy_v2_v2(painter->lastpaintpos, olduv);
|
|
|
|
}
|
2013-03-07 12:11:38 +00:00
|
|
|
/* OCIO_TODO: float buffers are now always linear, so always use color correction
|
|
|
|
* this should probably be changed when texture painting color space is supported
|
|
|
|
*/
|
2013-03-29 15:18:22 +00:00
|
|
|
brush_painter_2d_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0);
|
2013-03-10 02:30:53 +00:00
|
|
|
|
2013-04-30 09:59:40 +00:00
|
|
|
brush_painter_2d_refresh_cache(s, painter, newuv, is_data == false);
|
2013-03-10 02:30:53 +00:00
|
|
|
|
|
|
|
if (paint_2d_op(s, painter->cache.ibuf, olduv, newuv)) {
|
|
|
|
imapaint_image_update(s->sima, s->image, ibuf, false);
|
|
|
|
BKE_image_release_ibuf(s->image, ibuf, NULL);
|
|
|
|
redraw |= 1;
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
2013-03-07 12:11:38 +00:00
|
|
|
else {
|
2013-03-10 02:30:53 +00:00
|
|
|
BKE_image_release_ibuf(s->image, ibuf, NULL);
|
2013-03-07 12:11:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (redraw)
|
|
|
|
imapaint_clear_partial_redraw();
|
|
|
|
|
|
|
|
return redraw;
|
2013-01-01 21:23:12 +00:00
|
|
|
}
|
|
|
|
|
2013-03-08 04:00:06 +00:00
|
|
|
void *paint_2d_new_stroke(bContext *C, wmOperator *op)
|
|
|
|
{
|
2013-03-07 12:11:38 +00:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
ToolSettings *settings = scene->toolsettings;
|
Paint refactoring commit, non-disruptive (in theory :p)
* Fix precision overflow issue with overlay previews,
* Expose alpha mask mapping to UI (still not functional but coming soon).
* More overlay refactoring:
Overlay now does minimal checking for texture refresh.
Instead, we now have invalidation flags to set an aspect of the brush
overlay as invalid. This is necessary because this way we will be able to
separate and preview different brush attributes on the overlays, using
different textures:
These attributes/aspects are:
Primary texture (main texture for sculpt, vertex, imapaint)
Secondary texture (mask/alpha texture for imapaint)
Cursor texture (cursor texture. It involves brush strength and curves)
Modified the relevant RNA property update functions and C update callback
functions to call the relevant cursor invalidation functions instead
of checking every frame for multiple properties.
Properties that affect this are:
Image changes, if image is used by current brush,
Texture slot changes, similarly
Curve changes,
Object mode change invalidates the cursor
Paint tool change invalidates the cursor.
These changes give slightly more invalidation cases than simply
comparing the relevant properties each frame, but these do not occur in
performance critical moments and it's a much more elegant system than
adding more variables to check per frame each time we add something on
the system.
2013-04-12 17:21:31 +00:00
|
|
|
Brush *brush = BKE_paint_brush(&settings->imapaint.paint);
|
2013-03-07 12:11:38 +00:00
|
|
|
|
|
|
|
ImagePaintState *s = MEM_callocN(sizeof(ImagePaintState), "ImagePaintState");
|
|
|
|
|
|
|
|
s->sima = CTX_wm_space_image(C);
|
|
|
|
s->v2d = &CTX_wm_region(C)->v2d;
|
|
|
|
s->scene = scene;
|
|
|
|
s->screen = CTX_wm_screen(C);
|
|
|
|
|
|
|
|
s->brush = brush;
|
|
|
|
s->tool = brush->imagepaint_tool;
|
|
|
|
s->blend = brush->blend;
|
|
|
|
|
|
|
|
s->image = s->sima->image;
|
|
|
|
|
|
|
|
if (!paint_2d_canvas_set(s, s->image)) {
|
|
|
|
if (s->warnmultifile)
|
|
|
|
BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint");
|
|
|
|
if (s->warnpackedfile)
|
|
|
|
BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted");
|
|
|
|
|
|
|
|
MEM_freeN(s);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
paint_brush_init_tex(s->brush);
|
|
|
|
|
|
|
|
/* create painter */
|
|
|
|
s->painter = brush_painter_2d_new(scene, s->brush);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2013-03-25 02:41:30 +00:00
|
|
|
void paint_2d_redraw(const bContext *C, void *ps, int final)
|
2013-03-08 04:00:06 +00:00
|
|
|
{
|
2013-03-07 12:11:38 +00:00
|
|
|
ImagePaintState *s = ps;
|
|
|
|
|
|
|
|
if (final) {
|
|
|
|
if (s->image && !(s->sima && s->sima->lock))
|
|
|
|
GPU_free_image(s->image);
|
|
|
|
|
|
|
|
/* compositor listener deals with updating */
|
|
|
|
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!s->sima || !s->sima->lock)
|
|
|
|
ED_region_tag_redraw(CTX_wm_region(C));
|
|
|
|
else
|
|
|
|
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-08 04:00:06 +00:00
|
|
|
void paint_2d_stroke_done(void *ps)
|
|
|
|
{
|
2013-03-07 12:11:38 +00:00
|
|
|
ImagePaintState *s = ps;
|
|
|
|
|
|
|
|
paint_2d_canvas_free(s);
|
|
|
|
brush_painter_2d_free(s->painter);
|
|
|
|
paint_brush_exit_tex(s->brush);
|
|
|
|
|
|
|
|
MEM_freeN(s);
|
|
|
|
}
|