diff --git a/source/blender/include/BDR_gpencil.h b/source/blender/include/BDR_gpencil.h index 9b9294b0343..82263c8cda7 100644 --- a/source/blender/include/BDR_gpencil.h +++ b/source/blender/include/BDR_gpencil.h @@ -57,10 +57,13 @@ struct bGPDframe *gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe); struct bGPDlayer *gpencil_layer_addnew(struct bGPdata *gpd); struct bGPdata *gpencil_data_addnew(void); +struct bGPDframe *gpencil_frame_duplicate(struct bGPDframe *src); +struct bGPDlayer *gpencil_layer_duplicate(struct bGPDlayer *src); struct bGPdata *gpencil_data_duplicate(struct bGPdata *gpd); struct bGPdata *gpencil_data_getactive(struct ScrArea *sa); short gpencil_data_setactive(struct ScrArea *sa, struct bGPdata *gpd); +struct ScrArea *gpencil_data_findowner(struct bGPdata *gpd); void gpencil_frame_delete_laststroke(struct bGPDframe *gpf); @@ -79,7 +82,6 @@ void gpencil_delete_menu(void); void gpencil_convert_operation(short mode); void gpencil_convert_menu(void); -//short gpencil_paint(short mousebutton); short gpencil_do_paint(struct ScrArea *sa, short mousebutton); #endif /* BDR_GPENCIL_H */ diff --git a/source/blender/include/BIF_editaction.h b/source/blender/include/BIF_editaction.h index 1425cf6a67c..ae330b30d81 100644 --- a/source/blender/include/BIF_editaction.h +++ b/source/blender/include/BIF_editaction.h @@ -201,6 +201,10 @@ void delete_gpencil_layers(void); void delete_gplayer_frames(struct bGPDlayer *gpl); void duplicate_gplayer_frames(struct bGPDlayer *gpd); +void free_gpcopybuf(void); +void copy_gpdata(void); +void paste_gpdata(void); + void snap_gplayer_frames(struct bGPDlayer *gpl, short mode); void mirror_gplayer_frames(struct bGPDlayer *gpl, short mode); diff --git a/source/blender/src/editaction_gpencil.c b/source/blender/src/editaction_gpencil.c index 14269080b1f..1f30b83c6b3 100644 --- a/source/blender/src/editaction_gpencil.c +++ b/source/blender/src/editaction_gpencil.c @@ -362,25 +362,226 @@ void duplicate_gplayer_frames (bGPDlayer *gpl) /* duplicate this frame */ if (gpf->flag & GP_FRAME_SELECT) { bGPDframe *gpfd; - bGPDstroke *gps; /* duplicate frame, and deselect self */ - gpfd= MEM_dupallocN(gpf); + gpfd= gpencil_frame_duplicate(gpf); gpf->flag &= ~GP_FRAME_SELECT; - /* duplicate list of strokes too */ - duplicatelist(&gpfd->strokes, &gpf->strokes); - - /* dupalloc only makes another copy of mem, but doesn't adjust pointers */ - for (gps= gpfd->strokes.first; gps; gps= gps->next) { - gps->points= MEM_dupallocN(gps->points); - } - BLI_insertlinkafter(&gpl->frames, gpf, gpfd); } } } +/* -------------------------------------- */ +/* Copy and Paste Tools */ +/* - The copy/paste buffer currently stores a set of GP_Layers, with temporary + * GP_Frames with the necessary strokes + * - Unless there is only one element in the buffer, names are also tested to check for compatability. + * - All pasted frames are offset by the same amount. This is calculated as the difference in the times of + * the current frame and the 'first keyframe' (i.e. the earliest one in all channels). + * - The earliest frame is calculated per copy operation. + */ + +/* globals for copy/paste data (like for other copy/paste buffers) */ +ListBase gpcopybuf = {NULL, NULL}; +static float gpcopy_firstframe= 999999999.0f; + +/* This function frees any MEM_calloc'ed copy/paste buffer data */ +void free_gpcopybuf () +{ + free_gpencil_layers(&gpcopybuf); + + gpcopybuf.first= gpcopybuf.last= NULL; + gpcopy_firstframe= 999999999.0f; +} + +/* This function adds data to the copy/paste buffer, freeing existing data first + * Only the selected action channels gets their selected keyframes copied. + */ +void copy_gpdata () +{ + ListBase act_data = {NULL, NULL}; + bActListElem *ale; + int filter; + void *data; + short datatype; + + /* clear buffer first */ + free_gpcopybuf(); + + /* get data */ + data= get_action_context(&datatype); + if (data == NULL) return; + if (datatype != ACTCONT_GPENCIL) return; + + /* filter data */ + filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL); + actdata_filter(&act_data, filter, data, datatype); + + /* assume that each of these is an ipo-block */ + for (ale= act_data.first; ale; ale= ale->next) { + bGPDlayer *gpls, *gpln; + bGPDframe *gpf, *gpfn; + + /* get new layer to put into buffer */ + gpls= (bGPDlayer *)ale->data; + gpln= MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer"); + + gpln->frames.first= gpln->frames.last= NULL; + strcpy(gpln->info, gpls->info); + + BLI_addtail(&gpcopybuf, gpln); + + /* loop over frames, and copy only selected frames */ + for (gpf= gpls->frames.first; gpf; gpf= gpf->next) { + /* if frame is selected, make duplicate it and its strokes */ + if (gpf->flag & GP_FRAME_SELECT) { + /* add frame to buffer */ + gpfn= gpencil_frame_duplicate(gpf); + BLI_addtail(&gpln->frames, gpfn); + + /* check if this is the earliest frame encountered so far */ + if (gpf->framenum < gpcopy_firstframe) + gpcopy_firstframe= gpf->framenum; + } + } + } + + /* check if anything ended up in the buffer */ + if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last)) + error("Nothing copied to buffer"); + + /* free temp memory */ + BLI_freelistN(&act_data); +} + +void paste_gpdata () +{ + ListBase act_data = {NULL, NULL}; + bActListElem *ale; + int filter; + void *data; + short datatype; + + short no_name= 0; + float offset = CFRA - gpcopy_firstframe; + + /* check if buffer is empty */ + if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last)) { + error("No data in buffer to paste"); + return; + } + /* check if single channel in buffer (disregard names if so) */ + if (gpcopybuf.first == gpcopybuf.last) + no_name= 1; + + /* get data */ + data= get_action_context(&datatype); + if (data == NULL) return; + if (datatype != ACTCONT_GPENCIL) return; + + /* filter data */ + filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT); + actdata_filter(&act_data, filter, data, datatype); + + /* from selected channels */ + for (ale= act_data.first; ale; ale= ale->next) { + bGPDlayer *gpld= (bGPDlayer *)ale->data; + bGPDlayer *gpls= NULL; + bGPDframe *gpfs, *gpf; + + /* find suitable layer from buffer to use to paste from */ + for (gpls= gpcopybuf.first; gpls; gpls= gpls->next) { + /* check if layer name matches */ + if ((no_name) || (strcmp(gpls->info, gpld->info)==0)) + break; + } + + /* this situation might occur! */ + if (gpls == NULL) + continue; + + /* add frames from buffer */ + for (gpfs= gpls->frames.first; gpfs; gpfs= gpfs->next) { + /* temporarily apply offset to buffer-frame while copying */ + gpfs->framenum += offset; + + /* get frame to copy data into (if no frame returned, then just ignore) */ + gpf= gpencil_layer_getframe(gpld, gpfs->framenum, 1); + if (gpf) { + bGPDstroke *gps, *gpsn; + ScrArea *sa; + + /* get area that gp-data comes from */ + sa= gpencil_data_findowner((bGPdata *)ale->owner); + + /* this should be the right frame... as it may be a pre-existing frame, + * must make sure that only compatible stroke types get copied over + * - we cannot just add a duplicate frame, as that would cause errors + * - need to check for compatible types to minimise memory usage (copying 'junk' over) + */ + for (gps= gpfs->strokes.first; gps; gps= gps->next) { + short stroke_ok; + + /* if there's an area, check that it supports this type of stroke */ + if (sa) { + stroke_ok= 0; + + /* check if spacetype supports this type of stroke + * - NOTE: must sync this with gp_paint_initstroke() in gpencil.c + */ + switch (sa->spacetype) { + case SPACE_VIEW3D: /* 3D-View: either screen-aligned or 3d-space */ + if ((gps->flag == 0) || (gps->flag & GP_STROKE_3DSPACE)) + stroke_ok= 1; + break; + + case SPACE_NODE: /* Nodes Editor: either screen-aligned or view-aligned */ + case SPACE_IMAGE: /* Image Editor: either screen-aligned or view\image-aligned */ + if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DSPACE)) + stroke_ok= 1; + break; + + case SPACE_SEQ: /* Sequence Editor: either screen-aligned or view-aligned */ + if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DIMAGE)) + stroke_ok= 1; + break; + } + } + else + stroke_ok= 1; + + /* if stroke is ok, we make a copy of this stroke and add to frame */ + if (stroke_ok) { + /* make a copy of stroke, then of its points array */ + gpsn= MEM_dupallocN(gps); + gpsn->points= MEM_dupallocN(gps->points); + + /* append stroke to frame */ + BLI_addtail(&gpf->strokes, gpsn); + } + } + + /* if no strokes (i.e. new frame) added, free gpf */ + if (gpf->strokes.first == NULL) + gpencil_layer_delframe(gpld, gpf); + } + + /* unapply offset from buffer-frame */ + gpfs->framenum -= offset; + } + } + + /* free temp memory */ + BLI_freelistN(&act_data); + + /* undo and redraw stuff */ + allqueue(REDRAWVIEW3D, 0); + //allqueue(REDRAWNODES, 0); + allqueue(REDRAWACTION, 0); + BIF_undo_push("Paste Grease Pencil Frames"); +} + /* -------------------------------------- */ /* Snap Tools */ diff --git a/source/blender/src/gpencil.c b/source/blender/src/gpencil.c index 57fd958a94e..f6703fbe66b 100644 --- a/source/blender/src/gpencil.c +++ b/source/blender/src/gpencil.c @@ -112,7 +112,7 @@ void free_gpencil_strokes (bGPDframe *gpf) gpsn= gps->next; /* free stroke memory arrays, then stroke itself */ - MEM_freeN(gps->points); + if (gps->points) MEM_freeN(gps->points); BLI_freelinkN(&gpf->strokes, gps); } } @@ -135,7 +135,7 @@ void free_gpencil_frames (bGPDlayer *gpl) } } -/* Free all of the gp-layers for a viewport (list should be &G.vd->gpd or so) */ +/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ void free_gpencil_layers (ListBase *list) { bGPDlayer *gpl, *gpln; @@ -255,13 +255,63 @@ bGPdata *gpencil_data_addnew (void) /* -------- Data Duplication ---------- */ +/* make a copy of a given gpencil frame */ +bGPDframe *gpencil_frame_duplicate (bGPDframe *src) +{ + bGPDstroke *gps, *gpsd; + bGPDframe *dst; + + /* error checking */ + if (src == NULL) + return NULL; + + /* make a copy of the source frame */ + dst= MEM_dupallocN(src); + + /* copy strokes */ + dst->strokes.first = dst->strokes.last= NULL; + for (gps= src->strokes.first; gps; gps= gps->next) { + /* make copy of source stroke, then adjust pointer to points too */ + gpsd= MEM_dupallocN(gps); + gpsd->points= MEM_dupallocN(gps->points); + + BLI_addtail(&dst->strokes, gpsd); + } + + /* return new frame */ + return dst; +} + +/* make a copy of a given gpencil layer */ +bGPDlayer *gpencil_layer_duplicate (bGPDlayer *src) +{ + bGPDframe *gpf, *gpfd; + bGPDlayer *dst; + + /* error checking */ + if (src == NULL) + return NULL; + + /* make a copy of source layer */ + dst= MEM_dupallocN(src); + + /* copy frames */ + dst->frames.first= dst->frames.last= NULL; + for (gpf= src->frames.first; gpf; gpf= gpf->next) { + /* make a copy of source stroke */ + gpfd= gpencil_frame_duplicate(gpf); + BLI_addtail(&dst->frames, gpfd); + } + + /* return new layer */ + return dst; +} + /* make a copy of a given gpencil datablock */ bGPdata *gpencil_data_duplicate (bGPdata *src) { + bGPDlayer *gpl, *gpld; bGPdata *dst; - bGPDlayer *gpld, *gpls; - bGPDframe *gpfd, *gpfs; - bGPDstroke *gps; /* error checking */ if (src == NULL) @@ -271,25 +321,11 @@ bGPdata *gpencil_data_duplicate (bGPdata *src) dst= MEM_dupallocN(src); /* copy layers */ - duplicatelist(&dst->layers, &src->layers); - - for (gpld=dst->layers.first, gpls=src->layers.first; gpld && gpls; - gpld=gpld->next, gpls=gpls->next) - { - /* copy frames */ - duplicatelist(&gpld->frames, &gpls->frames); - - for (gpfd=gpld->frames.first, gpfs=gpls->frames.first; gpfd && gpfs; - gpfd=gpfd->next, gpfs=gpfs->next) - { - /* copy strokes */ - duplicatelist(&gpfd->strokes, &gpfs->strokes); - - for (gps= gpfd->strokes.first; gps; gps= gps->next) - { - gps->points= MEM_dupallocN(gps->points); - } - } + dst->layers.first= dst->layers.last= NULL; + for (gpl= src->layers.first; gpl; gpl= gpl->next) { + /* make a copy of source layer and its data */ + gpld= gpencil_layer_duplicate(gpl); + BLI_addtail(&dst->layers, gpld); } /* return new */ @@ -415,6 +451,30 @@ short gpencil_data_setactive (ScrArea *sa, bGPdata *gpd) return 0; } +/* return the ScrArea that has the given GP-datablock + * - assumes that only searching in current screen + * - is based on GP-datablocks only being able to + * exist for one area at a time (i.e. not multiuser) + */ +ScrArea *gpencil_data_findowner (bGPdata *gpd) +{ + ScrArea *sa; + + /* error checking */ + if (gpd == NULL) + return NULL; + + /* loop over all scrareas for current screen, and check if that area has this gpd */ + for (sa= G.curscreen->areabase.first; sa; sa= sa->next) { + /* use get-active func to see if match */ + if (gpencil_data_getactive(sa) == gpd) + return sa; + } + + /* not found */ + return NULL; +} + /* -------- GP-Frame API ---------- */ /* delete the last stroke of the given frame */ @@ -539,6 +599,7 @@ bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew) else { /* unresolved errogenous situation! */ printf("Error: cannot find appropriate gp-frame \n"); + /* gpl->actframe should still be NULL */ } } else { @@ -547,6 +608,7 @@ bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew) gpl->actframe= gpencil_frame_addnew(gpl, cframe); else { /* don't do anything... this may be when no frames yet! */ + /* gpl->actframe should still be NULL */ } } @@ -792,7 +854,7 @@ static void gp_stroke_to_bezier (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu) /* set settings */ bezt->h1= bezt->h2= HD_FREE; bezt->f1= bezt->f2= bezt->f3= SELECT; - bezt->radius = bezt->weight = pt->pressure * gpl->thickness; + bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1; } /* must calculate handles or else we crash */ @@ -1717,7 +1779,6 @@ static void gp_paint_initstroke (tGPsdata *p, short paintmode) break; case SPACE_SEQ: { - /* for now, this is not applicable here... */ p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE; } break; diff --git a/source/blender/src/header_action.c b/source/blender/src/header_action.c index 2834a41e9ec..0cd5fcba271 100644 --- a/source/blender/src/header_action.c +++ b/source/blender/src/header_action.c @@ -285,10 +285,16 @@ void do_action_buttons(unsigned short event) /* copy/paste buttons in Action Editor header */ case B_ACTCOPYKEYS: - copy_actdata(); + if (G.saction->mode == SACTCONT_GPENCIL) + copy_gpdata(); + else + copy_actdata(); break; case B_ACTPASTEKEYS: - paste_actdata(); + if (G.saction->mode == SACTCONT_GPENCIL) + paste_gpdata(); + else + paste_actdata(); break; case B_ACTPIN: /* __PINFAKE */ @@ -1735,20 +1741,24 @@ void action_buttons(void) uiClearButLock(); xco += 8; - - /* COPY PASTE */ - uiBlockBeginAlign(block); - if (curarea->headertype==HEADERTOP) { - uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYUP, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer"); - uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEUP, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer"); - } - else { - uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYDOWN, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer"); - uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEDOWN, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer"); - } - uiBlockEndAlign(block); - xco += (XIC + 8); - + } + + + /* COPY PASTE */ + uiBlockBeginAlign(block); + if (curarea->headertype==HEADERTOP) { + uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYUP, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer"); + uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEUP, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer"); + } + else { + uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYDOWN, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer"); + uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEDOWN, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer"); + } + uiBlockEndAlign(block); + xco += (XIC + 8); + + + if (G.saction->mode != SACTCONT_GPENCIL) { /* draw AUTOSNAP */ if (G.saction->flag & SACTION_DRAWTIME) { uiDefButC(block, MENU, B_REDR, diff --git a/source/blender/src/usiblender.c b/source/blender/src/usiblender.c index f6e4054379c..c4d22664b8e 100644 --- a/source/blender/src/usiblender.c +++ b/source/blender/src/usiblender.c @@ -1133,6 +1133,7 @@ void exit_usiblender(void) free_matcopybuf(); free_ipocopybuf(); free_actcopybuf(); + free_gpcopybuf(); free_vertexpaint(); free_texttools();