Bugfix #22040
[blender.git] / source / blender / editors / sculpt_paint / paint_image.c
index 526eb2d66616be7b78c624cd343d9903fa2b7e59..46bcd1f1c0da2cd147b98137fc3f47600fe98b7b 100644 (file)
@@ -16,9 +16,8 @@
  * 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., 59 Temple Place - Suite 330, Boston, MA       02111-1307, USA.
+ * 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.
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
 
-#include "DNA_brush_types.h"
-#include "DNA_image_types.h"
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
-#include "DNA_node_types.h"
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
-#include "DNA_screen_types.h"
-#include "DNA_space_types.h"
-#include "DNA_userdef_types.h"
-#include "DNA_view3d_types.h"
-#include "DNA_windowmanager_types.h"
+#include "DNA_brush_types.h"
 
 #include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_object.h"
 #include "BKE_brush.h"
-#include "BKE_global.h"
 #include "BKE_image.h"
 #include "BKE_main.h"
 #include "BKE_mesh.h"
-#include "BKE_node.h"
 #include "BKE_paint.h"
-#include "BKE_utildefines.h"
 #include "BKE_DerivedMesh.h"
 #include "BKE_report.h"
 #include "BKE_depsgraph.h"
+#include "BKE_library.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
 
-#include "UI_interface.h"
 #include "UI_view2d.h"
 
 #include "ED_image.h"
-#include "ED_object.h"
 #include "ED_screen.h"
 #include "ED_sculpt.h"
 #include "ED_view3d.h"
@@ -94,6 +84,7 @@
 
 #include "RNA_access.h"
 #include "RNA_define.h"
+#include "RNA_enum_types.h"
 
 #include "GPU_draw.h"
 
@@ -144,6 +135,11 @@ typedef struct ImagePaintPartialRedraw {
        int enabled;
 } ImagePaintPartialRedraw;
 
+typedef struct ImagePaintRegion {
+       int destx, desty;
+       int srcx, srcy;
+       int width, height;
+} ImagePaintRegion;
 
 /* ProjectionPaint defines */
 
@@ -167,17 +163,25 @@ typedef struct ImagePaintPartialRedraw {
 #define PROJ_DEBUG_WINCLIP 1
 
 /* projectFaceSeamFlags options */
-//#define PROJ_FACE_IGNORE     1<<0    /* When the face is hidden, backfacing or occluded */
-//#define PROJ_FACE_INIT       1<<1    /* When we have initialized the faces data */
-#define PROJ_FACE_SEAM1        1<<0    /* If this face has a seam on any of its edges */
-#define PROJ_FACE_SEAM2        1<<1
-#define PROJ_FACE_SEAM3        1<<2
-#define PROJ_FACE_SEAM4        1<<3
-
-#define PROJ_FACE_NOSEAM1      1<<4
-#define PROJ_FACE_NOSEAM2      1<<5
-#define PROJ_FACE_NOSEAM3      1<<6
-#define PROJ_FACE_NOSEAM4      1<<7
+//#define PROJ_FACE_IGNORE     (1<<0)  /* When the face is hidden, backfacing or occluded */
+//#define PROJ_FACE_INIT       (1<<1)  /* When we have initialized the faces data */
+#define PROJ_FACE_SEAM1        (1<<0)  /* If this face has a seam on any of its edges */
+#define PROJ_FACE_SEAM2        (1<<1)
+#define PROJ_FACE_SEAM3        (1<<2)
+#define PROJ_FACE_SEAM4        (1<<3)
+
+#define PROJ_FACE_NOSEAM1      (1<<4)
+#define PROJ_FACE_NOSEAM2      (1<<5)
+#define PROJ_FACE_NOSEAM3      (1<<6)
+#define PROJ_FACE_NOSEAM4      (1<<7)
+
+#define PROJ_SRC_VIEW          1
+#define PROJ_SRC_IMAGE_CAM     2
+#define PROJ_SRC_IMAGE_VIEW    3
+
+#define PROJ_VIEW_DATA_ID "view_data"
+#define PROJ_VIEW_DATA_SIZE (4*4 + 4*4 + 3) /* viewmat + winmat + clipsta + clipend + is_ortho */
+
 
 /* a slightly scaled down face is used to get fake 3D location for edge pixels in the seams
  * as this number approaches  1.0f the likelihood increases of float precision errors where
@@ -185,8 +189,8 @@ typedef struct ImagePaintPartialRedraw {
 #define PROJ_FACE_SCALE_SEAM   0.99f
 
 #define PROJ_BUCKET_NULL               0
-#define PROJ_BUCKET_INIT               1<<0
-// #define PROJ_BUCKET_CLONE_INIT      1<<1
+#define PROJ_BUCKET_INIT               (1<<0)
+// #define PROJ_BUCKET_CLONE_INIT      (1<<1)
 
 /* used for testing doubles, if a point is on a line etc */
 #define PROJ_GEOM_TOLERANCE 0.00075f
@@ -213,6 +217,7 @@ typedef struct ProjPaintState {
        RegionView3D *rv3d;
        ARegion *ar;
        Scene *scene;
+       int source; /* PROJ_SRC_**** */
 
        Brush *brush;
        short tool, blend;
@@ -254,6 +259,7 @@ typedef struct ProjPaintState {
        float screenMax[2]; 
        float screen_width;                     /* Calculated from screenMin & screenMax */
        float screen_height;
+       int winx, winy;                         /* from the carea or from the projection render */
        
        /* options for projection painting */
        int do_layer_clone;
@@ -281,6 +287,11 @@ typedef struct ProjPaintState {
        float viewPos[3];                       /* View location in object relative 3D space, so can compare to verts  */
        float clipsta, clipend;
        
+       /* reproject vars */
+       Image *reproject_image;
+       ImBuf *reproject_ibuf;
+
+
        /* threads */
        int thread_tot;
        int bucketMin[2];
@@ -372,7 +383,7 @@ static void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int
                        return tile->rect;
        
        if (*tmpibuf==NULL)
-               *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat|IB_rect, 0);
+               *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat|IB_rect);
        
        tile= MEM_callocN(sizeof(UndoImageTile), "UndoImageTile");
        strcpy(tile->idname, ima->id.name);
@@ -399,7 +410,7 @@ static void image_undo_restore(bContext *C, ListBase *lb)
        UndoImageTile *tile;
 
        tmpibuf= IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32,
-                               IB_rectfloat|IB_rect, 0);
+                                                       IB_rectfloat|IB_rect);
        
        for(tile=lb->first; tile; tile=tile->next) {
                /* find image based on name, pointer becomes invalid with global undo */
@@ -462,8 +473,6 @@ static int project_bucket_offset_safe(const ProjPaintState *ps, const float proj
        }
 }
 
-#define SIDE_OF_LINE(pa, pb, pp)       ((pa[0]-pp[0])*(pb[1]-pp[1]))-((pb[0]-pp[0])*(pa[1]-pp[1]))
-
 /* still use 2D X,Y space but this works for verts transformed by a perspective matrix, using their 4th component as a weight */
 static void barycentric_weights_v2_persp(float v1[4], float v2[4], float v3[4], float co[2], float w[3])
 {
@@ -475,14 +484,14 @@ static void barycentric_weights_v2_persp(float v1[4], float v2[4], float v3[4],
    wtot = w[0]+w[1]+w[2];
 
    if (wtot != 0.0f) {
-       wtot_inv = 1.0f/wtot;
+          wtot_inv = 1.0f/wtot;
 
-       w[0] = w[0]*wtot_inv;
-       w[1] = w[1]*wtot_inv;
-       w[2] = w[2]*wtot_inv;
+          w[0] = w[0]*wtot_inv;
+          w[1] = w[1]*wtot_inv;
+          w[2] = w[2]*wtot_inv;
    }
    else /* dummy values for zero area face */
-       w[0] = w[1] = w[2] = 1.0f/3.0f;
+          w[0] = w[1] = w[2] = 1.0f/3.0f;
 }
 
 static float VecZDepthOrtho(float pt[2], float v1[3], float v2[3], float v3[3], float w[3])
@@ -628,8 +637,8 @@ static int project_paint_PickColor(const ProjPaintState *ps, float pt[2], float
                }
        }
        else {
-               xi = (uv[0]*ibuf->x) + 0.5f;
-               yi = (uv[1]*ibuf->y) + 0.5f;
+               xi = (int)((uv[0]*ibuf->x) + 0.5f);
+               yi = (int)((uv[1]*ibuf->y) + 0.5f);
                
                //if (xi<0 || xi>=ibuf->x  ||  yi<0 || yi>=ibuf->y) return 0;
                
@@ -668,7 +677,7 @@ static int project_paint_PickColor(const ProjPaintState *ps, float pt[2], float
  *  0  : no occlusion
  * -1  : no occlusion but 2D intersection is true (avoid testing the other half of a quad)
  *  1  : occluded
-    2  : occluded with w[3] weights set (need to know in some cases) */
+       2       : occluded with w[3] weights set (need to know in some cases) */
 
 static int project_paint_occlude_ptv(float pt[3], float v1[3], float v2[3], float v3[3], float w[3], int is_ortho)
 {
@@ -737,6 +746,7 @@ static int project_bucket_point_occluded(const ProjPaintState *ps, LinkNode *buc
        int face_index;
        int isect_ret;
        float w[3]; /* not needed when clipping */
+       const short do_clip= ps->rv3d ? ps->rv3d->rflag & RV3D_CLIPPING : 0;
        
        /* 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 */
@@ -746,14 +756,14 @@ static int project_bucket_point_occluded(const ProjPaintState *ps, LinkNode *buc
 
                if (orig_face != face_index) {
                        mf = ps->dm_mface + face_index;
-                       if(ps->rv3d->rflag & RV3D_CLIPPING)
+                       if(do_clip)
                                isect_ret = project_paint_occlude_ptv_clip(ps, mf, pixelScreenCo, ps->screenCoords[mf->v1], ps->screenCoords[mf->v2], ps->screenCoords[mf->v3], 0);
                        else
                                isect_ret = project_paint_occlude_ptv(pixelScreenCo, ps->screenCoords[mf->v1], ps->screenCoords[mf->v2], ps->screenCoords[mf->v3], w, ps->is_ortho);
 
                        /* Note, if isect_ret==-1 then we dont want to test the other side of the quad */
                        if (isect_ret==0 && mf->v4) {
-                               if(ps->rv3d->rflag & RV3D_CLIPPING)
+                               if(do_clip)
                                        isect_ret = project_paint_occlude_ptv_clip(ps, mf, pixelScreenCo, ps->screenCoords[mf->v1], ps->screenCoords[mf->v3], ps->screenCoords[mf->v4], 1);
                                else
                                        isect_ret = project_paint_occlude_ptv(pixelScreenCo, ps->screenCoords[mf->v1], ps->screenCoords[mf->v3], ps->screenCoords[mf->v4], w, ps->is_ortho);
@@ -1001,9 +1011,11 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], const fl
        float puv[4][2]; /* pixelspace uv's */
        float no1[2], no2[2], no3[2], no4[2]; /* normals */
        float dir1[2], dir2[2], dir3[2], dir4[2];
-       float ibuf_x_inv = 1.0f / (float)ibuf_x; 
-       float ibuf_y_inv = 1.0f / (float)ibuf_y; 
-       
+       float ibuf_inv[2];
+
+       ibuf_inv[0]= 1.0f / (float)ibuf_x;
+       ibuf_inv[1]= 1.0f / (float)ibuf_y;
+
        /* make UV's in pixel space so we can */
        puv[0][0] = orig_uv[0][0] * ibuf_x;
        puv[0][1] = orig_uv[0][1] * ibuf_y;
@@ -1035,17 +1047,20 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], const fl
                sub_v2_v2v2(dir3, puv[0], puv[2]);
                normalize_v2(dir3);
        }
-       
+
+       /* TODO - angle_normalized_v2v2(...) * (M_PI/180.0f)
+        * This is incorrect. Its already given radians but without it wont work.
+        * need to look into a fix - campbell */
        if (is_quad) {
-               a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1));
-               a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2));
-               a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3));
-               a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4));
+               a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1) * ((float)M_PI/180.0f));
+               a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f));
+               a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f));
+               a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4) * ((float)M_PI/180.0f));
        }
        else {
-               a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1));
-               a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2));
-               a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3));
+               a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1) * ((float)M_PI/180.0f));
+               a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f));
+               a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f));
        }
        
        if (is_quad) {
@@ -1065,17 +1080,10 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], const fl
                add_v2_v2v2(outset_uv[1], puv[1], no2);
                add_v2_v2v2(outset_uv[2], puv[2], no3);
                add_v2_v2v2(outset_uv[3], puv[3], no4);
-               outset_uv[0][0] *= ibuf_x_inv;
-               outset_uv[0][1] *= ibuf_y_inv;
-               
-               outset_uv[1][0] *= ibuf_x_inv;
-               outset_uv[1][1] *= ibuf_y_inv;
-               
-               outset_uv[2][0] *= ibuf_x_inv;
-               outset_uv[2][1] *= ibuf_y_inv;
-               
-               outset_uv[3][0] *= ibuf_x_inv;
-               outset_uv[3][1] *= ibuf_y_inv;
+               mul_v2_v2(outset_uv[0], ibuf_inv);
+               mul_v2_v2(outset_uv[1], ibuf_inv);
+               mul_v2_v2(outset_uv[2], ibuf_inv);
+               mul_v2_v2(outset_uv[3], ibuf_inv);
        }
        else {
                sub_v2_v2v2(no1, dir3, dir1);
@@ -1090,14 +1098,10 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], const fl
                add_v2_v2v2(outset_uv[0], puv[0], no1);
                add_v2_v2v2(outset_uv[1], puv[1], no2);
                add_v2_v2v2(outset_uv[2], puv[2], no3);
-               outset_uv[0][0] *= ibuf_x_inv;
-               outset_uv[0][1] *= ibuf_y_inv;
-               
-               outset_uv[1][0] *= ibuf_x_inv;
-               outset_uv[1][1] *= ibuf_y_inv;
-               
-               outset_uv[2][0] *= ibuf_x_inv;
-               outset_uv[2][1] *= ibuf_y_inv;
+
+               mul_v2_v2(outset_uv[0], ibuf_inv);
+               mul_v2_v2(outset_uv[1], ibuf_inv);
+               mul_v2_v2(outset_uv[2], ibuf_inv);
        }
 }
 
@@ -1192,7 +1196,7 @@ static void screen_px_from_persp(
                w[2] *= wtot_inv;
        }
        else {
-               w[0] = w[1] = w[2] = 1.0/3.0; /* dummy values for zero area face */
+               w[0] = w[1] = w[2] = 1.0f/3.0f; /* dummy values for zero area face */
        }
        /* done re-weighting */
        
@@ -1329,7 +1333,7 @@ float project_paint_uvpixel_mask(
        
        // This only works when the opacity dosnt change while painting, stylus pressure messes with this
        // so dont use it.
-       // if (ps->is_airbrush==0) mask *= ps->brush->alpha;
+       // if (ps->is_airbrush==0) mask *= brush_alpha(ps->brush);
        
        return mask;
 }
@@ -1439,7 +1443,7 @@ static ProjPixel *project_paint_uvpixel_init(
                        sub_v2_v2v2(co, projPixel->projCoSS, (float *)ps->cloneOffset);
                        
                        /* 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(...) */
+                        * the faces are already initialized in project_paint_delayed_face_init(...) */
                        if (ibuf->rect_float) {
                                if (!project_paint_PickColor(ps, co, ((ProjPixelClone *)projPixel)->clonepx.f, NULL, 1)) {
                                        ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; /* zero alpha - ignore */
@@ -1637,10 +1641,10 @@ static void scale_quad(float insetCos[4][3], float *origCos[4], const float inse
        mul_v3_fl(insetCos[2], inset);
        mul_v3_fl(insetCos[3], inset);
        
-       add_v3_v3v3(insetCos[0], insetCos[0], cent);
-       add_v3_v3v3(insetCos[1], insetCos[1], cent);
-       add_v3_v3v3(insetCos[2], insetCos[2], cent);
-       add_v3_v3v3(insetCos[3], insetCos[3], cent);
+       add_v3_v3(insetCos[0], cent);
+       add_v3_v3(insetCos[1], cent);
+       add_v3_v3(insetCos[2], cent);
+       add_v3_v3(insetCos[3], cent);
 }
 
 
@@ -1659,9 +1663,9 @@ static void scale_tri(float insetCos[4][3], float *origCos[4], const float inset
        mul_v3_fl(insetCos[1], inset);
        mul_v3_fl(insetCos[2], inset);
        
-       add_v3_v3v3(insetCos[0], insetCos[0], cent);
-       add_v3_v3v3(insetCos[1], insetCos[1], cent);
-       add_v3_v3v3(insetCos[2], insetCos[2], cent);
+       add_v3_v3(insetCos[0], cent);
+       add_v3_v3(insetCos[1], cent);
+       add_v3_v3(insetCos[2], cent);
 }
 
 
@@ -1686,10 +1690,10 @@ static float Vec2Lenf_nosqrt_other(const float *v1, const float v2_1, const floa
 /* note, use a squared value so we can use Vec2Lenf_nosqrt
  * be sure that you have done a bounds check first or this may fail */
 /* only give bucket_bounds as an arg because we need it elsewhere */
-static int project_bucket_isect_circle(const int bucket_x, const int bucket_y, const float cent[2], const float radius_squared, rctf *bucket_bounds)
+static int project_bucket_isect_circle(const float cent[2], const float radius_squared, rctf *bucket_bounds)
 {
         
-       /* Would normally to a simple intersection test, however we know the bounds of these 2 alredy intersect 
+       /* Would normally to a simple intersection test, however we know the bounds of these 2 already intersect 
         * so we only need to test if the center is inside the vertical or horizontal bounds on either axis,
         * this is even less work then an intersection test
         * 
@@ -1849,7 +1853,7 @@ static void project_bucket_clip_face(
 {
        int inside_bucket_flag = 0;
        int inside_face_flag = 0;
-       const int flip = ((SIDE_OF_LINE(v1coSS, v2coSS, v3coSS) > 0.0f) != (SIDE_OF_LINE(uv1co, uv2co, uv3co) > 0.0f));
+       const int flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f));
        
        float bucket_bounds_ss[4][2];
 
@@ -2131,15 +2135,15 @@ if __name__ == '__main__':
 
        
 /* checks if pt is inside a convex 2D polyline, the polyline must be ordered rotating clockwise
- * otherwise it would have to test for mixed (SIDE_OF_LINE > 0.0f) cases */
+ * otherwise it would have to test for mixed (line_point_side_v2 > 0.0f) cases */
 int IsectPoly2Df(const float pt[2], float uv[][2], const int tot)
 {
        int i;
-       if (SIDE_OF_LINE(uv[tot-1], uv[0], pt) < 0.0f)
+       if (line_point_side_v2(uv[tot-1], uv[0], pt) < 0.0f)
                return 0;
        
        for (i=1; i<tot; i++) {
-               if (SIDE_OF_LINE(uv[i-1], uv[i], pt) < 0.0f)
+               if (line_point_side_v2(uv[i-1], uv[i], pt) < 0.0f)
                        return 0;
                
        }
@@ -2149,10 +2153,10 @@ int IsectPoly2Df(const float pt[2], float uv[][2], const int tot)
 static int IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot)
 {
        int i;
-       int side = (SIDE_OF_LINE(uv[tot-1], uv[0], pt) > 0.0f);
+       int side = (line_point_side_v2(uv[tot-1], uv[0], pt) > 0.0f);
        
        for (i=1; i<tot; i++) {
-               if ((SIDE_OF_LINE(uv[i-1], uv[i], pt) > 0.0f) != side)
+               if ((line_point_side_v2(uv[i-1], uv[i], pt) > 0.0f) != side)
                        return 0;
                
        }
@@ -2203,6 +2207,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
        int uv_clip_tot;
        const short is_ortho = ps->is_ortho;
        const short do_backfacecull = ps->do_backfacecull;
+       const short do_clip= ps->rv3d ? ps->rv3d->rflag & RV3D_CLIPPING : 0;
        
        vCo[0] = ps->dm_mvert[mf->v1].co;
        vCo[1] = ps->dm_mvert[mf->v2].co;
@@ -2302,7 +2307,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
                                                else                    screen_px_from_persp(uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w);
                                                
                                                /* a pitty we need to get the worldspace pixel location here */
-                                               if(ps->rv3d->rflag & RV3D_CLIPPING) {
+                                               if(do_clip) {
                                                        interp_v3_v3v3v3(wco, ps->dm_mvert[ (*(&mf->v1 + i1)) ].co, ps->dm_mvert[ (*(&mf->v1 + i2)) ].co, ps->dm_mvert[ (*(&mf->v1 + i3)) ].co, w);
                                                        if(view3d_test_clipping(ps->rv3d, wco, 1)) {
                                                                continue; /* Watch out that no code below this needs to run */
@@ -2483,8 +2488,8 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
                                                                                if (!is_ortho) {
                                                                                        pixelScreenCo[3] = 1.0f;
                                                                                        mul_m4_v4((float(*)[4])ps->projectMat, pixelScreenCo); /* cast because of const */
-                                                                                       pixelScreenCo[0] = (float)(ps->ar->winx/2.0f)+(ps->ar->winx/2.0f)*pixelScreenCo[0]/pixelScreenCo[3];    
-                                                                                       pixelScreenCo[1] = (float)(ps->ar->winy/2.0f)+(ps->ar->winy/2.0f)*pixelScreenCo[1]/pixelScreenCo[3];
+                                                                                       pixelScreenCo[0] = (float)(ps->winx/2.0f)+(ps->winx/2.0f)*pixelScreenCo[0]/pixelScreenCo[3];
+                                                                                       pixelScreenCo[1] = (float)(ps->winy/2.0f)+(ps->winy/2.0f)*pixelScreenCo[1]/pixelScreenCo[3];
                                                                                        pixelScreenCo[2] = pixelScreenCo[2]/pixelScreenCo[3]; /* Use the depth for bucket point occlusion */
                                                                                }
                                                                                
@@ -2507,17 +2512,17 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
                                                                                                w[0]=w[1]=w[2]= 0.0;
                                                                                                if (side) {
                                                                                                        w[fidx1?fidx1-1:0] = fac;
-                                                                                                       w[fidx2?fidx2-1:0] = 1.0-fac;
+                                                                                                       w[fidx2?fidx2-1:0] = 1.0f-fac;
                                                                                                }
                                                                                                else {
                                                                                                        w[fidx1] = fac;
-                                                                                                       w[fidx2] = 1.0-fac;
+                                                                                                       w[fidx2] = 1.0f-fac;
                                                                                                }
 #endif
                                                                                        }
                                                                                        
                                                                                        /* a pitty we need to get the worldspace pixel location here */
-                                                                                       if(ps->rv3d->rflag & RV3D_CLIPPING) {
+                                                                                       if(do_clip) {
                                                                                                if (side)       interp_v3_v3v3v3(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v3].co, ps->dm_mvert[mf->v4].co, w);
                                                                                                else            interp_v3_v3v3v3(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v2].co, ps->dm_mvert[mf->v3].co, w);
 
@@ -2565,11 +2570,12 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
 static void project_paint_bucket_bounds(const ProjPaintState *ps, const float min[2], const float max[2], int bucketMin[2], int bucketMax[2])
 {
        /* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */
-       bucketMin[0] = (int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f; /* these offsets of 0.5 and 1.5 seem odd but they are correct */
-       bucketMin[1] = (int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f;
+       /* XXX: the offset of 0.5 is always truncated to zero and the offset of 1.5f is always truncated to 1, is this really correct?? - jwilkins */
+       bucketMin[0] = (int)((int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f); /* these offsets of 0.5 and 1.5 seem odd but they are correct */
+       bucketMin[1] = (int)((int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f);
        
-       bucketMax[0] = (int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f;
-       bucketMax[1] = (int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f;  
+       bucketMax[0] = (int)((int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f);
+       bucketMax[1] = (int)((int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f);
        
        /* incase the rect is outside the mesh 2d bounds */
        CLAMP(bucketMin[0], 0, ps->buckets_x);
@@ -2647,7 +2653,7 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index
  * calculated when it might not be needed later, (at the moment at least)
  * obviously it shouldn't have bugs though */
 
-static int project_bucket_face_isect(ProjPaintState *ps, float min[2], float max[2], int bucket_x, int bucket_y, int bucket_index, const MFace *mf)
+static int project_bucket_face_isect(ProjPaintState *ps, int bucket_x, int bucket_y, const MFace *mf)
 {
        /* TODO - replace this with a tricker method that uses sideofline for all screenCoords's edges against the closest bucket corner */
        rctf bucket_bounds;
@@ -2707,11 +2713,11 @@ static int project_bucket_face_isect(ProjPaintState *ps, float min[2], float max
 
 /* Add faces to the bucket but dont initialize its pixels
  * TODO - when painting occluded, sort the faces on their min-Z and only add faces that faces that are not occluded */
-static void project_paint_delayed_face_init(ProjPaintState *ps, const MFace *mf, const MTFace *tf, const int face_index)
+static void project_paint_delayed_face_init(ProjPaintState *ps, const MFace *mf, const int face_index)
 {
        float min[2], max[2], *vCoSS;
        int bucketMin[2], bucketMax[2]; /* for  ps->bucketRect indexing */
-       int fidx, bucket_x, bucket_y, bucket_index;
+       int fidx, bucket_x, bucket_y;
        int has_x_isect = -1, has_isect = 0; /* for early loop exit */
        MemArena *arena = ps->arena_mt[0]; /* just use the first thread arena since threading has not started yet */
        
@@ -2728,10 +2734,8 @@ static void project_paint_delayed_face_init(ProjPaintState *ps, const MFace *mf,
        for (bucket_y = bucketMin[1]; bucket_y < bucketMax[1]; bucket_y++) {
                has_x_isect = 0;
                for (bucket_x = bucketMin[0]; bucket_x < bucketMax[0]; bucket_x++) {
-                       
-                       bucket_index = bucket_x + (bucket_y * ps->buckets_x);
-                       
-                       if (project_bucket_face_isect(ps, min, max, bucket_x, bucket_y, bucket_index, mf)) {
+                       if (project_bucket_face_isect(ps, bucket_x, bucket_y, mf)) {
+                               int bucket_index= bucket_x + (bucket_y * ps->buckets_x);
                                BLI_linklist_prepend_arena(
                                        &ps->bucketFaces[ bucket_index ],
                                        SET_INT_IN_POINTER(face_index), /* cast to a pointer to shut up the compiler */
@@ -2762,6 +2766,19 @@ static void project_paint_delayed_face_init(ProjPaintState *ps, const MFace *mf,
 #endif
 }
 
+static int project_paint_view_clip(View3D *v3d, RegionView3D *rv3d, float *clipsta, float *clipend)
+{
+       int orth= get_view3d_cliprange(v3d, rv3d, clipsta, clipend);
+
+       if (orth) { /* only needed for ortho */
+               float fac = 2.0f / ((*clipend) - (*clipsta));
+               *clipsta *= fac;
+               *clipend *= fac;
+       }
+
+       return orth;
+}
+
 /* run once per stroke before projection painting */
 static void project_paint_begin(ProjPaintState *ps)
 {      
@@ -2770,8 +2787,9 @@ static void project_paint_begin(ProjPaintState *ps)
        
        float no[3];
        
-       float (*projScreenCo)[4]; /* Note, we could have 4D vectors are only needed for */
+       float *projScreenCo; /* Note, we could have 4D vectors are only needed for */
        float projMargin;
+
        /* Image Vars - keep track of images we have used */
        LinkNode *image_LinkList = NULL;
        LinkNode *node;
@@ -2785,22 +2803,31 @@ static void project_paint_begin(ProjPaintState *ps)
        
        int a, i; /* generic looping vars */
        int image_index = -1, face_index;
+       MVert *mv;
        
        MemArena *arena; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */
+
+       const int diameter= 2*brush_size(ps->brush);
        
        /* ---- end defines ---- */
        
-       ED_view3d_local_clipping(ps->rv3d, ps->ob->obmat); /* faster clipping lookups */
+       if(ps->source==PROJ_SRC_VIEW)
+               ED_view3d_local_clipping(ps->rv3d, ps->ob->obmat); /* faster clipping lookups */
 
        /* paint onto the derived mesh */
        
        /* Workaround for subsurf selection, try the display mesh first */
-       if(ps->ob->derivedFinal && CustomData_has_layer( &ps->ob->derivedFinal->faceData, CD_MTFACE)) {
+       if (ps->source==PROJ_SRC_IMAGE_CAM) {
+               /* using render mesh, assume only camera was rendered from */
+               ps->dm = mesh_create_derived_render(ps->scene, ps->ob, ps->v3d->customdata_mask | CD_MASK_MTFACE);
+               ps->dm_release= TRUE;
+       }
+       else if(ps->ob->derivedFinal && CustomData_has_layer( &ps->ob->derivedFinal->faceData, CD_MTFACE)) {
                ps->dm = ps->ob->derivedFinal;
                ps->dm_release= FALSE;
        }
        else {
-               ps->dm = mesh_get_derived_final(ps->scene, ps->ob, ps->v3d->customdata_mask);
+               ps->dm = mesh_get_derived_final(ps->scene, ps->ob, ps->v3d->customdata_mask | CD_MASK_MTFACE);
                ps->dm_release= TRUE;
        }
        
@@ -2834,6 +2861,7 @@ static void project_paint_begin(ProjPaintState *ps)
                if (ps->dm_mtface_clone==NULL || ps->dm_mtface_clone==ps->dm_mtface) {
                        ps->do_layer_clone = 0;
                        ps->dm_mtface_clone= NULL;
+                       printf("ACK!\n");
                }
        }
        
@@ -2849,89 +2877,142 @@ static void project_paint_begin(ProjPaintState *ps)
                }
        }
        
-
+       /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */
+       if(ps->dm->type != DM_TYPE_CDDM) {
+               ps->dm_mvert= MEM_dupallocN(ps->dm_mvert);
+               ps->dm_mface= MEM_dupallocN(ps->dm_mface);
+               /* looks like these are ok for now.*/
+               /*
+               ps->dm_mtface= MEM_dupallocN(ps->dm_mtface);
+               ps->dm_mtface_clone= MEM_dupallocN(ps->dm_mtface_clone);
+               ps->dm_mtface_stencil= MEM_dupallocN(ps->dm_mtface_stencil);
+                */
+       }
        
        ps->viewDir[0] = 0.0f;
        ps->viewDir[1] = 0.0f;
        ps->viewDir[2] = 1.0f;
        
-       view3d_get_object_project_mat(ps->rv3d, ps->ob, ps->projectMat);
-       
-       /* viewDir - object relative */
-       invert_m4_m4(ps->ob->imat, ps->ob->obmat);
-       copy_m3_m4(mat, ps->rv3d->viewinv);
-       mul_m3_v3(mat, ps->viewDir);
-       copy_m3_m4(mat, ps->ob->imat);
-       mul_m3_v3(mat, ps->viewDir);
-       normalize_v3(ps->viewDir);
-       
-       /* viewPos - object relative */
-       VECCOPY(ps->viewPos, ps->rv3d->viewinv[3]);
-       copy_m3_m4(mat, ps->ob->imat);
-       mul_m3_v3(mat, ps->viewPos);
-       add_v3_v3v3(ps->viewPos, ps->viewPos, ps->ob->imat[3]);
-       
-       {       /* only use these for running 'get_view3d_viewplane' */
-               rctf viewplane;
-               
-               ps->is_ortho = get_view3d_viewplane(ps->v3d, ps->rv3d, ps->ar->winx, ps->ar->winy, &viewplane, &ps->clipsta, &ps->clipend, NULL);
-               
-               //printf("%f %f\n", ps->clipsta, ps->clipend);
-               if (ps->is_ortho) { /* only needed for ortho */
-                       float fac = 2.0f / (ps->clipend - ps->clipsta);  
-                       ps->clipsta *= fac;
-                       ps->clipend *= fac;
+       {
+               float viewmat[4][4];
+               float viewinv[4][4];
+
+               invert_m4_m4(ps->ob->imat, ps->ob->obmat);
+
+               if(ps->source==PROJ_SRC_VIEW) {
+                       /* normal drawing */
+                       ps->winx= ps->ar->winx;
+                       ps->winy= ps->ar->winy;
+
+                       copy_m4_m4(viewmat, ps->rv3d->viewmat);
+                       copy_m4_m4(viewinv, ps->rv3d->viewinv);
+
+                       view3d_get_object_project_mat(ps->rv3d, ps->ob, ps->projectMat);
+
+                       ps->is_ortho= project_paint_view_clip(ps->v3d, ps->rv3d, &ps->clipsta, &ps->clipend);
                }
                else {
-                       /* TODO - can we even adjust for clip start/end? */
+                       /* reprojection */
+                       float winmat[4][4];
+                       float vmat[4][4];
+
+                       ps->winx= ps->reproject_ibuf->x;
+                       ps->winy= ps->reproject_ibuf->y;
+
+                       if (ps->source==PROJ_SRC_IMAGE_VIEW) {
+                               /* image stores camera data, tricky */
+                               IDProperty *idgroup= IDP_GetProperties(&ps->reproject_image->id, 0);
+                               IDProperty *view_data= IDP_GetPropertyFromGroup(idgroup, PROJ_VIEW_DATA_ID);
+
+                               float *array= (float *)IDP_Array(view_data);
+
+                               /* use image array, written when creating image */
+                               memcpy(winmat, array, sizeof(winmat)); array += sizeof(winmat)/sizeof(float);
+                               memcpy(viewmat, array, sizeof(viewmat)); array += sizeof(viewmat)/sizeof(float);
+                               ps->clipsta= array[0];
+                               ps->clipend= array[1];
+                               ps->is_ortho= array[2] ? 1:0;
+
+                               invert_m4_m4(viewinv, viewmat);
+                       }
+                       else if (ps->source==PROJ_SRC_IMAGE_CAM) {
+                               Object *camera= ps->scene->camera;
+
+                               /* dont actually use these */
+                               float _viewdx, _viewdy, _ycor, _lens=0.0f;
+                               rctf _viewplane;
+
+                               /* viewmat & viewinv */
+                               copy_m4_m4(viewinv, ps->scene->camera->obmat);
+                               normalize_m4(viewinv);
+                               invert_m4_m4(viewmat, viewinv);
+
+                               /* camera winmat */
+                               object_camera_matrix(&ps->scene->r, camera, ps->winx, ps->winy, 0,
+                                               winmat, &_viewplane, &ps->clipsta, &ps->clipend,
+                                               &_lens, &_ycor, &_viewdx, &_viewdy);
+
+                               ps->is_ortho= (ps->scene->r.mode & R_ORTHO) ? 1 : 0;
+                       }
+
+                       /* same as view3d_get_object_project_mat */
+                       mul_m4_m4m4(vmat, ps->ob->obmat, viewmat);
+                       mul_m4_m4m4(ps->projectMat, vmat, winmat);
                }
+
+
+               /* viewDir - object relative */
+               invert_m4_m4(ps->ob->imat, ps->ob->obmat);
+               copy_m3_m4(mat, viewinv);
+               mul_m3_v3(mat, ps->viewDir);
+               copy_m3_m4(mat, ps->ob->imat);
+               mul_m3_v3(mat, ps->viewDir);
+               normalize_v3(ps->viewDir);
                
+               /* viewPos - object relative */
+               VECCOPY(ps->viewPos, viewinv[3]);
+               copy_m3_m4(mat, ps->ob->imat);
+               mul_m3_v3(mat, ps->viewPos);
+               add_v3_v3(ps->viewPos, ps->ob->imat[3]);
        }
        
-       ps->is_airbrush = (ps->brush->flag & BRUSH_AIRBRUSH) ? 1 : 0;
-       
-       ps->is_texbrush = (ps->brush->mtex[ps->brush->texact] && ps->brush->mtex[ps->brush->texact]->tex) ? 1 : 0;
-
-       
        /* calculate vert screen coords
         * run this early so we can calculate the x/y resolution of our bucket rect */
        INIT_MINMAX2(ps->screenMin, ps->screenMax);
        
        ps->screenCoords = MEM_mallocN(sizeof(float) * ps->dm_totvert * 4, "ProjectPaint ScreenVerts");
-       projScreenCo = ps->screenCoords;
+       projScreenCo= *ps->screenCoords;
        
        if (ps->is_ortho) {
-               for(a=0; a < ps->dm_totvert; a++, projScreenCo++) {
-                       VECCOPY((*projScreenCo), ps->dm_mvert[a].co);
-                       mul_m4_v3(ps->projectMat, (*projScreenCo));
+               for(a=0, mv=ps->dm_mvert; a < ps->dm_totvert; a++, mv++, projScreenCo+=4) {
+                       mul_v3_m4v3(projScreenCo, ps->projectMat, mv->co);
                        
                        /* screen space, not clamped */
-                       (*projScreenCo)[0] = (float)(ps->ar->winx/2.0f)+(ps->ar->winx/2.0f)*(*projScreenCo)[0];
-                       (*projScreenCo)[1] = (float)(ps->ar->winy/2.0f)+(ps->ar->winy/2.0f)*(*projScreenCo)[1];
-                       DO_MINMAX2((*projScreenCo), ps->screenMin, ps->screenMax);
+                       projScreenCo[0] = (float)(ps->winx/2.0f)+(ps->winx/2.0f)*projScreenCo[0];
+                       projScreenCo[1] = (float)(ps->winy/2.0f)+(ps->winy/2.0f)*projScreenCo[1];
+                       DO_MINMAX2(projScreenCo, ps->screenMin, ps->screenMax);
                }
        }
        else {
-               for(a=0; a < ps->dm_totvert; a++, projScreenCo++) {
-                       VECCOPY((*projScreenCo), ps->dm_mvert[a].co);
-                       (*projScreenCo)[3] = 1.0f;
+               for(a=0, mv=ps->dm_mvert; a < ps->dm_totvert; a++, mv++, projScreenCo+=4) {
+                       copy_v3_v3(projScreenCo, mv->co);
+                       projScreenCo[3] = 1.0f;
 
-                       mul_m4_v4(ps->projectMat, (*projScreenCo));
+                       mul_m4_v4(ps->projectMat, projScreenCo);
 
-                       
-                       if ((*projScreenCo)[3] > ps->clipsta) {
+                       if (projScreenCo[3] > ps->clipsta) {
                                /* screen space, not clamped */
-                               (*projScreenCo)[0] = (float)(ps->ar->winx/2.0f)+(ps->ar->winx/2.0f)*(*projScreenCo)[0]/(*projScreenCo)[3];
-                               (*projScreenCo)[1] = (float)(ps->ar->winy/2.0f)+(ps->ar->winy/2.0f)*(*projScreenCo)[1]/(*projScreenCo)[3];
-                               (*projScreenCo)[2] = (*projScreenCo)[2]/(*projScreenCo)[3]; /* Use the depth for bucket point occlusion */
-                               DO_MINMAX2((*projScreenCo), ps->screenMin, ps->screenMax);
+                               projScreenCo[0] = (float)(ps->winx/2.0f)+(ps->winx/2.0f)*projScreenCo[0]/projScreenCo[3];
+                               projScreenCo[1] = (float)(ps->winy/2.0f)+(ps->winy/2.0f)*projScreenCo[1]/projScreenCo[3];
+                               projScreenCo[2] = projScreenCo[2]/projScreenCo[3]; /* Use the depth for bucket point occlusion */
+                               DO_MINMAX2(projScreenCo, ps->screenMin, ps->screenMax);
                        }
                        else {
                                /* TODO - deal with cases where 1 side of a face goes behind the view ?
                                 * 
                                 * After some research this is actually very tricky, only option is to
                                 * clip the derived mesh before painting, which is a Pain */
-                               (*projScreenCo)[0] = FLT_MAX;
+                               projScreenCo[0] = FLT_MAX;
                        }
                }
        }
@@ -2946,20 +3027,29 @@ static void project_paint_begin(ProjPaintState *ps)
        ps->screenMax[1] += projMargin;
        ps->screenMin[1] -= projMargin;
        
+       if(ps->source==PROJ_SRC_VIEW) {
 #ifdef PROJ_DEBUG_WINCLIP
-       CLAMP(ps->screenMin[0], -ps->brush->size, ps->ar->winx + ps->brush->size);
-       CLAMP(ps->screenMax[0], -ps->brush->size, ps->ar->winx + ps->brush->size);
+               CLAMP(ps->screenMin[0], (float)(-diameter), (float)(ps->winx + diameter));
+               CLAMP(ps->screenMax[0], (float)(-diameter), (float)(ps->winx + diameter));
 
-       CLAMP(ps->screenMin[1], -ps->brush->size, ps->ar->winy + ps->brush->size);
-       CLAMP(ps->screenMax[1], -ps->brush->size, ps->ar->winy + ps->brush->size);
+               CLAMP(ps->screenMin[1], (float)(-diameter), (float)(ps->winy + diameter));
+               CLAMP(ps->screenMax[1], (float)(-diameter), (float)(ps->winy + diameter));
 #endif
-       
+       }
+       else { /* reprojection, use bounds */
+               ps->screenMin[0]= 0;
+               ps->screenMax[0]= (float)(ps->winx);
+
+               ps->screenMin[1]= 0;
+               ps->screenMax[1]= (float)(ps->winy);
+       }
+
        /* only for convenience */
        ps->screen_width  = ps->screenMax[0] - ps->screenMin[0];
        ps->screen_height = ps->screenMax[1] - ps->screenMin[1];
        
-       ps->buckets_x = (int)(ps->screen_width / (((float)ps->brush->size) / PROJ_BUCKET_BRUSH_DIV));
-       ps->buckets_y = (int)(ps->screen_height / (((float)ps->brush->size) / PROJ_BUCKET_BRUSH_DIV));
+       ps->buckets_x = (int)(ps->screen_width / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV));
+       ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV));
        
        /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */
        
@@ -2993,21 +3083,18 @@ static void project_paint_begin(ProjPaintState *ps)
                ps->thread_tot = BLI_system_thread_count();
        }
        for (a=0; a<ps->thread_tot; a++) {
-               ps->arena_mt[a] = BLI_memarena_new(1<<16);
+               ps->arena_mt[a] = BLI_memarena_new(1<<16, "project paint arena");
        }
        
        arena = ps->arena_mt[0]; 
        
        if (ps->do_backfacecull && ps->do_mask_normal) {
-               MVert *v = ps->dm_mvert;
                float viewDirPersp[3];
                
                ps->vertFlags = MEM_callocN(sizeof(char) * ps->dm_totvert, "paint-vertFlags");
                
-               for(a=0; a < ps->dm_totvert; a++, v++) {
-                       no[0] = (float)(v->no[0] / 32767.0f);
-                       no[1] = (float)(v->no[1] / 32767.0f);
-                       no[2] = (float)(v->no[2] / 32767.0f);
+               for(a=0, mv=ps->dm_mvert; a < ps->dm_totvert; a++, mv++) {
+                       normal_short_to_float_v3(no, mv->no);
                        
                        if (ps->is_ortho) {
                                if (angle_normalized_v3v3(ps->viewDir, no) >= ps->normal_angle) { /* 1 vert of this face is towards us */
@@ -3015,7 +3102,7 @@ static void project_paint_begin(ProjPaintState *ps)
                                }
                        }
                        else {
-                               sub_v3_v3v3(viewDirPersp, ps->viewPos, v->co);
+                               sub_v3_v3v3(viewDirPersp, ps->viewPos, mv->co);
                                normalize_v3(viewDirPersp);
                                if (angle_normalized_v3v3(viewDirPersp, no) >= ps->normal_angle) { /* 1 vert of this face is towards us */
                                        ps->vertFlags[a] |= PROJ_VERT_CULL;
@@ -3035,7 +3122,7 @@ static void project_paint_begin(ProjPaintState *ps)
                        BLI_linklist_prepend_arena(&ps->vertFaces[mf->v2], SET_INT_IN_POINTER(face_index), arena);
                        BLI_linklist_prepend_arena(&ps->vertFaces[mf->v3], SET_INT_IN_POINTER(face_index), arena);
                        if (mf->v4) {
-                               BLI_linklist_prepend_arena(&ps->vertFaces[ mf->v4 ], SET_INT_IN_POINTER(face_index), arena);
+                               BLI_linklist_prepend_arena(&ps->vertFaces[mf->v4], SET_INT_IN_POINTER(face_index), arena);
                        }
                }
 #endif
@@ -3106,7 +3193,7 @@ static void project_paint_begin(ProjPaintState *ps)
                                        }
                                }
                                else {
-                                       if (SIDE_OF_LINE(v1coSS, v2coSS, v3coSS) < 0.0f) {
+                                       if (line_point_side_v2(v1coSS, v2coSS, v3coSS) < 0.0f) {
                                                continue;
                                        }
                                        
@@ -3129,7 +3216,7 @@ static void project_paint_begin(ProjPaintState *ps)
                        if (image_index != -1) {
                                /* Initialize the faces screen pixels */
                                /* Add this to a list to initialize later */
-                               project_paint_delayed_face_init(ps, mf, tf, face_index);
+                               project_paint_delayed_face_init(ps, mf, face_index);
                        }
                }
        }
@@ -3154,14 +3241,13 @@ static void project_paint_begin_clone(ProjPaintState *ps, int mouse[2])
        /* setup clone offset */
        if (ps->tool == PAINT_TOOL_CLONE) {
                float projCo[4];
-               float *curs= give_cursor(ps->scene, ps->v3d);
-               VECCOPY(projCo, curs);
+               copy_v3_v3(projCo, give_cursor(ps->scene, ps->v3d));
                mul_m4_v3(ps->ob->imat, projCo);
                
                projCo[3] = 1.0f;
                mul_m4_v4(ps->projectMat, projCo);
-               ps->cloneOffset[0] = mouse[0] - ((float)(ps->ar->winx/2.0f)+(ps->ar->winx/2.0f)*projCo[0]/projCo[3]);
-               ps->cloneOffset[1] = mouse[1] - ((float)(ps->ar->winy/2.0f)+(ps->ar->winy/2.0f)*projCo[1]/projCo[3]);
+               ps->cloneOffset[0] = mouse[0] - ((float)(ps->winx/2.0f)+(ps->winx/2.0f)*projCo[0]/projCo[3]);
+               ps->cloneOffset[1] = mouse[1] - ((float)(ps->winy/2.0f)+(ps->winy/2.0f)*projCo[1]/projCo[3]);
        }       
 }      
 
@@ -3275,6 +3361,18 @@ static void project_paint_end(ProjPaintState *ps)
                BLI_memarena_free(ps->arena_mt[a]);
        }
        
+       /* copy for subsurf/multires, so throw away */
+       if(ps->dm->type != DM_TYPE_CDDM) {
+               if(ps->dm_mvert) MEM_freeN(ps->dm_mvert);
+               if(ps->dm_mface) MEM_freeN(ps->dm_mface);
+               /* looks like these dont need copying */
+               /*
+               if(ps->dm_mtface) MEM_freeN(ps->dm_mtface);
+               if(ps->dm_mtface_clone) MEM_freeN(ps->dm_mtface_clone);
+               if(ps->dm_mtface_stencil) MEM_freeN(ps->dm_mtface_stencil);
+               */
+       }
+
        if(ps->dm_release)
                ps->dm->release(ps->dm);
 }
@@ -3347,33 +3445,48 @@ static int project_image_refresh_tagged(ProjPaintState *ps)
 /* run this per painting onto each mouse location */
 static int project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2])
 {
-       float min_brush[2], max_brush[2];
-       float size_half = ((float)ps->brush->size) * 0.5f;
-       
-       /* so we dont have a bucket bounds that is way too small to paint into */
-       // if (size_half < 1.0f) size_half = 1.0f; // this dosnt work yet :/
-       
-       min_brush[0] = mval_f[0] - size_half;
-       min_brush[1] = mval_f[1] - size_half;
-       
-       max_brush[0] = mval_f[0] + size_half;
-       max_brush[1] = mval_f[1] + size_half;
-       
-       /* offset to make this a valid bucket index */
-       project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax);
-       
-       /* mouse outside the model areas? */
-       if (ps->bucketMin[0]==ps->bucketMax[0] || ps->bucketMin[1]==ps->bucketMax[1]) {
-               return 0;
+       if(ps->source==PROJ_SRC_VIEW) {
+               float min_brush[2], max_brush[2];
+               const float radius = (float)brush_size(ps->brush);
+
+               /* so we dont have a bucket bounds that is way too small to paint into */
+               // if (radius < 1.0f) radius = 1.0f; // this doesn't work yet :/
+
+               min_brush[0] = mval_f[0] - radius;
+               min_brush[1] = mval_f[1] - radius;
+
+               max_brush[0] = mval_f[0] + radius;
+               max_brush[1] = mval_f[1] + radius;
+
+               /* offset to make this a valid bucket index */
+               project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax);
+
+               /* mouse outside the model areas? */
+               if (ps->bucketMin[0]==ps->bucketMax[0] || ps->bucketMin[1]==ps->bucketMax[1]) {
+                       return 0;
+               }
+
+               ps->context_bucket_x = ps->bucketMin[0];
+               ps->context_bucket_y = ps->bucketMin[1];
+       }
+       else { /* reproject: PROJ_SRC_* */
+               ps->bucketMin[0]= 0;
+               ps->bucketMin[1]= 0;
+
+               ps->bucketMax[0]= ps->buckets_x;
+               ps->bucketMax[1]= ps->buckets_y;
+
+               ps->context_bucket_x = 0;
+               ps->context_bucket_y = 0;
        }
-       
-       ps->context_bucket_x = ps->bucketMin[0];
-       ps->context_bucket_y = ps->bucketMin[1];
        return 1;
 }
 
+
 static int project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf *bucket_bounds, const float mval[2])
 {
+       const int diameter= 2*brush_size(ps->brush);
+
        if (ps->thread_tot > 1)
                BLI_lock_thread(LOCK_CUSTOM1);
        
@@ -3385,7 +3498,9 @@ static int project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf
                        /* use bucket_bounds for project_bucket_isect_circle and project_bucket_init*/
                        project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds);
                        
-                       if (project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, ps->brush->size * ps->brush->size, bucket_bounds)) {
+                       if (    (ps->source != PROJ_SRC_VIEW) ||
+                                       project_bucket_isect_circle(mval, (float)(diameter*diameter), bucket_bounds)
+                       ) {
                                *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x);
                                ps->context_bucket_x++;
                                
@@ -3432,14 +3547,28 @@ static void blend_color_mix(unsigned char *cp, const unsigned char *cp1, const u
 
 static void blend_color_mix_float(float *cp, const float *cp1, const float *cp2, const float fac)
 {
-       const float mfac= 1.0-fac;
+       const float mfac= 1.0f-fac;
        cp[0]= mfac*cp1[0] + fac*cp2[0];
        cp[1]= mfac*cp1[1] + fac*cp2[1];
        cp[2]= mfac*cp1[2] + fac*cp2[2];
        cp[3]= mfac*cp1[3] + fac*cp2[3];
 }
 
-static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask)
+static void blend_color_mix_accum(unsigned char *cp, const unsigned char *cp1, const unsigned char *cp2, const int fac)
+{
+       /* this and other blending modes previously used >>8 instead of /255. both
+          are not equivalent (>>8 is /256), and the former results in rounding
+          errors that can turn colors black fast after repeated blending */
+       const int mfac= 255-fac;
+       const int alpha= cp1[3] + ((fac * cp2[3]) / 255);
+
+       cp[0]= (mfac*cp1[0]+fac*cp2[0])/255;
+       cp[1]= (mfac*cp1[1]+fac*cp2[1])/255;
+       cp[2]= (mfac*cp1[2]+fac*cp2[2])/255;
+       cp[3]= alpha > 255 ? 255 : alpha;
+}
+
+static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float alpha, float mask)
 {
        if (ps->is_airbrush==0 && mask < 1.0f) {
                projPixel->newColor.uint = IMB_blend_color(projPixel->newColor.uint, ((ProjPixelClone*)projPixel)->clonepx.uint, (int)(alpha*255), ps->blend);
@@ -3450,7 +3579,7 @@ static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, floa
        }
 }
 
-static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask)
+static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, float alpha, float mask)
 {
        if (ps->is_airbrush==0 && mask < 1.0f) {
                IMB_blend_color_float(projPixel->newColor.f, projPixel->newColor.f, ((ProjPixelClone *)projPixel)->clonepx.f, alpha, ps->blend);
@@ -3467,7 +3596,7 @@ static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, fl
  * accumulation of color greater then 'projPixel->mask' however in the case of smear its not 
  * really that important to be correct as it is with clone and painting 
  */
-static void do_projectpaint_smear(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask, MemArena *smearArena, LinkNode **smearPixels, float co[2])
+static void do_projectpaint_smear(ProjPaintState *ps, ProjPixel *projPixel, float alpha, float mask, MemArena *smearArena, LinkNode **smearPixels, float co[2])
 {
        unsigned char rgba_ub[4];
        
@@ -3478,7 +3607,7 @@ static void do_projectpaint_smear(ProjPaintState *ps, ProjPixel *projPixel, floa
        BLI_linklist_prepend_arena(smearPixels, (void *)projPixel, smearArena);
 } 
 
-static void do_projectpaint_smear_f(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask, MemArena *smearArena, LinkNode **smearPixels_f, float co[2])
+static void do_projectpaint_smear_f(ProjPaintState *ps, ProjPixel *projPixel, float alpha, float mask, MemArena *smearArena, LinkNode **smearPixels_f, float co[2])
 {
        unsigned char rgba_ub[4];
        unsigned char rgba_smear[4];
@@ -3557,7 +3686,6 @@ static void *do_projectpaint_thread(void *ph_v)
        
        float rgba[4], alpha, dist_nosqrt, dist;
        
-       float brush_size_sqared;
        float falloff;
        int bucket_index;
        int is_floatbuf = 0;
@@ -3565,27 +3693,26 @@ static void *do_projectpaint_thread(void *ph_v)
        rctf bucket_bounds;
        
        /* for smear only */
-       float pos_ofs[2];
+       float pos_ofs[2] = {0};
        float co[2];
        float mask = 1.0f; /* airbrush wont use mask */
        unsigned short mask_short;
-       float size_half = ((float)ps->brush->size) * 0.5f;
+       const float radius= (float)brush_size(ps->brush);
+       const float radius_squared= radius*radius; /* avoid a square root with every dist comparison */
+       
+       short lock_alpha= ELEM(ps->brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? 0 : ps->brush->flag & BRUSH_LOCK_ALPHA;
        
        LinkNode *smearPixels = NULL;
        LinkNode *smearPixels_f = NULL;
        MemArena *smearArena = NULL; /* mem arena for this brush projection only */
        
-       
        if (tool==PAINT_TOOL_SMEAR) {
                pos_ofs[0] = pos[0] - lastpos[0];
                pos_ofs[1] = pos[1] - lastpos[1];
                
-               smearArena = BLI_memarena_new(1<<16);
+               smearArena = BLI_memarena_new(1<<16, "paint smear arena");
        }
        
-       /* avoid a square root with every dist comparison */
-       brush_size_sqared = ps->brush->size * ps->brush->size; 
-       
        /* printf("brush bounds %d %d %d %d\n", bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */
        
        while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) {                              
@@ -3596,91 +3723,120 @@ static void *do_projectpaint_thread(void *ph_v)
                        project_bucket_init(ps, thread_index, bucket_index, &bucket_bounds);
                }
 
-               for (node = ps->bucketRect[bucket_index]; node; node = node->next) {
-                       
-                       projPixel = (ProjPixel *)node->link;
+               if(ps->source != PROJ_SRC_VIEW) {
+
+                       /* Re-Projection, simple, no brushes! */
                        
-                       /*dist = len_v2v2(projPixel->projCoSS, pos);*/ /* correct but uses a sqrtf */
-                       dist_nosqrt = Vec2Lenf_nosqrt(projPixel->projCoSS, pos);
+                       for (node = ps->bucketRect[bucket_index]; node; node = node->next) {
+                               projPixel = (ProjPixel *)node->link;
+
+                               bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, projPixel->projCoSS[0], projPixel->projCoSS[1]);
+                               if(projPixel->newColor.ch[3]) {
+                                       mask = ((float)projPixel->mask)/65535.0f;
+                                       blend_color_mix_accum(projPixel->pixel.ch_pt,  projPixel->origColor.ch, projPixel->newColor.ch, (int)(mask*projPixel->newColor.ch[3]));
+
+                               }
+                       }
+               }
+               else {
+                       /* Normal brush painting */
                        
-                       /*if (dist < s->brush->size) {*/ /* correct but uses a sqrtf */
-                       if (dist_nosqrt < brush_size_sqared && (dist=sqrtf(dist_nosqrt)) < size_half) {
-                               falloff = brush_curve_strength_clamp(ps->brush, dist, size_half);
-                               if (falloff > 0.0f) {
-                                       if (ps->is_texbrush) {
-                                               brush_sample_tex(ps->brush, projPixel->projCoSS, rgba);
-                                               alpha = rgba[3];
-                                       } else {
-                                               alpha = 1.0f;
-                                       }
-                                       
-                                       if (ps->is_airbrush) {
-                                               /* for an aurbrush there is no real mask, so just multiply the alpha by it */
-                                               alpha *= falloff * ps->brush->alpha;
-                                               mask = ((float)projPixel->mask)/65535.0f;
-                                       }
-                                       else {
-                                               /* This brush dosnt accumulate so add some curve to the brushes falloff */
-                                               falloff = 1.0f - falloff;
-                                               falloff = 1.0f - (falloff * falloff);
+                       for (node = ps->bucketRect[bucket_index]; node; node = node->next) {
+
+                               projPixel = (ProjPixel *)node->link;
+
+                               /*dist = len_v2v2(projPixel->projCoSS, pos);*/ /* correct but uses a sqrtf */
+                               dist_nosqrt = Vec2Lenf_nosqrt(projPixel->projCoSS, pos);
+
+                               /*if (dist < radius) {*/ /* correct but uses a sqrtf */
+                               if (dist_nosqrt <= radius_squared) {
+                                       dist=sqrtf(dist_nosqrt);
+
+                                       falloff = brush_curve_strength_clamp(ps->brush, dist, radius);
+
+                                       if (falloff > 0.0f) {
+                                               if (ps->is_texbrush) {
+                                                       /* note, for clone and smear, we only use the alpha, could be a special function */
+                                                       brush_sample_tex(ps->brush, projPixel->projCoSS, rgba, thread_index);
+                                                       alpha = rgba[3];
+                                               } else {
+                                                       alpha = 1.0f;
+                                               }
                                                
-                                               mask_short = projPixel->mask * (ps->brush->alpha * falloff);
-                                               if (mask_short > projPixel->mask_max) {
-                                                       mask = ((float)mask_short)/65535.0f;
-                                                       projPixel->mask_max = mask_short;
+                                               if (ps->is_airbrush) {
+                                                       /* for an aurbrush there is no real mask, so just multiply the alpha by it */
+                                                       alpha *= falloff * brush_alpha(ps->brush);
+                                                       mask = ((float)projPixel->mask)/65535.0f;
                                                }
                                                else {
-                                                       /*mask = ((float)projPixel->mask_max)/65535.0f;*/
+                                                       /* This brush dosnt accumulate so add some curve to the brushes falloff */
+                                                       falloff = 1.0f - falloff;
+                                                       falloff = 1.0f - (falloff * falloff);
                                                        
-                                                       /* Go onto the next pixel */
-                                                       continue;
+                                                       mask_short = (unsigned short)(projPixel->mask * (brush_alpha(ps->brush) * falloff));
+                                                       if (mask_short > projPixel->mask_max) {
+                                                               mask = ((float)mask_short)/65535.0f;
+                                                               projPixel->mask_max = mask_short;
+                                                       }
+                                                       else {
+                                                               /*mask = ((float)projPixel->mask_max)/65535.0f;*/
+
+                                                               /* Go onto the next pixel */
+                                                               continue;
+                                                       }
                                                }
-                                       }
-                                       
-                                       if (alpha > 0.0f) {
                                                
-                                               if (last_index != projPixel->image_index) {
-                                                       last_index = projPixel->image_index;
-                                                       last_projIma = projImages + last_index;
+                                               if (alpha > 0.0f) {
+
+                                                       if (last_index != projPixel->image_index) {
+                                                               last_index = projPixel->image_index;
+                                                               last_projIma = projImages + last_index;
+
+                                                               last_projIma->touch = 1;
+                                                               is_floatbuf = last_projIma->ibuf->rect_float ? 1 : 0;
+                                                       }
+
+                                                       last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index;
+                                                       last_partial_redraw_cell->x1 = MIN2(last_partial_redraw_cell->x1, projPixel->x_px);
+                                                       last_partial_redraw_cell->y1 = MIN2(last_partial_redraw_cell->y1, projPixel->y_px);
+
+                                                       last_partial_redraw_cell->x2 = MAX2(last_partial_redraw_cell->x2, projPixel->x_px+1);
+                                                       last_partial_redraw_cell->y2 = MAX2(last_partial_redraw_cell->y2, projPixel->y_px+1);
+
                                                        
-                                                       last_projIma->touch = 1;
-                                                       is_floatbuf = last_projIma->ibuf->rect_float ? 1 : 0;
-                                               }
-                                               
-                                               last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index;
-                                               last_partial_redraw_cell->x1 = MIN2(last_partial_redraw_cell->x1, projPixel->x_px);
-                                               last_partial_redraw_cell->y1 = MIN2(last_partial_redraw_cell->y1, projPixel->y_px);
-                                               
-                                               last_partial_redraw_cell->x2 = MAX2(last_partial_redraw_cell->x2, projPixel->x_px+1);
-                                               last_partial_redraw_cell->y2 = MAX2(last_partial_redraw_cell->y2, projPixel->y_px+1);
-                                               
-                                               
-                                               switch(tool) {
-                                               case PAINT_TOOL_CLONE:
-                                                       if (is_floatbuf) {
-                                                               if (((ProjPixelClone *)projPixel)->clonepx.f[3]) {
-                                                                       do_projectpaint_clone_f(ps, projPixel, rgba, alpha, mask);
+                                                       switch(tool) {
+                                                       case PAINT_TOOL_CLONE:
+                                                               if (is_floatbuf) {
+                                                                       if (((ProjPixelClone *)projPixel)->clonepx.f[3]) {
+                                                                               do_projectpaint_clone_f(ps, projPixel, alpha, mask); /* rgba isnt used for cloning, only alpha */
+                                                                       }
                                                                }
-                                                       }
-                                                       else {
-                                                               if (((ProjPixelClone*)projPixel)->clonepx.ch[3]) { 
-                                                                       do_projectpaint_clone(ps, projPixel, rgba, alpha, mask);
+                                                               else {
+                                                                       if (((ProjPixelClone*)projPixel)->clonepx.ch[3]) {
+                                                                               do_projectpaint_clone(ps, projPixel, alpha, mask); /* rgba isnt used for cloning, only alpha */
+                                                                       }
                                                                }
+                                                               break;
+                                                       case PAINT_TOOL_SMEAR:
+                                                               sub_v2_v2v2(co, projPixel->projCoSS, pos_ofs);
+
+                                                               if (is_floatbuf)        do_projectpaint_smear_f(ps, projPixel, alpha, mask, smearArena, &smearPixels_f, co);
+                                                               else                            do_projectpaint_smear(ps, projPixel, alpha, mask, smearArena, &smearPixels, co);
+                                                               break;
+                                                       default:
+                                                               if (is_floatbuf)        do_projectpaint_draw_f(ps, projPixel, rgba, alpha, mask);
+                                                               else                            do_projectpaint_draw(ps, projPixel, rgba, alpha, mask);
+                                                               break;
                                                        }
-                                                       break;
-                                               case PAINT_TOOL_SMEAR:
-                                                       sub_v2_v2v2(co, projPixel->projCoSS, pos_ofs);
-                                                       
-                                                       if (is_floatbuf)        do_projectpaint_smear_f(ps, projPixel, rgba, alpha, mask, smearArena, &smearPixels_f, co);
-                                                       else                            do_projectpaint_smear(ps, projPixel, rgba, alpha, mask, smearArena, &smearPixels, co);
-                                                       break;
-                                               default:
-                                                       if (is_floatbuf)        do_projectpaint_draw_f(ps, projPixel, rgba, alpha, mask);
-                                                       else                            do_projectpaint_draw(ps, projPixel, rgba, alpha, mask);
-                                                       break;
                                                }
+
+                                               if(lock_alpha) {
+                                                       if (is_floatbuf)        projPixel->pixel.f_pt[3]= projPixel->origColor.f[3];
+                                                       else                            projPixel->pixel.ch_pt[3]= projPixel->origColor.ch[3];
+                                               }
+
+                                               /* done painting */
                                        }
-                                       /* done painting */
                                }
                        }
                }
@@ -3705,7 +3861,7 @@ static void *do_projectpaint_thread(void *ph_v)
        return NULL;
 }
 
-static int project_paint_op(void *state, ImBuf *ibufb, float *lastpos, float *pos)
+static int project_paint_op(void *state, ImBuf *UNUSED(ibufb), float *lastpos, float *pos)
 {
        /* First unpack args from the struct */
        ProjPaintState *ps = (ProjPaintState *)state;
@@ -3729,8 +3885,8 @@ static int project_paint_op(void *state, ImBuf *ibufb, float *lastpos, float *po
                //memset(&handles[a], 0, sizeof(BakeShade));
                
                handles[a].ps = ps;
-               VECCOPY2D(handles[a].mval, pos);
-               VECCOPY2D(handles[a].prevmval, lastpos);
+               copy_v2_v2(handles[a].mval, pos);
+               copy_v2_v2(handles[a].prevmval, lastpos);
                
                /* thread spesific */
                handles[a].thread_index = a;
@@ -3772,14 +3928,14 @@ static int project_paint_op(void *state, ImBuf *ibufb, float *lastpos, float *po
 }
 
 
-static int project_paint_sub_stroke(ProjPaintState *ps, BrushPainter *painter, int *prevmval_i, int *mval_i, double time, float pressure)
+static int project_paint_sub_stroke(ProjPaintState *ps, BrushPainter *painter, int *UNUSED(prevmval_i), int *mval_i, double time, float pressure)
 {
        
        /* Use mouse coords as floats for projection painting */
        float pos[2];
        
-       pos[0] = mval_i[0];
-       pos[1] = mval_i[1];
+       pos[0] = (float)(mval_i[0]);
+       pos[1] = (float)(mval_i[1]);
        
        // we may want to use this later 
        // brush_painter_require_imbuf(painter, ((ibuf->rect_float)? 1: 0), 0, 0);
@@ -3808,7 +3964,7 @@ static int project_paint_stroke(ProjPaintState *ps, BrushPainter *painter, int *
 
 /* Imagepaint Partial Redraw & Dirty Region */
 
-static void imapaint_clear_partial_redraw()
+static void imapaint_clear_partial_redraw(void)
 {
        memset(&imapaintpartial, 0, sizeof(imapaintpartial));
 }
@@ -3857,8 +4013,9 @@ static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, s
        if(ibuf->rect_float)
                /* TODO - should just update a portion from imapaintpartial! */
                imb_freerectImBuf(ibuf); /* force recreate of char rect */
+       
        if(ibuf->mipmap[0])
-               imb_freemipmapImBuf(ibuf);
+               ibuf->userflags |= IB_MIPMAP_INVALID;
 
        /* todo: should set_tpage create ->rect? */
        if(texpaint || (sima && sima->lock)) {
@@ -3903,7 +4060,8 @@ static int imapaint_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, flo
 {
        float inrgb[3];
 
-       if ((x >= ibuf->x) || (y >= ibuf->y)) {
+       // XXX: signed unsigned mismatch
+       if ((x >= (unsigned int)(ibuf->x)) || (y >= (unsigned int)(ibuf->y))) {
                if (torus) imapaint_ibuf_get_set_rgb(ibuf, x, y, 1, 0, inrgb);
                else return 0;
        }
@@ -3968,10 +4126,68 @@ static void imapaint_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, short toru
        }
 }
 
+static void imapaint_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 imapaint_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);
+       imapaint_set_region(&region[tot++], destx, desty, srcx, srcy, w, h);
+
+       /* do 3 other rects if needed */
+       if (w < origw)
+               imapaint_set_region(&region[tot++], (destx+w)%dbuf->x, desty, (srcx+w)%sbuf->x, srcy, origw-w, h);
+       if (h < origh)
+               imapaint_set_region(&region[tot++], destx, (desty+h)%dbuf->y, srcx, (srcy+h)%sbuf->y, w, origh-h);
+       if ((w < origw) && (h < origh))
+               imapaint_set_region(&region[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 imapaint_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos)
 {
-       IMB_rectblend_torus(ibufb, ibuf, 0, 0, pos[0], pos[1],
-               ibufb->x, ibufb->y, IMB_BLEND_COPY_RGB);
+       ImagePaintRegion region[4];
+       int a, tot;
+
+       imapaint_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y);
+       tot= imapaint_torus_split_region(region, ibufb, ibuf);
+
+       for(a=0; a<tot; a++)
+               IMB_rectblend(ibufb, ibuf, region[a].destx, region[a].desty,
+                       region[a].srcx, region[a].srcy,
+                       region[a].width, region[a].height, IMB_BLEND_COPY_RGB);
 }
 
 static ImBuf *imapaint_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos)
@@ -3979,7 +4195,7 @@ static ImBuf *imapaint_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->depth, ibufb->flags, 0);
+       ImBuf *clonebuf= IMB_allocImBuf(w, h, ibufb->depth, ibufb->flags);
 
        IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h);
        IMB_rectblend(clonebuf, ibuf, destx, desty, srcx, srcy, w, h,
@@ -4001,12 +4217,14 @@ static void imapaint_convert_brushco(ImBuf *ibufb, float *pos, int *ipos)
 static int imapaint_paint_op(void *state, ImBuf *ibufb, float *lastpos, float *pos)
 {
        ImagePaintState *s= ((ImagePaintState*)state);
-       ImBuf *clonebuf= NULL;
+       ImBuf *clonebuf= NULL, *frombuf;
+       ImagePaintRegion region[4];
        short torus= s->brush->flag & BRUSH_TORUS;
        short blend= s->blend;
        float *offset= s->brush->clone.offset;
        float liftpos[2];
        int bpos[2], blastpos[2], bliftpos[2];
+       int a, tot;
 
        imapaint_convert_brushco(ibufb, pos, bpos);
 
@@ -4029,16 +4247,29 @@ static int imapaint_paint_op(void *state, ImBuf *ibufb, float *lastpos, float *p
                clonebuf= imapaint_lift_clone(s->clonecanvas, ibufb, bliftpos);
        }
 
-       imapaint_dirty_region(s->image, s->canvas, bpos[0], bpos[1], ibufb->x, ibufb->y);
+       frombuf= (clonebuf)? clonebuf: ibufb;
+
+       if(torus) {
+               imapaint_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
+               tot= imapaint_torus_split_region(region, s->canvas, frombuf);
+       }
+       else {
+               imapaint_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
+               tot= 1;
+       }
 
        /* blend into canvas */
-       if(torus)
-               IMB_rectblend_torus(s->canvas, (clonebuf)? clonebuf: ibufb,
-                       bpos[0], bpos[1], 0, 0, ibufb->x, ibufb->y, blend);
-       else
-               IMB_rectblend(s->canvas, (clonebuf)? clonebuf: ibufb,
-                       bpos[0], bpos[1], 0, 0, ibufb->x, ibufb->y, blend);
-                       
+       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);
+               
+               IMB_rectblend(s->canvas, frombuf,
+                       region[a].destx, region[a].desty,
+                       region[a].srcx, region[a].srcy,
+                       region[a].width, region[a].height, blend);
+       }
+
        if(clonebuf) IMB_freeImBuf(clonebuf);
 
        return 1;
@@ -4066,7 +4297,10 @@ static int imapaint_canvas_set(ImagePaintState *s, Image *ima)
        ImBuf *ibuf= BKE_image_get_ibuf(ima, s->sima? &s->sima->iuser: NULL);
        
        /* verify that we can paint and set canvas */
-       if(ima->packedfile && ima->rr) {
+       if(ima==NULL) {
+               return 0;
+       }
+       else if(ima->packedfile && ima->rr) {
                s->warnpackedfile = ima->id.name + 2;
                return 0;
        }       
@@ -4074,7 +4308,7 @@ static int imapaint_canvas_set(ImagePaintState *s, Image *ima)
                s->warnmultifile = ima->id.name + 2;
                return 0;
        }
-       else if(!ima || !ibuf || !(ibuf->rect || ibuf->rect_float))
+       else if(!ibuf || !(ibuf->rect || ibuf->rect_float))
                return 0;
 
        s->image= ima;
@@ -4090,10 +4324,17 @@ static int imapaint_canvas_set(ImagePaintState *s, Image *ima)
 
                s->clonecanvas= ibuf;
 
+               /* temporarily add float rect for cloning */
                if(s->canvas->rect_float && !s->clonecanvas->rect_float) {
-                       /* temporarily add float rect for cloning */
+                       int profile = IB_PROFILE_NONE;
+                       
+                       /* Don't want to color manage, but don't disturb existing profiles */
+                       SWAP(int, s->clonecanvas->profile, profile);
+
                        IMB_float_from_rect(s->clonecanvas);
                        s->clonefreefloat= 1;
+                       
+                       SWAP(int, s->clonecanvas->profile, profile);
                }
                else if(!s->canvas->rect_float && !s->clonecanvas->rect)
                        IMB_rect_from_float(s->clonecanvas);
@@ -4147,7 +4388,7 @@ static int imapaint_paint_stroke(ViewContext *vc, ImagePaintState *s, BrushPaint
                        ibuf= BKE_image_get_ibuf(newimage, s->sima? &s->sima->iuser: NULL);
 
                        if(ibuf && ibuf->rect)
-                               imapaint_pick_uv(s->scene, s->ob, s->me, newfaceindex, mval, newuv);
+                               imapaint_pick_uv(s->scene, s->ob, newfaceindex, mval, newuv);
                        else {
                                newimage = NULL;
                                newuv[0] = newuv[1] = 0.0f;
@@ -4158,8 +4399,8 @@ static int imapaint_paint_stroke(ViewContext *vc, ImagePaintState *s, BrushPaint
 
                /* see if stroke is broken, and if so finish painting in old position */
                if (s->image) {
-                       imapaint_pick_uv(s->scene, s->ob, s->me, s->faceindex, mval, fwuv);
-                       imapaint_pick_uv(s->scene, s->ob, s->me, newfaceindex, prevmval, bkuv);
+                       imapaint_pick_uv(s->scene, s->ob, s->faceindex, mval, fwuv);
+                       imapaint_pick_uv(s->scene, s->ob, newfaceindex, prevmval, bkuv);
 
                        if (newimage == s->image)
                                breakstroke= texpaint_break_stroke(s->uv, fwuv, bkuv, newuv);
@@ -4170,7 +4411,7 @@ static int imapaint_paint_stroke(ViewContext *vc, ImagePaintState *s, BrushPaint
                        fwuv[0]= fwuv[1]= 0.0f;
 
                if (breakstroke) {
-                       imapaint_pick_uv(s->scene, s->ob, s->me, s->faceindex, mval, fwuv);
+                       imapaint_pick_uv(s->scene, s->ob, s->faceindex, mval, fwuv);
                        redraw |= imapaint_paint_sub_stroke(s, painter, s->image, texpaint,
                                fwuv, time, 1, pressure);
                        imapaint_clear_partial_redraw();
@@ -4253,12 +4494,10 @@ static int image_paint_3d_poll(bContext *C)
 
 static int image_paint_2d_clone_poll(bContext *C)
 {
-       Scene *scene= CTX_data_scene(C);
-       ToolSettings *settings= scene->toolsettings;
        Brush *brush= image_paint_brush(C);
 
        if(!CTX_wm_region_view3d(C) && image_paint_poll(C))
-               if(brush && (settings->imapaint.tool == PAINT_TOOL_CLONE))
+               if(brush && (brush->imagepaint_tool == PAINT_TOOL_CLONE))
                        if(brush->clone.image)
                                return 1;
        
@@ -4283,11 +4522,13 @@ typedef struct PaintOperation {
        int first;
        int prevmouse[2];
        float prev_pressure; /* need this since we dont get tablet events for pressure change */
-       int brush_size_orig;
+       int orig_brush_size;
        double starttime;
 
        ViewContext vc;
        wmTimer *timer;
+
+       short restore_projection;
 } PaintOperation;
 
 static void paint_redraw(bContext *C, ImagePaintState *s, int final)
@@ -4296,29 +4537,8 @@ static void paint_redraw(bContext *C, ImagePaintState *s, int final)
                if(s->image)
                        GPU_free_image(s->image);
 
+               /* compositor listener deals with updating */
                WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, s->image);
-
-               // XXX node update
-#if 0
-               if(!s->sima && s->image) {
-                       /* after paint, tag Image or RenderResult nodes changed */
-                       if(s->scene->nodetree) {
-                               imagepaint_composite_tags(s->scene->nodetree, image, &s->sima->iuser);
-                       }
-                       /* signal composite (hurmf, need an allqueue?) */
-                       if(s->sima->lock) {
-                               ScrArea *sa;
-                               for(sa=s->screen->areabase.first; sa; sa= sa->next) {
-                                       if(sa->spacetype==SPACE_NODE) {
-                                               if(((SpaceNode *)sa->spacedata.first)->treetype==NTREE_COMPOSIT) {
-                                                       addqueue(sa->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
-                                                       break;
-                                               }
-                                       }
-                               }
-                       }
-               }               
-#endif
        }
        else {
                if(!s->sima || !s->sima->lock)
@@ -4328,21 +4548,81 @@ static void paint_redraw(bContext *C, ImagePaintState *s, int final)
        }
 }
 
+/* initialize project paint settings from context */
+static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps)
+{
+       Scene *scene= CTX_data_scene(C);
+       ToolSettings *settings= scene->toolsettings;
+       Brush *brush= paint_brush(&settings->imapaint.paint);
+
+       /* brush */
+       ps->brush = brush;
+       ps->tool = brush->imagepaint_tool;
+       ps->blend = brush->blend;
+
+       ps->is_airbrush = (brush->flag & BRUSH_AIRBRUSH) ? 1 : 0;
+       ps->is_texbrush = (brush->mtex.tex) ? 1 : 0;
+
+
+       /* these can be NULL */
+       ps->v3d= CTX_wm_view3d(C);
+       ps->rv3d= CTX_wm_region_view3d(C);
+       ps->ar= CTX_wm_region(C);
+
+       ps->scene= scene;
+       ps->ob= ob; /* allow override of active object */
+
+       /* setup projection painting data */
+       ps->do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? 0 : 1;
+       ps->do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? 0 : 1;
+       ps->do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? 0 : 1;
+
+       if (ps->tool == PAINT_TOOL_CLONE)
+               ps->do_layer_clone = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE);
+
+       ps->do_layer_stencil = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) ? 1 : 0;
+       ps->do_layer_stencil_inv = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) ? 1 : 0;
+
+
+#ifndef PROJ_DEBUG_NOSEAMBLEED
+       ps->seam_bleed_px = settings->imapaint.seam_bleed; /* pixel num to bleed */
+#endif
+
+       if(ps->do_mask_normal) {
+               ps->normal_angle_inner = settings->imapaint.normal_angle;
+               ps->normal_angle = (ps->normal_angle_inner + 90.0f) * 0.5f;
+       }
+       else {
+               ps->normal_angle_inner= ps->normal_angle= settings->imapaint.normal_angle;
+       }
+
+       ps->normal_angle_inner *=       (float)(M_PI_2 / 90);
+       ps->normal_angle *=                     (float)(M_PI_2 / 90);
+       ps->normal_angle_range = ps->normal_angle - ps->normal_angle_inner;
+
+       if(ps->normal_angle_range <= 0.0f)
+               ps->do_mask_normal = 0; /* no need to do blending */
+}
+
 static int texture_paint_init(bContext *C, wmOperator *op)
 {
        Scene *scene= CTX_data_scene(C);
        ToolSettings *settings= scene->toolsettings;
-       PaintOperation *pop;
-       Brush *brush;
+       Brush *brush= paint_brush(&settings->imapaint.paint);
+       PaintOperation *pop= MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */
 
-       pop= MEM_callocN(sizeof(PaintOperation), "PaintOperation");
        pop->first= 1;
        op->customdata= pop;
        
+       /* XXX: Soften tool does not support projection painting atm, so just disable
+               projection for this brush */
+       if(brush->imagepaint_tool == PAINT_TOOL_SOFTEN) {
+               settings->imapaint.flag |= IMAGEPAINT_PROJECT_DISABLE;
+               pop->restore_projection = 1;
+       }
+
        /* initialize from context */
        if(CTX_wm_region_view3d(C)) {
-               pop->ps.v3d= CTX_wm_view3d(C);
-               pop->ps.rv3d= CTX_wm_region_view3d(C);
                pop->mode= PAINT_MODE_3D;
 
                if(!(settings->imapaint.flag & IMAGEPAINT_PROJECT_DISABLE))
@@ -4356,43 +4636,24 @@ static int texture_paint_init(bContext *C, wmOperator *op)
        }
 
        pop->s.scene= scene;
-       pop->ps.scene= scene;
        pop->s.screen= CTX_wm_screen(C);
-       pop->ps.ar= CTX_wm_region(C);
-
-       /* intialize brush */
-       brush= paint_brush(&settings->imapaint.paint);
-       if(!brush)
-               return 0;
 
        pop->s.brush = brush;
-       pop->s.tool = settings->imapaint.tool;
+       pop->s.tool = brush->imagepaint_tool;
        if(pop->mode == PAINT_MODE_3D && (pop->s.tool == PAINT_TOOL_CLONE))
                pop->s.tool = PAINT_TOOL_DRAW;
-       pop->s.blend = pop->s.brush->blend;
-       
-       if(pop->mode == PAINT_MODE_3D_PROJECT) {
-               pop->ps.brush = pop->s.brush;
-               pop->ps.tool = pop->s.tool;
-               pop->ps.blend = pop->s.blend;
-
-               pop->brush_size_orig = pop->ps.brush->size; /* not nice hack because 1 size brushes always fail with projection paint */
-       }
+       pop->s.blend = brush->blend;
+       pop->orig_brush_size= brush_size(brush);
 
        if(pop->mode != PAINT_MODE_2D) {
-               pop->ps.ob = pop->s.ob = OBACT;
-               if (!pop->s.ob || !(pop->s.ob->lay & pop->ps.v3d->lay)) return 0;
+               pop->s.ob = OBACT;
                pop->s.me = get_mesh(pop->s.ob);
                if (!pop->s.me) return 0;
-
-               /* Dont allow brush size below 2 */
-               if (pop->ps.brush && pop->ps.brush->size<=1)
-                       pop->ps.brush->size = 2;
        }
        else {
                pop->s.image = pop->s.sima->image;
 
-               if(!imapaint_canvas_set(&pop->s, pop->s.sima->image)) {
+               if(!imapaint_canvas_set(&pop->s, pop->s.image)) {
                        if(pop->s.warnmultifile)
                                BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint");
                        if(pop->s.warnpackedfile)
@@ -4401,40 +4662,23 @@ static int texture_paint_init(bContext *C, wmOperator *op)
                        return 0;
                }
        }
-       
+
        /* note, if we have no UVs on the derived mesh, then we must return here */
        if(pop->mode == PAINT_MODE_3D_PROJECT) {
-               /* setup projection painting data */
-               pop->ps.do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? 0 : 1;
-               pop->ps.do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? 0 : 1;
-               pop->ps.do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? 0 : 1;;
-               
-               if (pop->ps.tool == PAINT_TOOL_CLONE)
-                       pop->ps.do_layer_clone = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE);
-               
-               pop->ps.do_layer_stencil = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) ? 1 : 0;
-               pop->ps.do_layer_stencil_inv = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) ? 1 : 0;
-               
-               
-#ifndef PROJ_DEBUG_NOSEAMBLEED
-               pop->ps.seam_bleed_px = settings->imapaint.seam_bleed; /* pixel num to bleed */
-#endif
 
-               if(pop->ps.do_mask_normal) {
-                       pop->ps.normal_angle_inner = settings->imapaint.normal_angle;
-                       pop->ps.normal_angle = (pop->ps.normal_angle_inner + 90.0f) * 0.5f;
-               }
-               else {
-                       pop->ps.normal_angle_inner= pop->ps.normal_angle= settings->imapaint.normal_angle;
-               }
+               /* initialize all data from the context */
+               project_state_init(C, OBACT, &pop->ps);
 
-               pop->ps.normal_angle_inner *=   M_PI_2 / 90;
-               pop->ps.normal_angle *=                 M_PI_2 / 90;
-               pop->ps.normal_angle_range = pop->ps.normal_angle - pop->ps.normal_angle_inner;
-               
-               if(pop->ps.normal_angle_range <= 0.0f)
-                       pop->ps.do_mask_normal = 0; /* no need to do blending */
+               pop->ps.source= PROJ_SRC_VIEW;
+
+               if (pop->ps.ob==NULL || !(pop->ps.ob->lay & pop->ps.v3d->lay))
+                       return 0;
+
+               /* Dont allow brush size below 2 */
+               if (brush_size(brush) < 2)
+                       brush_set_size(brush, 2);
 
+               /* allocate and initialize spacial data structures */
                project_paint_begin(&pop->ps);
                
                if(pop->ps.dm==NULL)
@@ -4442,7 +4686,7 @@ static int texture_paint_init(bContext *C, wmOperator *op)
        }
        
        settings->imapaint.flag |= IMAGEPAINT_DRAWING;
-       undo_paint_push_begin(UNDO_PAINT_IMAGE, "Image Paint",
+       undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
                image_undo_restore, image_undo_free);
 
        /* create painter */
@@ -4459,8 +4703,8 @@ static void paint_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
        int mouse[2], redraw;
 
        RNA_float_get_array(itemptr, "mouse", mousef);
-       mouse[0] = mousef[0];
-       mouse[1] = mousef[1];
+       mouse[0] = (int)(mousef[0]);
+       mouse[1] = (int)(mousef[1]);
        time= RNA_float_get(itemptr, "time");
        pressure= RNA_float_get(itemptr, "pressure");
 
@@ -4497,12 +4741,15 @@ static void paint_exit(bContext *C, wmOperator *op)
        if(pop->timer)
                WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), pop->timer);
 
+       if(pop->restore_projection)
+               settings->imapaint.flag &= ~IMAGEPAINT_PROJECT_DISABLE;
+
        settings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
        imapaint_canvas_free(&pop->s);
        brush_painter_free(pop->painter);
 
        if(pop->mode == PAINT_MODE_3D_PROJECT) {
-               pop->ps.brush->size = pop->brush_size_orig;
+               brush_set_size(pop->ps.brush, pop->orig_brush_size);
                project_paint_end(&pop->ps);
        }
        
@@ -4573,15 +4820,22 @@ static void paint_apply_event(bContext *C, wmOperator *op, wmEvent *event)
 
                /* special exception here for too high pressure values on first touch in
                   windows for some tablets, then we just skip first touch ..  */
-               if ((pop->s.brush->flag & (BRUSH_ALPHA_PRESSURE|BRUSH_SIZE_PRESSURE|BRUSH_SPACING_PRESSURE)) && tablet && (pressure >= 0.99f))
+               if (tablet && (pressure >= 0.99f) && ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || brush_use_alpha_pressure(pop->s.brush) || brush_use_size_pressure(pop->s.brush)))
+                       return;
+
+               /* This can be removed once fixed properly in
+                brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, float pressure, void *user) 
+                at zero pressure we should do nothing 1/2^12 is .0002 which is the sensitivity of the most sensitive pen tablet available*/
+               if (tablet && (pressure < .0002f) && ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || brush_use_alpha_pressure(pop->s.brush) || brush_use_size_pressure(pop->s.brush)))
                        return;
+       
        }
 
        /* fill in stroke */
        RNA_collection_add(op->ptr, "stroke", &itemptr);
 
-       mousef[0] = mouse[0];
-       mousef[1] = mouse[1];
+       mousef[0] = (float)(mouse[0]);
+       mousef[1] = (float)(mouse[1]);
        RNA_float_set_array(&itemptr, "mouse", mousef);
        RNA_float_set(&itemptr, "time", (float)(time - pop->starttime));
        RNA_float_set(&itemptr, "pressure", pressure);
@@ -4623,6 +4877,7 @@ static int paint_modal(bContext *C, wmOperator *op, wmEvent *event)
                        paint_exit(C, op);
                        return OPERATOR_FINISHED;
                case MOUSEMOVE:
+               case INBETWEEN_MOUSEMOVE:
                        paint_apply_event(C, op, event);
                        break;
                case TIMER:
@@ -4681,12 +4936,17 @@ static int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
 
 /************************ cursor drawing *******************************/
 
-static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
+static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata))
 {
        Brush *brush= image_paint_brush(C);
+       Paint *paint= paint_get_active(CTX_data_scene(C));
 
-       if(brush) {
+       if(paint && brush) {
                float zoomx, zoomy;
+
+               if(!(paint->flags & PAINT_SHOW_BRUSH))
+                       return;
+
                glPushMatrix();
 
                glTranslatef((float)x, (float)y, 0.0f);
@@ -4694,13 +4954,13 @@ static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
                if(get_imapaint_zoom(C, &zoomx, &zoomy))
                        glScalef(zoomx, zoomy, 1.0f);
 
-               glColor4ub(255, 255, 255, 128);
+               glColor4f(brush->add_col[0], brush->add_col[1], brush->add_col[2], 0.5f);
                glEnable( GL_LINE_SMOOTH );
                glEnable(GL_BLEND);
-               glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size*0.5f, 40);
+               glutil_draw_lined_arc(0, (float)(M_PI*2.0), (float)brush_size(brush), 40);
                glDisable(GL_BLEND);
                glDisable( GL_LINE_SMOOTH );
-               
+
                glPopMatrix();
        }
 }
@@ -4717,23 +4977,38 @@ static void toggle_paint_cursor(bContext *C, int enable)
                settings->imapaint.paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), image_paint_poll, brush_drawcursor, NULL);
 }
 
+/* enable the paint cursor if it isn't already.
+
+   purpose is to make sure the paint cursor is shown if paint
+   mode is enabled in the image editor. the paint poll will
+   ensure that the cursor is hidden when not in paint mode */
+void ED_space_image_paint_update(wmWindowManager *wm, ToolSettings *settings)
+{
+       ImagePaintSettings *imapaint = &settings->imapaint;
+
+       if(!imapaint->paintcursor) {
+               imapaint->paintcursor =
+                       WM_paint_cursor_activate(wm, image_paint_poll,
+                                                brush_drawcursor, NULL);
+       }
+}
+
 /* ************ image paint radial control *************/
 static int paint_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event)
 {
        float zoom;
        ToolSettings *ts = CTX_data_scene(C)->toolsettings;
        get_imapaint_zoom(C, &zoom, &zoom);
-       toggle_paint_cursor(C, !ts->imapaint.paintcursor);
-       brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5 * zoom);
+       toggle_paint_cursor(C, 0);
+       brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), zoom);
        return WM_radial_control_invoke(C, op, event);
 }
 
 static int paint_radial_control_modal(bContext *C, wmOperator *op, wmEvent *event)
 {
-       ToolSettings *ts = CTX_data_scene(C)->toolsettings;
        int ret = WM_radial_control_modal(C, op, event);
        if(ret != OPERATOR_RUNNING_MODAL)
-               toggle_paint_cursor(C, !ts->imapaint.paintcursor);
+               toggle_paint_cursor(C, 1);
        return ret;
 }
 
@@ -4742,10 +5017,10 @@ static int paint_radial_control_exec(bContext *C, wmOperator *op)
        Brush *brush = paint_brush(&CTX_data_scene(C)->toolsettings->imapaint.paint);
        float zoom;
        int ret;
-       char str[256];
+       char str[64];
        get_imapaint_zoom(C, &zoom, &zoom);
-       ret = brush_radial_control_exec(op, brush, 2.0 / zoom);
-       WM_radial_control_string(op, str, 256);
+       ret = brush_radial_control_exec(op, brush, 1.0f / zoom);
+       WM_radial_control_string(op, str, sizeof(str));
        
        WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush);
 
@@ -4781,9 +5056,7 @@ static void grab_clone_apply(bContext *C, wmOperator *op)
        float delta[2];
 
        RNA_float_get_array(op->ptr, "delta", delta);
-       brush->clone.offset[0] += delta[0];
-       brush->clone.offset[1] += delta[1];
-
+       add_v2_v2(brush->clone.offset, delta);
        ED_region_tag_redraw(CTX_wm_region(C));
 }
 
@@ -4800,8 +5073,7 @@ static int grab_clone_invoke(bContext *C, wmOperator *op, wmEvent *event)
        GrabClone *cmv;
 
        cmv= MEM_callocN(sizeof(GrabClone), "GrabClone");
-       cmv->startoffset[0]= brush->clone.offset[0];
-       cmv->startoffset[1]= brush->clone.offset[1];
+       copy_v2_v2(cmv->startoffset, brush->clone.offset);
        cmv->startx= event->x;
        cmv->starty= event->y;
        op->customdata= cmv;
@@ -4834,8 +5106,7 @@ static int grab_clone_modal(bContext *C, wmOperator *op, wmEvent *event)
                        delta[1]= fy - startfy;
                        RNA_float_set_array(op->ptr, "delta", delta);
 
-                       brush->clone.offset[0]= cmv->startoffset[0];
-                       brush->clone.offset[1]= cmv->startoffset[1];
+                       copy_v2_v2(brush->clone.offset, cmv->startoffset);
 
                        grab_clone_apply(C, op);
                        break;
@@ -4844,7 +5115,7 @@ static int grab_clone_modal(bContext *C, wmOperator *op, wmEvent *event)
        return OPERATOR_RUNNING_MODAL;
 }
 
-static int grab_clone_cancel(bContext *C, wmOperator *op)
+static int grab_clone_cancel(bContext *UNUSED(C), wmOperator *op)
 {
        MEM_freeN(op->customdata);
        return OPERATOR_CANCELLED;
@@ -4892,11 +5163,40 @@ static int sample_color_invoke(bContext *C, wmOperator *op, wmEvent *event)
        ARegion *ar= CTX_wm_region(C);
        int location[2];
 
-       location[0]= event->x - ar->winrct.xmin;
-       location[1]= event->y - ar->winrct.ymin;
-       RNA_int_set_array(op->ptr, "location", location);
+       if(ar) {
+               location[0]= event->x - ar->winrct.xmin;
+               location[1]= event->y - ar->winrct.ymin;
+               RNA_int_set_array(op->ptr, "location", location);
+
+               sample_color_exec(C, op);
+       }
+
+       WM_event_add_modal_handler(C, op);
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+static int sample_color_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+       ARegion *ar= CTX_wm_region(C);
+       int location[2];
+
+       switch(event->type) {
+               case LEFTMOUSE:
+               case RIGHTMOUSE: // XXX hardcoded
+                       return OPERATOR_FINISHED;
+               case MOUSEMOVE:
+                       if(ar) {
+                               location[0]= event->x - ar->winrct.xmin;
+                               location[1]= event->y - ar->winrct.ymin;
+                               RNA_int_set_array(op->ptr, "location", location);
+
+                               sample_color_exec(C, op);
+                       }
+                       break;
+       }
 
-       return sample_color_exec(C, op);
+       return OPERATOR_RUNNING_MODAL;
 }
 
 void PAINT_OT_sample_color(wmOperatorType *ot)
@@ -4908,6 +5208,7 @@ void PAINT_OT_sample_color(wmOperatorType *ot)
        /* api callbacks */
        ot->exec= sample_color_exec;
        ot->invoke= sample_color_invoke;
+       ot->modal= sample_color_modal;
        ot->poll= image_paint_poll;
 
        /* flags */
@@ -5029,7 +5330,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op)
                toggle_paint_cursor(C, 1);
        }
 
-       DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+       DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
        WM_event_add_notifier(C, NC_SCENE|ND_MODE, scene);
 
        return OPERATOR_FINISHED;
@@ -5055,16 +5356,16 @@ static int texture_paint_radial_control_invoke(bContext *C, wmOperator *op, wmEv
 {
        ToolSettings *ts = CTX_data_scene(C)->toolsettings;
        toggle_paint_cursor(C, !ts->imapaint.paintcursor);
-       brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5);
+       brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 1);
        return WM_radial_control_invoke(C, op, event);
 }
 
 static int texture_paint_radial_control_exec(bContext *C, wmOperator *op)
 {
        Brush *brush = paint_brush(&CTX_data_scene(C)->toolsettings->imapaint.paint);
-       int ret = brush_radial_control_exec(op, brush, 2);
-       char str[256];
-       WM_radial_control_string(op, str, 256);
+       int ret = brush_radial_control_exec(op, brush, 1);
+       char str[64];
+       WM_radial_control_string(op, str, sizeof(str));
 
        WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush);
 
@@ -5098,7 +5399,7 @@ void PAINT_OT_texture_paint_radial_control(wmOperatorType *ot)
        ot->poll= texture_paint_poll;
        
        /* flags */
-       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
 }
 
 
@@ -5107,3 +5408,200 @@ int facemask_paint_poll(bContext *C)
        return paint_facesel_test(CTX_data_active_object(C));
 }
 
+/* use project paint to re-apply an image */
+static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
+{
+       Image *image= BLI_findlink(&CTX_data_main(C)->image, RNA_enum_get(op->ptr, "image"));
+       Scene *scene= CTX_data_scene(C);
+       ProjPaintState ps= {0};
+       int orig_brush_size;
+       IDProperty *idgroup;
+       IDProperty *view_data= NULL;
+
+       project_state_init(C, OBACT, &ps);
+
+       if(ps.ob==NULL || ps.ob->type != OB_MESH) {
+               BKE_report(op->reports, RPT_ERROR, "No active mesh object.");
+               return OPERATOR_CANCELLED;
+       }
+
+       if(image==NULL) {
+               BKE_report(op->reports, RPT_ERROR, "Image could not be found.");
+               return OPERATOR_CANCELLED;
+       }
+
+       ps.reproject_image= image;
+       ps.reproject_ibuf= BKE_image_get_ibuf(image, NULL);
+
+       if(ps.reproject_ibuf==NULL || ps.reproject_ibuf->rect==NULL) {
+               BKE_report(op->reports, RPT_ERROR, "Image data could not be found.");
+               return OPERATOR_CANCELLED;
+       }
+
+       idgroup= IDP_GetProperties(&image->id, 0);
+
+       if(idgroup) {
+               view_data= IDP_GetPropertyFromGroup(idgroup, PROJ_VIEW_DATA_ID);
+
+               /* type check to make sure its ok */
+               if(view_data->len != PROJ_VIEW_DATA_SIZE || view_data->type != IDP_ARRAY || view_data->subtype != IDP_FLOAT) {
+                       BKE_report(op->reports, RPT_ERROR, "Image project data invalid.");
+                       return OPERATOR_CANCELLED;
+               }
+       }
+
+       if(view_data) {
+               /* image has stored view projection info */
+               ps.source= PROJ_SRC_IMAGE_VIEW;
+       }
+       else {
+               ps.source= PROJ_SRC_IMAGE_CAM;
+
+               if(scene->camera==NULL) {
+                       BKE_report(op->reports, RPT_ERROR, "No active camera set.");
+                       return OPERATOR_CANCELLED;
+               }
+       }
+
+       /* override */
+       ps.is_texbrush= 0;
+       ps.is_airbrush= 1;
+       orig_brush_size= brush_size(ps.brush);
+       brush_set_size(ps.brush, 32); /* cover the whole image */
+
+       ps.tool= PAINT_TOOL_DRAW; /* so pixels are initialized with minimal info */
+
+       scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING;
+
+       undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
+               image_undo_restore, image_undo_free);
+
+       /* allocate and initialize spacial data structures */
+       project_paint_begin(&ps);
+
+       if(ps.dm==NULL) {
+               brush_set_size(ps.brush, orig_brush_size);
+               return OPERATOR_CANCELLED;
+       }
+       else {
+               float pos[2]= {0.0, 0.0};
+               float lastpos[2]= {0.0, 0.0};
+               int a;
+
+               for (a=0; a < ps.image_tot; a++)
+                       partial_redraw_array_init(ps.projImages[a].partRedrawRect);
+
+               project_paint_op(&ps, NULL, lastpos, pos);
+
+               project_image_refresh_tagged(&ps);
+
+               for (a=0; a < ps.image_tot; a++) {
+                       GPU_free_image(ps.projImages[a].ima);
+                       WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, ps.projImages[a].ima);
+               }
+       }
+
+       project_paint_end(&ps);
+
+       scene->toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
+       brush_set_size(ps.brush, orig_brush_size);
+
+       return OPERATOR_FINISHED;
+}
+
+void PAINT_OT_project_image(wmOperatorType *ot)
+{
+       PropertyRNA *prop;
+
+       /* identifiers */
+       ot->name= "Project Image";
+       ot->idname= "PAINT_OT_project_image";
+       ot->description= "Project an edited render from the active camera back onto the object";
+
+       /* api callbacks */
+       ot->invoke= WM_enum_search_invoke;
+       ot->exec= texture_paint_camera_project_exec;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+       prop= RNA_def_enum(ot->srna, "image", DummyRNA_NULL_items, 0, "Image", "");
+       RNA_def_enum_funcs(prop, RNA_image_itemf);
+       ot->prop= prop;
+}
+
+static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op)
+{
+       Image *image;
+       ImBuf *ibuf;
+       char filename[FILE_MAX];
+
+       Scene *scene= CTX_data_scene(C);
+       ToolSettings *settings= scene->toolsettings;
+       int w= settings->imapaint.screen_grab_size[0];
+       int h= settings->imapaint.screen_grab_size[1];
+       int maxsize;
+
+       RNA_string_get(op->ptr, "filepath", filename);
+
+       glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxsize);
+
+       if(w > maxsize) w= maxsize;
+       if(h > maxsize) h= maxsize;
+
+       ibuf= ED_view3d_draw_offscreen_imbuf(CTX_data_scene(C), CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect);
+       if(!ibuf) {
+               /* Mostly happens when OpenGL offscreen buffer was failed to create, */
+               /* but could be other reasons. Should be handled in the future. nazgul */
+               BKE_report(op->reports, RPT_ERROR, "Failed to create OpenGL offscreen buffer.");
+               return OPERATOR_CANCELLED;
+       }
+
+       image= BKE_add_image_imbuf(ibuf);
+
+       if(image) {
+               /* now for the trickyness. store the view projection here!
+                * reprojection will reuse this */
+               View3D *v3d= CTX_wm_view3d(C);
+               RegionView3D *rv3d= CTX_wm_region_view3d(C);
+
+               IDPropertyTemplate val;
+               IDProperty *idgroup= IDP_GetProperties(&image->id, 1);
+               IDProperty *view_data;
+               int orth;
+               float *array;
+
+               val.array.len = PROJ_VIEW_DATA_SIZE;
+               val.array.type = IDP_FLOAT;
+               view_data = IDP_New(IDP_ARRAY, val, PROJ_VIEW_DATA_ID);
+
+               array= (float *)IDP_Array(view_data);
+               memcpy(array, rv3d->winmat, sizeof(rv3d->winmat)); array += sizeof(rv3d->winmat)/sizeof(float);
+               memcpy(array, rv3d->viewmat, sizeof(rv3d->viewmat)); array += sizeof(rv3d->viewmat)/sizeof(float);
+               orth= project_paint_view_clip(v3d, rv3d, &array[0], &array[1]);
+               array[2]= orth ? 1.0f : 0.0f; /* using float for a bool is dodgy but since its an extra member in the array... easier then adding a single bool prop */
+
+               IDP_AddToGroup(idgroup, view_data);
+
+               rename_id(&image->id, "image_view");
+       }
+
+       return OPERATOR_FINISHED;
+}
+
+void PAINT_OT_image_from_view(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Image from View";
+       ot->idname= "PAINT_OT_image_from_view";
+       ot->description= "Make an image from the current 3D view for re-projection";
+
+       /* api callbacks */
+       ot->exec= texture_paint_image_from_view_exec;
+       ot->poll= ED_operator_region_view3d_active;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER;
+
+       RNA_def_string_file_name(ot->srna, "filepath", "", FILE_MAX, "File Path", "Name of the file");
+}