diff options
90 files changed, 2962 insertions, 1752 deletions
diff --git a/extern/curve_fit_nd/intern/curve_fit_corners_detect.c b/extern/curve_fit_nd/intern/curve_fit_corners_detect.c index c0258182774..67ed3561a65 100644 --- a/extern/curve_fit_nd/intern/curve_fit_corners_detect.c +++ b/extern/curve_fit_nd/intern/curve_fit_corners_detect.c @@ -66,7 +66,7 @@ typedef unsigned int uint; /** \name Simple Vector Math Lib * \{ */ -static double angle_vnvnvn_cos( +static double cos_vnvnvn( const double v0[], const double v1[], const double v2[], const uint dims) { @@ -89,7 +89,7 @@ static double angle_vnvnvn( const double v0[], const double v1[], const double v2[], const uint dims) { - return acos(angle_vnvnvn_cos(v0, v1, v2, dims)); + return acos(cos_vnvnvn(v0, v1, v2, dims)); } @@ -260,7 +260,7 @@ static double point_corner_angle( const double *p = &points[i * dims]; /* initial test */ - if (angle_vnvnvn_cos(&points[(i - 1) * dims], p, &points[(i + 1) * dims], dims) > angle_threshold_cos) { + if (cos_vnvnvn(&points[(i - 1) * dims], p, &points[(i + 1) * dims], dims) > angle_threshold_cos) { return 0.0; } @@ -283,7 +283,7 @@ static double point_corner_angle( p_mid_prev, &i_mid_prev_next, p_mid_next, &i_mid_next_prev)) { - const double angle_mid_cos = angle_vnvnvn_cos(p_mid_prev, p, p_mid_next, dims); + const double angle_mid_cos = cos_vnvnvn(p_mid_prev, p, p_mid_next, dims); /* compare as cos and flip direction */ diff --git a/extern/libopenjpeg/bio.c b/extern/libopenjpeg/bio.c index 4c02f464d8d..f04f3e503fb 100644 --- a/extern/libopenjpeg/bio.c +++ b/extern/libopenjpeg/bio.c @@ -42,7 +42,7 @@ Write a bit @param bio BIO handle @param b Bit to write (0 or 1) */ -static void bio_putbit(opj_bio_t *bio, int b); +static void bio_putbit(opj_bio_t *bio, unsigned int b); /** Read a bit @param bio BIO handle @@ -78,7 +78,7 @@ static int bio_byteout(opj_bio_t *bio) { if (bio->bp >= bio->end) { return 1; } - *bio->bp++ = bio->buf >> 8; + *bio->bp++ = (unsigned char)(bio->buf >> 8); return 0; } @@ -92,7 +92,7 @@ static int bio_bytein(opj_bio_t *bio) { return 0; } -static void bio_putbit(opj_bio_t *bio, int b) { +static void bio_putbit(opj_bio_t *bio, unsigned int b) { if (bio->ct == 0) { bio_byteout(bio); } @@ -126,7 +126,7 @@ void bio_destroy(opj_bio_t *bio) { } int bio_numbytes(opj_bio_t *bio) { - return (bio->bp - bio->start); + return (int)(bio->bp - bio->start); } void bio_init_enc(opj_bio_t *bio, unsigned char *bp, int len) { diff --git a/extern/libopenjpeg/cidx_manager.c b/extern/libopenjpeg/cidx_manager.c index 6131b938ea6..f3b251ffa09 100644 --- a/extern/libopenjpeg/cidx_manager.c +++ b/extern/libopenjpeg/cidx_manager.c @@ -29,8 +29,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <stdio.h> -#include <stdlib.h> #include "opj_includes.h" diff --git a/extern/libopenjpeg/cio.c b/extern/libopenjpeg/cio.c index b8a7ecf8a87..97cccea6dee 100644 --- a/extern/libopenjpeg/cio.c +++ b/extern/libopenjpeg/cio.c @@ -30,6 +30,7 @@ */ #include "opj_includes.h" +#include <assert.h> /* ----------------------------------------------------------------------- */ @@ -106,6 +107,7 @@ int OPJ_CALLCONV cio_tell(opj_cio_t *cio) { * pos : position, in number of bytes, from the beginning of the stream */ void OPJ_CALLCONV cio_seek(opj_cio_t *cio, int pos) { + assert((cio->start + pos) <= cio->end); cio->bp = cio->start + pos; } @@ -113,6 +115,7 @@ void OPJ_CALLCONV cio_seek(opj_cio_t *cio, int pos) { * Number of bytes left before the end of the stream. */ int cio_numbytesleft(opj_cio_t *cio) { + assert((cio->end - cio->bp) >= 0); return cio->end - cio->bp; } @@ -139,6 +142,7 @@ opj_bool cio_byteout(opj_cio_t *cio, unsigned char v) { * Read a byte. */ unsigned char cio_bytein(opj_cio_t *cio) { + assert(cio->bp >= cio->start); if (cio->bp >= cio->end) { opj_event_msg(cio->cinfo, EVT_ERROR, "read error: passed the end of the codestream (start = %d, current = %d, end = %d\n", cio->start, cio->bp, cio->end); return 0; @@ -152,7 +156,7 @@ unsigned char cio_bytein(opj_cio_t *cio) { * v : value to write * n : number of bytes to write */ -unsigned int cio_write(opj_cio_t *cio, unsigned long long int v, int n) { +unsigned int cio_write(opj_cio_t *cio, unsigned int64 v, int n) { int i; for (i = n - 1; i >= 0; i--) { if( !cio_byteout(cio, (unsigned char) ((v >> (i << 3)) & 0xff)) ) @@ -173,7 +177,7 @@ unsigned int cio_read(opj_cio_t *cio, int n) { unsigned int v; v = 0; for (i = n - 1; i >= 0; i--) { - v += cio_bytein(cio) << (i << 3); + v += (unsigned int)cio_bytein(cio) << (i << 3); } return v; } @@ -184,6 +188,10 @@ unsigned int cio_read(opj_cio_t *cio, int n) { * n : number of bytes to skip */ void cio_skip(opj_cio_t *cio, int n) { + assert((cio->bp + n) >= cio->bp); + if (((cio->bp + n) < cio->start) || ((cio->bp + n) > cio->end)) { + assert(0); + } cio->bp += n; } diff --git a/extern/libopenjpeg/cio.h b/extern/libopenjpeg/cio.h index ce1a13ecb3a..e62743141ca 100644 --- a/extern/libopenjpeg/cio.h +++ b/extern/libopenjpeg/cio.h @@ -31,6 +31,13 @@ #ifndef __CIO_H #define __CIO_H + +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define int64 __int64 +#else +#define int64 long long +#endif + /** @file cio.h @brief Implementation of a byte input-output process (CIO) @@ -63,7 +70,7 @@ Write some bytes @param n Number of bytes to write @return Returns the number of bytes written or 0 if an error occured */ -unsigned int cio_write(opj_cio_t *cio, unsigned long long int v, int n); +unsigned int cio_write(opj_cio_t *cio, unsigned int64 v, int n); /** Read some bytes @param cio CIO handle diff --git a/extern/libopenjpeg/event.c b/extern/libopenjpeg/event.c index 0dc22f12549..38db33a9432 100644 --- a/extern/libopenjpeg/event.c +++ b/extern/libopenjpeg/event.c @@ -103,18 +103,17 @@ opj_bool opj_event_msg(opj_common_ptr cinfo, int event_type, const char *fmt, .. va_list arg; int str_length/*, i, j*/; /* UniPG */ char message[MSG_SIZE]; - memset(message, 0, MSG_SIZE); /* initialize the optional parameter list */ va_start(arg, fmt); - /* check the length of the format string */ - str_length = (strlen(fmt) > MSG_SIZE) ? MSG_SIZE : strlen(fmt); /* parse the format string and put the result in 'message' */ - vsprintf(message, fmt, arg); /* UniPG */ + str_length = vsnprintf(message, MSG_SIZE, fmt, arg); /* UniPG */ /* deinitialize the optional parameter list */ va_end(arg); /* output the message to the user program */ - msg_handler(message, cinfo->client_data); + if( str_length > -1 && str_length < MSG_SIZE ) + msg_handler(message, cinfo->client_data); + else return OPJ_FALSE; } return OPJ_TRUE; diff --git a/extern/libopenjpeg/image.c b/extern/libopenjpeg/image.c index 7c1e7f7faa2..579fd73d718 100644 --- a/extern/libopenjpeg/image.c +++ b/extern/libopenjpeg/image.c @@ -40,7 +40,7 @@ opj_image_t* OPJ_CALLCONV opj_image_create(int numcmpts, opj_image_cmptparm_t *c image->color_space = clrspc; image->numcomps = numcmpts; /* allocate memory for the per-component information */ - image->comps = (opj_image_comp_t*)opj_malloc(image->numcomps * sizeof(opj_image_comp_t)); + image->comps = (opj_image_comp_t*)opj_calloc(1,image->numcomps * sizeof(opj_image_comp_t)); if(!image->comps) { fprintf(stderr,"Unable to allocate memory for image.\n"); opj_image_destroy(image); @@ -86,3 +86,4 @@ void OPJ_CALLCONV opj_image_destroy(opj_image_t *image) { opj_free(image); } } + diff --git a/extern/libopenjpeg/j2k.c b/extern/libopenjpeg/j2k.c index d34c75faa7b..93e5c9eb80a 100644 --- a/extern/libopenjpeg/j2k.c +++ b/extern/libopenjpeg/j2k.c @@ -32,6 +32,7 @@ */ #include "opj_includes.h" +#include <assert.h> /** @defgroup J2K J2K - JPEG-2000 codestream reader/writer */ /*@{*/ @@ -404,6 +405,7 @@ static void j2k_write_siz(opj_j2k_t *j2k) { static void j2k_read_siz(opj_j2k_t *j2k) { int len, i; + int n_comps; opj_cio_t *cio = j2k->cio; opj_image_t *image = j2k->image; @@ -422,12 +424,33 @@ static void j2k_read_siz(opj_j2k_t *j2k) { if ((image->x0<0)||(image->x1<0)||(image->y0<0)||(image->y1<0)) { opj_event_msg(j2k->cinfo, EVT_ERROR, - "%s: invalid image size (x0:%d, x1:%d, y0:%d, y1:%d)\n", + "invalid image size (x0:%d, x1:%d, y0:%d, y1:%d)\n", image->x0,image->x1,image->y0,image->y1); return; } + n_comps = (len - 36 - 2 ) / 3; + assert( (len - 36 - 2 ) % 3 == 0 ); image->numcomps = cio_read(cio, 2); /* Csiz */ + assert( n_comps == image->numcomps ); + (void)n_comps; + + /* testcase 4035.pdf.SIGSEGV.d8b.3375 */ + if (image->x0 > image->x1 || image->y0 > image->y1) { + opj_event_msg(j2k->cinfo, EVT_ERROR, "Error with SIZ marker: negative image size (%d x %d)\n", image->x1 - image->x0, image->y1 - image->y0); + return; + } + /* testcase 2539.pdf.SIGFPE.706.1712 (also 3622.pdf.SIGFPE.706.2916 and 4008.pdf.SIGFPE.706.3345 and maybe more) */ + if (!(cp->tdx * cp->tdy)) { + opj_event_msg(j2k->cinfo, EVT_ERROR, "Error with SIZ marker: invalid tile size (tdx: %d, tdy: %d)\n", cp->tdx, cp->tdy); + return; + } + + /* testcase 1610.pdf.SIGSEGV.59c.681 */ + if (((int64)image->x1) * ((int64)image->y1) != (image->x1 * image->y1)) { + opj_event_msg(j2k->cinfo, EVT_ERROR, "Prevent buffer overflow (x1: %d, y1: %d)\n", image->x1, image->y1); + return; + } #ifdef USE_JPWL if (j2k->cp->correct) { @@ -466,11 +489,19 @@ static void j2k_read_siz(opj_j2k_t *j2k) { /* update components number in the jpwl_exp_comps filed */ cp->exp_comps = image->numcomps; } +#else + (void)len; #endif /* USE_JPWL */ + /* prevent division by zero */ + if (!(cp->tdx * cp->tdy)) { + opj_event_msg(j2k->cinfo, EVT_ERROR, "invalid tile size (tdx: %d, tdy: %d)\n", cp->tdx, cp->tdy); + return; + } + image->comps = (opj_image_comp_t*) opj_calloc(image->numcomps, sizeof(opj_image_comp_t)); for (i = 0; i < image->numcomps; i++) { - int tmp, w, h; + int tmp; tmp = cio_read(cio, 1); /* Ssiz_i */ image->comps[i].prec = (tmp & 0x7f) + 1; image->comps[i].sgnd = tmp >> 7; @@ -506,9 +537,11 @@ static void j2k_read_siz(opj_j2k_t *j2k) { } #endif /* USE_JPWL */ - /* TODO: unused ? */ - w = int_ceildiv(image->x1 - image->x0, image->comps[i].dx); - h = int_ceildiv(image->y1 - image->y0, image->comps[i].dy); + /* prevent division by zero */ + if (!(image->comps[i].dx * image->comps[i].dy)) { + opj_event_msg(j2k->cinfo, EVT_ERROR, "JPWL: invalid component size (dx: %d, dy: %d)\n", image->comps[i].dx, image->comps[i].dy); + return; + } image->comps[i].resno_decoded = 0; /* number of resolution decoded */ image->comps[i].factor = cp->reduce; /* reducing factor per component */ @@ -517,6 +550,15 @@ static void j2k_read_siz(opj_j2k_t *j2k) { cp->tw = int_ceildiv(image->x1 - cp->tx0, cp->tdx); cp->th = int_ceildiv(image->y1 - cp->ty0, cp->tdy); + /* gdal_fuzzer_check_number_of_tiles.jp2 */ + if (cp->tw == 0 || cp->th == 0 || cp->tw > 65535 / cp->th) { + opj_event_msg(j2k->cinfo, EVT_ERROR, + "Invalid number of tiles : %u x %u (maximum fixed by jpeg2000 norm is 65535 tiles)\n", + cp->tw, cp->th); + return; + } + + #ifdef USE_JPWL if (j2k->cp->correct) { /* if JPWL is on, we check whether TX errors have damaged @@ -558,7 +600,17 @@ static void j2k_read_siz(opj_j2k_t *j2k) { #endif /* USE_JPWL */ cp->tcps = (opj_tcp_t*) opj_calloc(cp->tw * cp->th, sizeof(opj_tcp_t)); + if (cp->tcps == NULL) + { + opj_event_msg(j2k->cinfo, EVT_ERROR, "Out of memory\n"); + return; + } cp->tileno = (int*) opj_malloc(cp->tw * cp->th * sizeof(int)); + if (cp->tileno == NULL) + { + opj_event_msg(j2k->cinfo, EVT_ERROR, "Out of memory\n"); + return; + } cp->tileno_size = 0; #ifdef USE_JPWL @@ -684,6 +736,12 @@ static void j2k_read_cox(opj_j2k_t *j2k, int compno) { "of resolutions of this component\nModify the cp_reduce parameter.\n\n", compno); j2k->state |= J2K_STATE_ERR; } + if( tccp->numresolutions > J2K_MAXRLVLS ) { + opj_event_msg(j2k->cinfo, EVT_ERROR, "Error decoding component %d.\nThe number of resolutions is too big: %d vs max= %d. Truncating.\n\n", + compno, tccp->numresolutions, J2K_MAXRLVLS); + j2k->state |= J2K_STATE_ERR; + tccp->numresolutions = J2K_MAXRLVLS; + } tccp->cblkw = cio_read(cio, 1) + 2; /* SPcox (E) */ tccp->cblkh = cio_read(cio, 1) + 2; /* SPcox (F) */ @@ -753,6 +811,7 @@ static void j2k_read_cod(opj_j2k_t *j2k) { opj_image_t *image = j2k->image; len = cio_read(cio, 2); /* Lcod */ + (void)len; tcp->csty = cio_read(cio, 1); /* Scod */ tcp->prg = (OPJ_PROG_ORDER)cio_read(cio, 1); /* SGcod (A) */ tcp->numlayers = cio_read(cio, 2); /* SGcod (B) */ @@ -806,7 +865,14 @@ static void j2k_read_coc(opj_j2k_t *j2k) { opj_cio_t *cio = j2k->cio; len = cio_read(cio, 2); /* Lcoc */ + (void)len; compno = cio_read(cio, image->numcomps <= 256 ? 1 : 2); /* Ccoc */ + if (compno >= image->numcomps) { + opj_event_msg(j2k->cinfo, EVT_ERROR, + "bad component number in COC (%d out of a maximum of %d)\n", + compno, image->numcomps); + return; + } tcp->tccps[compno].csty = cio_read(cio, 1); /* Scoc */ j2k_read_cox(j2k, compno); } @@ -877,6 +943,8 @@ static void j2k_read_qcx(opj_j2k_t *j2k, int compno, int len) { opj_event_msg(j2k->cinfo, EVT_WARNING , "bad number of subbands in Sqcx (%d) regarding to J2K_MAXBANDS (%d) \n" "- limiting number of bands to J2K_MAXBANDS and try to move to the next markers\n", numbands, J2K_MAXBANDS); + /* edf_c2_1013627.jp2 */ + numbands = 1; } #endif /* USE_JPWL */ @@ -988,9 +1056,16 @@ static void j2k_read_qcc(opj_j2k_t *j2k) { /* keep your private count of tiles */ backup_compno++; - }; + } #endif /* USE_JPWL */ + if ((compno < 0) || (compno >= numcomp)) { + opj_event_msg(j2k->cinfo, EVT_ERROR, + "bad component number in QCC (%d out of a maximum of %d)\n", + compno, j2k->image->numcomps); + return; + } + j2k_read_qcx(j2k, compno, len - 2 - (numcomp <= 256 ? 1 : 2)); } @@ -1036,6 +1111,15 @@ static void j2k_read_poc(opj_j2k_t *j2k) { len = cio_read(cio, 2); /* Lpoc */ numpchgs = (len - 2) / (5 + 2 * (numcomps <= 256 ? 1 : 2)); + if( numpchgs >= 32 ) + { + /* edf_c2_1103421.jp2 */ + opj_event_msg(j2k->cinfo, EVT_ERROR, + "bad number of POCS (%d out of a maximum of %d)\n", + numpchgs, 32); + numpchgs = 0; + } + for (i = old_poc; i < numpchgs + old_poc; i++) { opj_poc_t *poc; poc = &tcp->pocs[i]; @@ -1058,9 +1142,12 @@ static void j2k_read_crg(opj_j2k_t *j2k) { int numcomps = j2k->image->numcomps; len = cio_read(cio, 2); /* Lcrg */ + (void)len; for (i = 0; i < numcomps; i++) { Xcrg_i = cio_read(cio, 2); /* Xcrg_i */ + (void)Xcrg_i; Ycrg_i = cio_read(cio, 2); /* Ycrg_i */ + (void)Ycrg_i; } } @@ -1072,13 +1159,16 @@ static void j2k_read_tlm(opj_j2k_t *j2k) { len = cio_read(cio, 2); /* Ltlm */ Ztlm = cio_read(cio, 1); /* Ztlm */ + (void)Ztlm; Stlm = cio_read(cio, 1); /* Stlm */ ST = ((Stlm >> 4) & 0x01) + ((Stlm >> 4) & 0x02); SP = (Stlm >> 6) & 0x01; tile_tlm = (len - 4) / ((SP + 1) * 2 + ST); for (i = 0; i < tile_tlm; i++) { Ttlm_i = cio_read(cio, ST); /* Ttlm_i */ + (void)Ttlm_i; Ptlm_i = cio_read(cio, SP ? 4 : 2); /* Ptlm_i */ + (void)Ptlm_i; } } @@ -1089,6 +1179,7 @@ static void j2k_read_plm(opj_j2k_t *j2k) { len = cio_read(cio, 2); /* Lplm */ Zplm = cio_read(cio, 1); /* Zplm */ + (void)Zplm; len -= 3; while (len > 0) { Nplm = cio_read(cio, 4); /* Nplm */ @@ -1114,6 +1205,7 @@ static void j2k_read_plt(opj_j2k_t *j2k) { len = cio_read(cio, 2); /* Lplt */ Zplt = cio_read(cio, 1); /* Zplt */ + (void)Zplt; for (i = len - 3; i > 0; i--) { add = cio_read(cio, 1); packet_len = (packet_len << 7) + add; /* Iplt_i */ @@ -1261,6 +1353,7 @@ static void j2k_read_sot(opj_j2k_t *j2k) { opj_cio_t *cio = j2k->cio; len = cio_read(cio, 2); + (void)len; tileno = cio_read(cio, 2); #ifdef USE_JPWL @@ -1269,7 +1362,7 @@ static void j2k_read_sot(opj_j2k_t *j2k) { static int backup_tileno = 0; /* tileno is negative or larger than the number of tiles!!! */ - if ((tileno < 0) || (tileno > (cp->tw * cp->th))) { + if ((tileno < 0) || (tileno >= (cp->tw * cp->th))) { opj_event_msg(j2k->cinfo, EVT_ERROR, "JPWL: bad tile number (%d out of a maximum of %d)\n", tileno, (cp->tw * cp->th)); @@ -1286,8 +1379,18 @@ static void j2k_read_sot(opj_j2k_t *j2k) { /* keep your private count of tiles */ backup_tileno++; - }; + } + else #endif /* USE_JPWL */ + { + /* tileno is negative or larger than the number of tiles!!! */ + if ((tileno < 0) || (tileno >= (cp->tw * cp->th))) { + opj_event_msg(j2k->cinfo, EVT_ERROR, + "JPWL: bad tile number (%d out of a maximum of %d)\n", + tileno, (cp->tw * cp->th)); + return; + } + } if (cp->tileno_size == 0) { cp->tileno[cp->tileno_size] = tileno; @@ -1325,8 +1428,18 @@ static void j2k_read_sot(opj_j2k_t *j2k) { totlen); } - }; + } + else #endif /* USE_JPWL */ + { + /* totlen is negative or larger than the bytes left!!! */ + if ((totlen < 0) || (totlen > (cio_numbytesleft(cio) + 8))) { + opj_event_msg(j2k->cinfo, EVT_ERROR, + "JPWL: bad tile byte size (%d bytes against %d bytes left)\n", + totlen, cio_numbytesleft(cio) + 8); + return; + } + } if (!totlen) totlen = cio_numbytesleft(cio) + 8; @@ -1478,7 +1591,13 @@ static void j2k_read_sod(opj_j2k_t *j2k) { } data = j2k->tile_data[curtileno]; + data_ptr = data; /* store in case of failure */ data = (unsigned char*) opj_realloc(data, (j2k->tile_len[curtileno] + len) * sizeof(unsigned char)); + if( data == NULL ) { + opj_event_msg(j2k->cinfo, EVT_ERROR, "Could not reallocated\n" ); + opj_free( data_ptr ); + return; + } data_ptr = data + j2k->tile_len[curtileno]; for (i = 0; i < len; i++) { @@ -1518,8 +1637,10 @@ static void j2k_read_rgn(opj_j2k_t *j2k) { int numcomps = j2k->image->numcomps; len = cio_read(cio, 2); /* Lrgn */ + (void)len; compno = cio_read(cio, numcomps <= 256 ? 1 : 2); /* Crgn */ roisty = cio_read(cio, 1); /* Srgn */ + (void)roisty; #ifdef USE_JPWL if (j2k->cp->correct) { @@ -1536,6 +1657,13 @@ static void j2k_read_rgn(opj_j2k_t *j2k) { }; #endif /* USE_JPWL */ + if (compno >= numcomps) { + opj_event_msg(j2k->cinfo, EVT_ERROR, + "bad component number in RGN (%d out of a maximum of %d)\n", + compno, j2k->image->numcomps); + return; + } + tcp->tccps[compno].roishift = cio_read(cio, 1); /* SPrgn */ } @@ -1554,7 +1682,7 @@ static void j2k_write_eoc(opj_j2k_t *j2k) { static void j2k_read_eoc(opj_j2k_t *j2k) { int i, tileno; - opj_bool success; + opj_bool success = OPJ_FALSE; /* if packets should be decoded */ if (j2k->cp->limit_decoding != DECODE_ALL_BUT_PACKETS) { @@ -1562,11 +1690,17 @@ static void j2k_read_eoc(opj_j2k_t *j2k) { tcd_malloc_decode(tcd, j2k->image, j2k->cp); for (i = 0; i < j2k->cp->tileno_size; i++) { tcd_malloc_decode_tile(tcd, j2k->image, j2k->cp, i, j2k->cstr_info); - tileno = j2k->cp->tileno[i]; - success = tcd_decode_tile(tcd, j2k->tile_data[tileno], j2k->tile_len[tileno], tileno, j2k->cstr_info); - opj_free(j2k->tile_data[tileno]); - j2k->tile_data[tileno] = NULL; - tcd_free_decode_tile(tcd, i); + if (j2k->cp->tileno[i] != -1) + { + tileno = j2k->cp->tileno[i]; + success = tcd_decode_tile(tcd, j2k->tile_data[tileno], j2k->tile_len[tileno], tileno, j2k->cstr_info); + assert( tileno != -1 ); + opj_free(j2k->tile_data[tileno]); + j2k->tile_data[tileno] = NULL; + tcd_free_decode_tile(tcd, i); + } + else + success = OPJ_FALSE; if (success == OPJ_FALSE) { j2k->state |= J2K_STATE_ERR; break; @@ -1737,6 +1871,17 @@ void j2k_destroy_decompress(opj_j2k_t *j2k) { opj_free(j2k->tile_len); } if(j2k->tile_data != NULL) { + if(j2k->cp != NULL) { + for (i = 0; i < j2k->cp->tileno_size; i++) { + int tileno = j2k->cp->tileno[i]; + if( tileno != -1 ) + { + opj_free(j2k->tile_data[tileno]); + j2k->tile_data[tileno] = NULL; + } + } + } + opj_free(j2k->tile_data); } if(j2k->default_tcp != NULL) { @@ -1856,9 +2001,15 @@ opj_image_t* j2k_decode(opj_j2k_t *j2k, opj_cio_t *cio, opj_codestream_info_t *c #endif /* USE_JPWL */ if (id >> 8 != 0xff) { - opj_image_destroy(image); - opj_event_msg(cinfo, EVT_ERROR, "%.8x: expected a marker instead of %x\n", cio_tell(cio) - 2, id); - return 0; + if(cio_numbytesleft(cio) != 0) /* not end of file reached and no EOC */ + { + opj_event_msg(cinfo, EVT_ERROR, "%.8x: expected a marker instead of %x\n", cio_tell(cio) - 2, id); + opj_image_destroy(image); + return 0; + } + opj_event_msg(cinfo, EVT_WARNING, "%.8x: expected a marker instead of %x\n", cio_tell(cio) - 2, id); + j2k->state = J2K_STATE_NEOC; + break; } e = j2k_dec_mstab_lookup(id); /* Check if the marker is known*/ @@ -1877,7 +2028,10 @@ opj_image_t* j2k_decode(opj_j2k_t *j2k, opj_cio_t *cio, opj_codestream_info_t *c (*e->handler)(j2k); } if (j2k->state & J2K_STATE_ERR) + { + opj_image_destroy(image); return NULL; + } if (j2k->state == J2K_STATE_MT) { break; @@ -1888,6 +2042,11 @@ opj_image_t* j2k_decode(opj_j2k_t *j2k, opj_cio_t *cio, opj_codestream_info_t *c } if (j2k->state == J2K_STATE_NEOC) { j2k_read_eoc(j2k); + /* Check one last time for errors during decoding before returning */ + if (j2k->state & J2K_STATE_ERR) { + opj_image_destroy(image); + return NULL; + } } if (j2k->state != J2K_STATE_MT) { @@ -1949,9 +2108,15 @@ opj_image_t* j2k_decode_jpt_stream(opj_j2k_t *j2k, opj_cio_t *cio, opj_codestre id = cio_read(cio, 2); if (id >> 8 != 0xff) { - opj_image_destroy(image); - opj_event_msg(cinfo, EVT_ERROR, "%.8x: expected a marker instead of %x\n", cio_tell(cio) - 2, id); - return 0; + if(cio_numbytesleft(cio) != 0) /* no end of file reached and no EOC */ + { + opj_event_msg(cinfo, EVT_ERROR, "%.8x: expected a marker instead of %x\n", cio_tell(cio) - 2, id); + opj_image_destroy(image); + return 0; + } + opj_event_msg(cinfo, EVT_WARNING, "%.8x: expected a marker instead of %x\n", cio_tell(cio) - 2, id); + j2k->state = J2K_STATE_NEOC; + break; } e = j2k_dec_mstab_lookup(id); if (!(j2k->state & e->states)) { diff --git a/extern/libopenjpeg/jp2.c b/extern/libopenjpeg/jp2.c index 5ae114c33b9..d3322603c8b 100644 --- a/extern/libopenjpeg/jp2.c +++ b/extern/libopenjpeg/jp2.c @@ -30,6 +30,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include "opj_includes.h" +#include <assert.h> /** @defgroup JP2 JP2 - JPEG-2000 file format reader/writer */ /*@{*/ @@ -172,6 +173,9 @@ static opj_bool jp2_read_boxhdr(opj_common_ptr cinfo, opj_cio_t *cio, opj_jp2_bo } else if (box->length == 0) { box->length = cio_numbytesleft(cio) + 8; + } else if (box->length < 0) { + opj_event_msg(cinfo, EVT_ERROR, "Integer overflow in box->length\n"); + return OPJ_FALSE; /* TODO: actually check jp2_read_boxhdr's return value */ } return OPJ_TRUE; @@ -206,7 +210,10 @@ static opj_bool jp2_read_ihdr(opj_jp2_t *jp2, opj_cio_t *cio) { opj_common_ptr cinfo = jp2->cinfo; - jp2_read_boxhdr(cinfo, cio, &box); + if(jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) { + opj_event_msg(cinfo, EVT_ERROR, "Failed to read boxhdr\n"); + return OPJ_FALSE; + } if (JP2_IHDR != box.type) { opj_event_msg(cinfo, EVT_ERROR, "Expected IHDR Marker\n"); return OPJ_FALSE; @@ -279,7 +286,10 @@ static opj_bool jp2_read_bpcc(opj_jp2_t *jp2, opj_cio_t *cio) { opj_common_ptr cinfo = jp2->cinfo; - jp2_read_boxhdr(cinfo, cio, &box); + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) { + opj_event_msg(cinfo, EVT_ERROR, "Failed to read boxhdr\n"); + return OPJ_FALSE; + } if (JP2_BPCC != box.type) { opj_event_msg(cinfo, EVT_ERROR, "Expected BPCC Marker\n"); return OPJ_FALSE; @@ -469,7 +479,7 @@ static opj_bool jp2_read_pclr(opj_jp2_t *jp2, opj_cio_t *cio, for(i = 0; i < nr_channels; ++i) { /* Cji */ - *entries++ = cio_read(cio, channel_size[i]>>3); + *entries++ = cio_read(cio, (channel_size[i]+7)>>3); } } @@ -512,10 +522,8 @@ static opj_bool jp2_read_cmap(opj_jp2_t *jp2, opj_cio_t *cio, static void jp2_apply_cdef(opj_image_t *image, opj_jp2_color_t *color) { opj_jp2_cdef_info_t *info; - int color_space; unsigned short i, n, cn, typ, asoc, acn; - color_space = image->color_space; info = color->jp2_cdef->info; n = color->jp2_cdef->n; @@ -525,6 +533,7 @@ static void jp2_apply_cdef(opj_image_t *image, opj_jp2_color_t *color) if((asoc = info[i].asoc) == 0) continue; cn = info[i].cn; typ = info[i].typ; acn = asoc - 1; + (void)typ; if(cn != acn) { @@ -639,90 +648,173 @@ opj_bool jp2_read_jp2h(opj_jp2_t *jp2, opj_cio_t *cio, opj_jp2_color_t *color) opj_common_ptr cinfo = jp2->cinfo; - jp2_read_boxhdr(cinfo, cio, &box); - do - { - if (JP2_JP2H != box.type) - { - if (box.type == JP2_JP2C) - { - opj_event_msg(cinfo, EVT_ERROR, "Expected JP2H Marker\n"); - return OPJ_FALSE; - } - cio_skip(cio, box.length - 8); + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) return OPJ_FALSE; + do { + if (JP2_JP2H != box.type) + { + if (box.type == JP2_JP2C) + { + opj_event_msg(cinfo, EVT_ERROR, "Expected JP2H Marker\n"); + return OPJ_FALSE; + } + if (box.length <= 8) return OPJ_FALSE; + cio_skip(cio, box.length - 8); - if(cio->bp >= cio->end) return OPJ_FALSE; + if(cio->bp >= cio->end) return OPJ_FALSE; - jp2_read_boxhdr(cinfo, cio, &box); - } - } while(JP2_JP2H != box.type); + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) return OPJ_FALSE; + } + } while(JP2_JP2H != box.type); if (!jp2_read_ihdr(jp2, cio)) return OPJ_FALSE; jp2h_end = box.init_pos + box.length; - if (jp2->bpc == 255) - { - if (!jp2_read_bpcc(jp2, cio)) - return OPJ_FALSE; - } - jp2_read_boxhdr(cinfo, cio, &box); + if (jp2->bpc == 255) + { + if (!jp2_read_bpcc(jp2, cio)) + return OPJ_FALSE; + } + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) return OPJ_FALSE; - while(cio_tell(cio) < jp2h_end) - { - if(box.type == JP2_COLR) - { - if( !jp2_read_colr(jp2, cio, &box, color)) - { - cio_seek(cio, box.init_pos + 8); - cio_skip(cio, box.length - 8); - } - jp2_read_boxhdr(cinfo, cio, &box); - continue; - } + while(cio_tell(cio) < jp2h_end) + { + if(box.type == JP2_COLR) + { + if( !jp2_read_colr(jp2, cio, &box, color)) + { + if (box.length <= 8) return OPJ_FALSE; + cio_seek(cio, box.init_pos + 8); + cio_skip(cio, box.length - 8); + } + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) return OPJ_FALSE; + continue; + } if(box.type == JP2_CDEF && !jp2->ignore_pclr_cmap_cdef) - { - if( !jp2_read_cdef(jp2, cio, &box, color)) - { - cio_seek(cio, box.init_pos + 8); - cio_skip(cio, box.length - 8); - } - jp2_read_boxhdr(cinfo, cio, &box); - continue; - } + { + if( !jp2_read_cdef(jp2, cio, &box, color)) + { + if (box.length <= 8) return OPJ_FALSE; + cio_seek(cio, box.init_pos + 8); + cio_skip(cio, box.length - 8); + } + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) return OPJ_FALSE; + continue; + } if(box.type == JP2_PCLR && !jp2->ignore_pclr_cmap_cdef) - { - if( !jp2_read_pclr(jp2, cio, &box, color)) - { - cio_seek(cio, box.init_pos + 8); - cio_skip(cio, box.length - 8); - } - jp2_read_boxhdr(cinfo, cio, &box); - continue; - } + { + if( !jp2_read_pclr(jp2, cio, &box, color)) + { + if (box.length <= 8) return OPJ_FALSE; + cio_seek(cio, box.init_pos + 8); + cio_skip(cio, box.length - 8); + } + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) return OPJ_FALSE; + continue; + } if(box.type == JP2_CMAP && !jp2->ignore_pclr_cmap_cdef) - { - if( !jp2_read_cmap(jp2, cio, &box, color)) - { + { + if( !jp2_read_cmap(jp2, cio, &box, color)) + { + if (box.length <= 8) return OPJ_FALSE; + cio_seek(cio, box.init_pos + 8); + cio_skip(cio, box.length - 8); + } + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) return OPJ_FALSE; + continue; + } + if (box.length <= 8) return OPJ_FALSE; cio_seek(cio, box.init_pos + 8); cio_skip(cio, box.length - 8); - } - jp2_read_boxhdr(cinfo, cio, &box); - continue; - } - cio_seek(cio, box.init_pos + 8); - cio_skip(cio, box.length - 8); - jp2_read_boxhdr(cinfo, cio, &box); + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) return OPJ_FALSE; - }/* while(cio_tell(cio) < box_end) */ + }/* while(cio_tell(cio) < box_end) */ - cio_seek(cio, jp2h_end); + cio_seek(cio, jp2h_end); -/* Part 1, I.5.3.3 : 'must contain at least one' */ - return (color->jp2_has_colr == 1); + /* Part 1, I.5.3.3 : 'must contain at least one' */ + return (color->jp2_has_colr == 1); }/* jp2_read_jp2h() */ +static opj_bool opj_jp2_check_color(opj_image_t *image, opj_jp2_color_t *color, opj_common_ptr cinfo) +{ + int i; + + /* testcase 4149.pdf.SIGSEGV.cf7.3501 */ + if (color->jp2_cdef) { + opj_jp2_cdef_info_t *info = color->jp2_cdef->info; + int n = color->jp2_cdef->n; + + for (i = 0; i < n; i++) { + if (info[i].cn >= image->numcomps) { + opj_event_msg(cinfo, EVT_ERROR, "Invalid component index %d (>= %d).\n", info[i].cn, image->numcomps); + return OPJ_FALSE; + } + if (info[i].asoc > 0 && (info[i].asoc - 1) >= image->numcomps) { + opj_event_msg(cinfo, EVT_ERROR, "Invalid component index %d (>= %d).\n", info[i].asoc - 1, image->numcomps); + return OPJ_FALSE; + } + } + } + + /* testcases 451.pdf.SIGSEGV.f4c.3723, 451.pdf.SIGSEGV.5b5.3723 and + 66ea31acbb0f23a2bbc91f64d69a03f5_signal_sigsegv_13937c0_7030_5725.pdf */ + if (color->jp2_pclr && color->jp2_pclr->cmap) { + int nr_channels = color->jp2_pclr->nr_channels; + opj_jp2_cmap_comp_t *cmap = color->jp2_pclr->cmap; + opj_bool *pcol_usage, is_sane = OPJ_TRUE; + + /* verify that all original components match an existing one */ + for (i = 0; i < nr_channels; i++) { + if (cmap[i].cmp >= image->numcomps) { + opj_event_msg(cinfo, EVT_ERROR, "Invalid component index %d (>= %d).\n", cmap[i].cmp, image->numcomps); + is_sane = OPJ_FALSE; + } + } + + pcol_usage = opj_calloc(nr_channels, sizeof(opj_bool)); + if (!pcol_usage) { + opj_event_msg(cinfo, EVT_ERROR, "Unexpected OOM.\n"); + return OPJ_FALSE; + } + /* verify that no component is targeted more than once */ + for (i = 0; i < nr_channels; i++) { + int pcol = cmap[i].pcol; + assert(cmap[i].mtyp == 0 || cmap[i].mtyp == 1); + if (pcol >= nr_channels) { + opj_event_msg(cinfo, EVT_ERROR, "Invalid component/palette index for direct mapping %d.\n", pcol); + is_sane = OPJ_FALSE; + } + else if (pcol_usage[pcol] && cmap[i].mtyp == 1) { + opj_event_msg(cinfo, EVT_ERROR, "Component %d is mapped twice.\n", pcol); + is_sane = OPJ_FALSE; + } + else if (cmap[i].mtyp == 0 && cmap[i].pcol != 0) { + /* I.5.3.5 PCOL: If the value of the MTYP field for this channel is 0, then + * the value of this field shall be 0. */ + opj_event_msg(cinfo, EVT_ERROR, "Direct use at #%d however pcol=%d.\n", i, pcol); + is_sane = OPJ_FALSE; + } + else + pcol_usage[pcol] = OPJ_TRUE; + } + /* verify that all components are targeted at least once */ + for (i = 0; i < nr_channels; i++) { + if (!pcol_usage[i] && cmap[i].mtyp != 0) { + opj_event_msg(cinfo, EVT_ERROR, "Component %d doesn't have a mapping.\n", i); + is_sane = OPJ_FALSE; + } + } + opj_free(pcol_usage); + if (!is_sane) { + return OPJ_FALSE; + } + } + + return OPJ_TRUE; +} + opj_image_t* opj_jp2_decode(opj_jp2_t *jp2, opj_cio_t *cio, opj_codestream_info_t *cstr_info) { @@ -756,6 +848,10 @@ opj_image_t* opj_jp2_decode(opj_jp2_t *jp2, opj_cio_t *cio, } if (!jp2->ignore_pclr_cmap_cdef){ + if (!opj_jp2_check_color(image, &color, cinfo)) { + opj_event_msg(cinfo, EVT_ERROR, "Failed to decode PCRL box\n"); + return NULL; + } /* Set Image Color Space */ if (jp2->enumcs == 16) @@ -839,8 +935,10 @@ static opj_bool jp2_read_ftyp(opj_jp2_t *jp2, opj_cio_t *cio) { opj_common_ptr cinfo = jp2->cinfo; - jp2_read_boxhdr(cinfo, cio, &box); - + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) { + opj_event_msg(cinfo, EVT_ERROR, "Failed to read boxhdr\n"); + return OPJ_FALSE; + } if (JP2_FTYP != box.type) { opj_event_msg(cinfo, EVT_ERROR, "Expected FTYP Marker\n"); return OPJ_FALSE; @@ -849,6 +947,14 @@ static opj_bool jp2_read_ftyp(opj_jp2_t *jp2, opj_cio_t *cio) { jp2->brand = cio_read(cio, 4); /* BR */ jp2->minversion = cio_read(cio, 4); /* MinV */ jp2->numcl = (box.length - 16) / 4; + + /* edf_c2_1673169.jp2 */ + if (cio_numbytesleft(cio) < ((int)jp2->numcl * 4)) { + opj_event_msg(cinfo, EVT_ERROR, "Not enough bytes in FTYP Box " + "(expected %d, but only %d left)\n", + ((int)jp2->numcl * 4), cio_numbytesleft(cio)); + return OPJ_FALSE; + } jp2->cl = (unsigned int *) opj_malloc(jp2->numcl * sizeof(unsigned int)); for (i = 0; i < (int)jp2->numcl; i++) { @@ -897,15 +1003,20 @@ static opj_bool jp2_read_jp2c(opj_jp2_t *jp2, opj_cio_t *cio, unsigned int *j2k_ opj_common_ptr cinfo = jp2->cinfo; - jp2_read_boxhdr(cinfo, cio, &box); + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) { + opj_event_msg(cinfo, EVT_ERROR, "Failed to read boxhdr\n"); + return OPJ_FALSE; + } do { if(JP2_JP2C != box.type) { + if (box.length <= 8) return OPJ_FALSE; cio_skip(cio, box.length - 8); - jp2_read_boxhdr(cinfo, cio, &box); + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) return OPJ_FALSE; } } while(JP2_JP2C != box.type); *j2k_codestream_offset = cio_tell(cio); + if (box.length <= 8) return OPJ_FALSE; *j2k_codestream_length = box.length - 8; return OPJ_TRUE; @@ -930,7 +1041,10 @@ static opj_bool jp2_read_jp(opj_jp2_t *jp2, opj_cio_t *cio) { opj_common_ptr cinfo = jp2->cinfo; - jp2_read_boxhdr(cinfo, cio, &box); + if( jp2_read_boxhdr(cinfo, cio, &box) == OPJ_FALSE ) { + opj_event_msg(cinfo, EVT_ERROR, "Failed to read boxhdr\n"); + return OPJ_FALSE; + } if (JP2_JP != box.type) { opj_event_msg(cinfo, EVT_ERROR, "Expected JP Marker\n"); return OPJ_FALSE; @@ -1070,6 +1184,7 @@ void jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters) { opj_jp2_t* jp2_create_compress(opj_common_ptr cinfo) { opj_jp2_t *jp2 = (opj_jp2_t*)opj_malloc(sizeof(opj_jp2_t)); if(jp2) { + memset(jp2, 0, sizeof(opj_jp2_t)); jp2->cinfo = cinfo; /* create the J2K codec */ jp2->j2k = j2k_create_compress(cinfo); diff --git a/extern/libopenjpeg/openjpeg.h b/extern/libopenjpeg/openjpeg.h index 53e9fac0438..59147c8b3d1 100644 --- a/extern/libopenjpeg/openjpeg.h +++ b/extern/libopenjpeg/openjpeg.h @@ -135,6 +135,10 @@ typedef enum COLOR_SPACE { CLRSPC_SYCC = 3 /**< YUV */ } OPJ_COLOR_SPACE; +#define ENUMCS_SRGB 16 +#define ENUMCS_GRAY 17 +#define ENUMCS_SYCC 18 + /** Supported codec */ diff --git a/extern/libopenjpeg/opj_config.h b/extern/libopenjpeg/opj_config.h index 82e12be03a9..5d0a877b840 100644 --- a/extern/libopenjpeg/opj_config.h +++ b/extern/libopenjpeg/opj_config.h @@ -3,7 +3,7 @@ * the endian check is a blender define */ /* create config.h for CMake */ -#define PACKAGE_VERSION "1.5.0" +#define PACKAGE_VERSION "1.5.2" #define HAVE_INTTYPES_H #define HAVE_MEMORY_H @@ -23,6 +23,7 @@ /* #undef HAVE_LIBLCMS2 */ /* #undef HAVE_LCMS1_H */ /* #undef HAVE_LCMS2_H */ +/* #undef USE_SYSTEM_GETOPT */ /* Byte order. */ /* All compilers that support Mac OS X define either __BIG_ENDIAN__ or diff --git a/extern/libopenjpeg/opj_includes.h b/extern/libopenjpeg/opj_includes.h index 2b5866a9990..e9194fd9886 100644 --- a/extern/libopenjpeg/opj_includes.h +++ b/extern/libopenjpeg/opj_includes.h @@ -89,18 +89,17 @@ Most compilers implement their own version of this keyword ... /* MSVC and Borland C do not have lrintf */ #if defined(_MSC_VER) || defined(__BORLANDC__) static INLINE long lrintf(float f){ -#ifdef _M_X64 - return (long)((f>0.0f) ? (f + 0.5f):(f -0.5f)); -#else - int i; +#ifdef _M_IX86 + long int i; _asm{ fld f fistp i }; - return i; -#endif +#else + return (long)((f>0.0f) ? (f + 0.5f):(f -0.5f)); +#endif /* _M_IX86 */ } #endif diff --git a/extern/libopenjpeg/opj_malloc.h b/extern/libopenjpeg/opj_malloc.h index 87493f497c7..aef2ee3b8c9 100644 --- a/extern/libopenjpeg/opj_malloc.h +++ b/extern/libopenjpeg/opj_malloc.h @@ -83,8 +83,10 @@ Allocate memory aligned to a 16 byte boundry #else /* Not _WIN32 */ #if defined(__sun) #define HAVE_MEMALIGN + #elif defined(__FreeBSD__) + #define HAVE_POSIX_MEMALIGN /* Linux x86_64 and OSX always align allocations to 16 bytes */ - #elif !defined(__amd64__) && !defined(__APPLE__) + #elif !defined(__amd64__) && !defined(__APPLE__) && !defined(_AIX) #define HAVE_MEMALIGN #include <malloc.h> #endif diff --git a/extern/libopenjpeg/patches/fbsd.patch b/extern/libopenjpeg/patches/fbsd.patch deleted file mode 100644 index 90e77600941..00000000000 --- a/extern/libopenjpeg/patches/fbsd.patch +++ /dev/null @@ -1,13 +0,0 @@ -Index: extern/libopenjpeg/opj_malloc.h -=================================================================== ---- extern/libopenjpeg/opj_malloc.h (revision 27736) -+++ extern/libopenjpeg/opj_malloc.h (working copy) -@@ -76,7 +76,7 @@ - #if defined(__sun) - #define HAVE_MEMALIGN - #elif defined(__GNUC__) -- #ifndef __APPLE__ -+ #if !defined(__APPLE__) && !defined(__FreeBSD__) - #define HAVE_MEMALIGN - #include <malloc.h> - #endif diff --git a/extern/libopenjpeg/patches/osx.patch b/extern/libopenjpeg/patches/osx.patch deleted file mode 100644 index c518978eed6..00000000000 --- a/extern/libopenjpeg/patches/osx.patch +++ /dev/null @@ -1,17 +0,0 @@ -Index: opj_malloc.h -=================================================================== ---- opj_malloc.h (revision 15089) -+++ opj_malloc.h (working copy) -@@ -76,8 +76,10 @@ - #if defined(__sun) - #define HAVE_MEMALIGN - #elif defined(__GNUC__) -- #define HAVE_MEMALIGN -- #include <malloc.h> -+ #ifndef __APPLE__ -+ #define HAVE_MEMALIGN -+ #include <malloc.h> -+ #endif - /* Linux x86_64 and OSX always align allocations to 16 bytes */ - #elif !defined(__amd64__) && !defined(__APPLE__) - /* FIXME: Yes, this is a big assumption */ diff --git a/extern/libopenjpeg/t1.c b/extern/libopenjpeg/t1.c index 477720412aa..ed9cdc3fea6 100644 --- a/extern/libopenjpeg/t1.c +++ b/extern/libopenjpeg/t1.c @@ -1577,6 +1577,7 @@ void t1_decode_cblks( opj_free(cblk->segs); } /* cblkno */ opj_free(precinct->cblks.dec); + precinct->cblks.dec = NULL; } /* precno */ } /* bandno */ } /* resno */ diff --git a/extern/libopenjpeg/t2.c b/extern/libopenjpeg/t2.c index 232a5437329..2585c3d56a6 100644 --- a/extern/libopenjpeg/t2.c +++ b/extern/libopenjpeg/t2.c @@ -30,6 +30,7 @@ */ #include "opj_includes.h" +#include <assert.h> /** @defgroup T2 T2 - Implementation of a tier-2 coding */ /*@{*/ @@ -64,7 +65,7 @@ static int t2_encode_packet(opj_tcd_tile_t *tile, opj_tcp_t *tcp, opj_pi_iterato @param cblksty @param first */ -static void t2_init_seg(opj_tcd_cblk_dec_t* cblk, int index, int cblksty, int first); +static opj_bool t2_init_seg(opj_tcd_cblk_dec_t* cblk, int index, int cblksty, int first); /** Decode a packet of a tile from a source buffer @param t2 T2 handle @@ -296,9 +297,17 @@ static int t2_encode_packet(opj_tcd_tile_t * tile, opj_tcp_t * tcp, opj_pi_itera return (c - dest); } -static void t2_init_seg(opj_tcd_cblk_dec_t* cblk, int index, int cblksty, int first) { +static opj_bool t2_init_seg(opj_tcd_cblk_dec_t* cblk, int index, int cblksty, int first) { opj_tcd_seg_t* seg; - cblk->segs = (opj_tcd_seg_t*) opj_realloc(cblk->segs, (index + 1) * sizeof(opj_tcd_seg_t)); + opj_tcd_seg_t* segs; + segs = (opj_tcd_seg_t*) opj_realloc(cblk->segs, (index + 1) * sizeof(opj_tcd_seg_t)); + + if (segs == NULL) + { + return OPJ_FALSE; + } + cblk->segs = segs; + seg = &cblk->segs[index]; seg->data = NULL; seg->dataindex = 0; @@ -316,6 +325,8 @@ static void t2_init_seg(opj_tcd_cblk_dec_t* cblk, int index, int cblksty, int fi } else { seg->maxpasses = 109; } + + return OPJ_TRUE; } static int t2_decode_packet(opj_t2_t* t2, unsigned char *src, int len, opj_tcd_tile_t *tile, @@ -330,13 +341,15 @@ static int t2_decode_packet(opj_t2_t* t2, unsigned char *src, int len, opj_tcd_t int precno = pi->precno; /* precinct value */ int layno = pi->layno; /* quality layer value */ - opj_tcd_resolution_t* res = &tile->comps[compno].resolutions[resno]; - unsigned char *hd = NULL; int present; opj_bio_t *bio = NULL; /* BIO component */ - + + opj_tcd_resolution_t* res; + assert(&tile->comps[compno] != NULL); + res = &tile->comps[compno].resolutions[resno]; + if (layno == 0) { for (bandno = 0; bandno < res->numbands; bandno++) { opj_tcd_band_t *band = &res->bands[bandno]; @@ -462,12 +475,22 @@ static int t2_decode_packet(opj_t2_t* t2, unsigned char *src, int len, opj_tcd_t cblk->numlenbits += increment; segno = 0; if (!cblk->numsegs) { - t2_init_seg(cblk, segno, tcp->tccps[compno].cblksty, 1); + if (!t2_init_seg(cblk, segno, tcp->tccps[compno].cblksty, 1)) + { + opj_event_msg(t2->cinfo, EVT_ERROR, "Out of memory\n"); + bio_destroy(bio); + return -999; + } } else { segno = cblk->numsegs - 1; if (cblk->segs[segno].numpasses == cblk->segs[segno].maxpasses) { ++segno; - t2_init_seg(cblk, segno, tcp->tccps[compno].cblksty, 0); + if (!t2_init_seg(cblk, segno, tcp->tccps[compno].cblksty, 0)) + { + opj_event_msg(t2->cinfo, EVT_ERROR, "Out of memory\n"); + bio_destroy(bio); + return -999; + } } } n = cblk->numnewpasses; @@ -478,7 +501,12 @@ static int t2_decode_packet(opj_t2_t* t2, unsigned char *src, int len, opj_tcd_t n -= cblk->segs[segno].numnewpasses; if (n > 0) { ++segno; - t2_init_seg(cblk, segno, tcp->tccps[compno].cblksty, 0); + if (!t2_init_seg(cblk, segno, tcp->tccps[compno].cblksty, 0)) + { + opj_event_msg(t2->cinfo, EVT_ERROR, "Out of memory\n"); + bio_destroy(bio); + return -999; + } } } while (n > 0); } @@ -714,7 +742,11 @@ int t2_decode_packets(opj_t2_t *t2, unsigned char *src, int len, int tileno, opj } else { e = 0; } - if(e == -999) return -999; + if(e == -999) + { + pi_destroy(pi, cp, tileno); + return -999; + } /* progression in resolution */ image->comps[pi[pino].compno].resno_decoded = (e > 0) ? diff --git a/extern/libopenjpeg/tcd.c b/extern/libopenjpeg/tcd.c index 18cdbc786bc..62904eb2c65 100644 --- a/extern/libopenjpeg/tcd.c +++ b/extern/libopenjpeg/tcd.c @@ -30,7 +30,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define _ISOC99_SOURCE /* lrintf is C99 */ #include "opj_includes.h" +#include <assert.h> void tcd_dump(FILE *fd, opj_tcd_t *tcd, opj_tcd_image_t * img) { int tileno, compno, resno, bandno, precno;/*, cblkno;*/ @@ -249,7 +251,9 @@ void tcd_malloc_encode(opj_tcd_t *tcd, opj_image_t * image, opj_cp_t * cp, int c cbgwidthexpn = pdx - 1; cbgheightexpn = pdy - 1; } - + (void)brcbgyend; + (void)brcbgxend; + cblkwidthexpn = int_min(tccp->cblkw, cbgwidthexpn); cblkheightexpn = int_min(tccp->cblkh, cbgheightexpn); @@ -333,8 +337,10 @@ void tcd_malloc_encode(opj_tcd_t *tcd, opj_image_t * image, opj_cp_t * cp, int c cblk->y0 = int_max(cblkystart, prc->y0); cblk->x1 = int_min(cblkxend, prc->x1); cblk->y1 = int_min(cblkyend, prc->y1); - cblk->data = (unsigned char*) opj_calloc(8192+2, sizeof(unsigned char)); + cblk->data = (unsigned char*) opj_calloc(9728+2, sizeof(unsigned char)); /* FIXME: mqc_init_enc and mqc_byteout underrun the buffer if we don't do this. Why? */ + cblk->data[0] = 0; + cblk->data[1] = 0; cblk->data += 2; cblk->layers = (opj_tcd_layer_t*) opj_calloc(100, sizeof(opj_tcd_layer_t)); cblk->passes = (opj_tcd_pass_t*) opj_calloc(100, sizeof(opj_tcd_pass_t)); @@ -509,6 +515,8 @@ void tcd_init_encode(opj_tcd_t *tcd, opj_image_t * image, opj_cp_t * cp, int cur cbgwidthexpn = pdx - 1; cbgheightexpn = pdy - 1; } + (void)brcbgyend; + (void)brcbgxend; cblkwidthexpn = int_min(tccp->cblkw, cbgwidthexpn); cblkheightexpn = int_min(tccp->cblkh, cbgheightexpn); @@ -594,6 +602,8 @@ void tcd_init_encode(opj_tcd_t *tcd, opj_image_t * image, opj_cp_t * cp, int cur cblk->y1 = int_min(cblkyend, prc->y1); cblk->data = (unsigned char*) opj_calloc(8192+2, sizeof(unsigned char)); /* FIXME: mqc_init_enc and mqc_byteout underrun the buffer if we don't do this. Why? */ + cblk->data[0] = 0; + cblk->data[1] = 0; cblk->data += 2; cblk->layers = (opj_tcd_layer_t*) opj_calloc(100, sizeof(opj_tcd_layer_t)); cblk->passes = (opj_tcd_pass_t*) opj_calloc(100, sizeof(opj_tcd_pass_t)); @@ -614,7 +624,7 @@ void tcd_malloc_decode(opj_tcd_t *tcd, opj_image_t * image, opj_cp_t * cp) { tcd->image = image; tcd->tcd_image->tw = cp->tw; tcd->tcd_image->th = cp->th; - tcd->tcd_image->tiles = (opj_tcd_tile_t *) opj_malloc(cp->tw * cp->th * sizeof(opj_tcd_tile_t)); + tcd->tcd_image->tiles = (opj_tcd_tile_t *) opj_calloc(cp->tw * cp->th, sizeof(opj_tcd_tile_t)); /* Allocate place to store the decoded data = final image @@ -690,6 +700,12 @@ void tcd_malloc_decode_tile(opj_tcd_t *tcd, opj_image_t * image, opj_cp_t * cp, opj_tccp_t *tccp = &tcp->tccps[compno]; opj_tcd_tilecomp_t *tilec = &tile->comps[compno]; + if (tccp->numresolutions <= 0) + { + cp->tileno[tileno] = -1; + return; + } + /* border of each tile component (global) */ tilec->x0 = int_ceildiv(tile->x0, image->comps[compno].dx); tilec->y0 = int_ceildiv(tile->y0, image->comps[compno].dy); @@ -749,6 +765,8 @@ void tcd_malloc_decode_tile(opj_tcd_t *tcd, opj_image_t * image, opj_cp_t * cp, cbgwidthexpn = pdx - 1; cbgheightexpn = pdy - 1; } + (void)brcbgyend; + (void)brcbgxend; cblkwidthexpn = int_min(tccp->cblkw, cbgwidthexpn); cblkheightexpn = int_min(tccp->cblkh, cbgheightexpn); @@ -1370,16 +1388,31 @@ opj_bool tcd_decode_tile(opj_tcd_t *tcd, unsigned char *src, int len, int tileno if (l == -999) { eof = 1; opj_event_msg(tcd->cinfo, EVT_ERROR, "tcd_decode: incomplete bistream\n"); + return OPJ_FALSE; } /*------------------TIER1-----------------*/ t1_time = opj_clock(); /* time needed to decode a tile */ t1 = t1_create(tcd->cinfo); + if (t1 == NULL) + { + opj_event_msg(tcd->cinfo, EVT_ERROR, "Out of memory\n"); + t1_destroy(t1); + return OPJ_FALSE; + } + for (compno = 0; compno < tile->numcomps; ++compno) { opj_tcd_tilecomp_t* tilec = &tile->comps[compno]; /* The +3 is headroom required by the vectorized DWT */ tilec->data = (int*) opj_aligned_malloc((((tilec->x1 - tilec->x0) * (tilec->y1 - tilec->y0))+3) * sizeof(int)); + if (tilec->data == NULL) + { + opj_event_msg(tcd->cinfo, EVT_ERROR, "Out of memory\n"); + t1_destroy(t1); + return OPJ_FALSE; + } + t1_decode_cblks(t1, tilec, &tcd->tcp->tccps[compno]); } t1_destroy(t1); @@ -1394,13 +1427,15 @@ opj_bool tcd_decode_tile(opj_tcd_t *tcd, unsigned char *src, int len, int tileno int numres2decode; if (tcd->cp->reduce != 0) { - tcd->image->comps[compno].resno_decoded = - tile->comps[compno].numresolutions - tcd->cp->reduce - 1; - if (tcd->image->comps[compno].resno_decoded < 0) { + if ( tile->comps[compno].numresolutions < ( tcd->cp->reduce - 1 ) ) { opj_event_msg(tcd->cinfo, EVT_ERROR, "Error decoding tile. The number of resolutions to remove [%d+1] is higher than the number " " of resolutions in the original codestream [%d]\nModify the cp_reduce parameter.\n", tcd->cp->reduce, tile->comps[compno].numresolutions); return OPJ_FALSE; } + else { + tcd->image->comps[compno].resno_decoded = + tile->comps[compno].numresolutions - tcd->cp->reduce - 1; + } } numres2decode = tcd->image->comps[compno].resno_decoded + 1; @@ -1421,6 +1456,13 @@ opj_bool tcd_decode_tile(opj_tcd_t *tcd, unsigned char *src, int len, int tileno int n = (tile->comps[0].x1 - tile->comps[0].x0) * (tile->comps[0].y1 - tile->comps[0].y0); if (tile->numcomps >= 3 ){ + /* testcase 1336.pdf.asan.47.376 */ + if ((tile->comps[0].x1 - tile->comps[0].x0) * (tile->comps[0].y1 - tile->comps[0].y0) < n || + ( tile->comps[1].x1 - tile->comps[1].x0) * (tile->comps[1].y1 - tile->comps[1].y0) < n || + ( tile->comps[2].x1 - tile->comps[2].x0) * (tile->comps[2].y1 - tile->comps[2].y0) < n) { + opj_event_msg(tcd->cinfo, EVT_ERROR, "Tiles don't all have the same dimension. Skip the MCT step.\n"); + return OPJ_FALSE; + } if (tcd->tcp->tccps[0].qmfbid == 1) { mct_decode( tile->comps[0].data, @@ -1452,18 +1494,33 @@ opj_bool tcd_decode_tile(opj_tcd_t *tcd, unsigned char *src, int len, int tileno int tw = tilec->x1 - tilec->x0; int w = imagec->w; + int i, j; int offset_x = int_ceildivpow2(imagec->x0, imagec->factor); int offset_y = int_ceildivpow2(imagec->y0, imagec->factor); + /* NR-DEC-2977.pdf.asan.67.2198.jp2-52-decode */ + if( res->x0 - offset_x < 0 || res->x1 - offset_x < 0 + || res->y0 - offset_y < 0 || res->y1 - offset_y < 0 ) + { + opj_event_msg(tcd->cinfo, EVT_ERROR, "Impossible offsets %d / %d\n", offset_x, offset_y); + return OPJ_FALSE; + } + assert( 0 <= res->x0 - offset_x && 0 <= res->x1 - offset_x ); + assert( 0 <= res->y0 - offset_y && 0 <= res->y1 - offset_y ); - int i, j; if(!imagec->data){ imagec->data = (int*) opj_malloc(imagec->w * imagec->h * sizeof(int)); } + if (!imagec->data) + { + opj_event_msg(tcd->cinfo, EVT_ERROR, "Out of memory\n"); + return OPJ_FALSE; + } if(tcd->tcp->tccps[compno].qmfbid == 1) { for(j = res->y0; j < res->y1; ++j) { for(i = res->x0; i < res->x1; ++i) { int v = tilec->data[i - res->x0 + (j - res->y0) * tw]; v += adjust; + /*assert( (i - offset_x) + (j - offset_y) * w >= 0 );*/ imagec->data[(i - offset_x) + (j - offset_y) * w] = int_clamp(v, min, max); } } @@ -1473,6 +1530,7 @@ opj_bool tcd_decode_tile(opj_tcd_t *tcd, unsigned char *src, int len, int tileno float tmp = ((float*)tilec->data)[i - res->x0 + (j - res->y0) * tw]; int v = lrintf(tmp); v += adjust; + /*assert( (i - offset_x) + (j - offset_y) * w >= 0 );*/ imagec->data[(i - offset_x) + (j - offset_y) * w] = int_clamp(v, min, max); } } @@ -1492,32 +1550,51 @@ opj_bool tcd_decode_tile(opj_tcd_t *tcd, unsigned char *src, int len, int tileno void tcd_free_decode(opj_tcd_t *tcd) { opj_tcd_image_t *tcd_image = tcd->tcd_image; + int i = 0; + for (i = 0; i < tcd_image->tw * tcd_image->th; i++) + { + tcd_free_decode_tile(tcd, i); + } + opj_free(tcd_image->tiles); } void tcd_free_decode_tile(opj_tcd_t *tcd, int tileno) { - int compno,resno,bandno,precno; + int compno,resno,bandno,precno,cblkno; opj_tcd_image_t *tcd_image = tcd->tcd_image; opj_tcd_tile_t *tile = &tcd_image->tiles[tileno]; - for (compno = 0; compno < tile->numcomps; compno++) { - opj_tcd_tilecomp_t *tilec = &tile->comps[compno]; - for (resno = 0; resno < tilec->numresolutions; resno++) { - opj_tcd_resolution_t *res = &tilec->resolutions[resno]; - for (bandno = 0; bandno < res->numbands; bandno++) { - opj_tcd_band_t *band = &res->bands[bandno]; - for (precno = 0; precno < res->ph * res->pw; precno++) { - opj_tcd_precinct_t *prec = &band->precincts[precno]; - if (prec->imsbtree != NULL) tgt_destroy(prec->imsbtree); - if (prec->incltree != NULL) tgt_destroy(prec->incltree); + if (tile->comps != NULL) { + for (compno = 0; compno < tile->numcomps; compno++) { + opj_tcd_tilecomp_t *tilec = &tile->comps[compno]; + for (resno = 0; resno < tilec->numresolutions; resno++) { + opj_tcd_resolution_t *res = &tilec->resolutions[resno]; + for (bandno = 0; bandno < res->numbands; bandno++) { + opj_tcd_band_t *band = &res->bands[bandno]; + for (precno = 0; precno < res->ph * res->pw; precno++) { + opj_tcd_precinct_t *prec = &band->precincts[precno]; + if (prec->cblks.dec != NULL) { + for (cblkno = 0; cblkno < prec->cw * prec->ch; ++cblkno) { + opj_tcd_cblk_dec_t* cblk = &prec->cblks.dec[cblkno]; + opj_free(cblk->data); + opj_free(cblk->segs); + } + opj_free(prec->cblks.dec); + } + if (prec->imsbtree != NULL) tgt_destroy(prec->imsbtree); + if (prec->incltree != NULL) tgt_destroy(prec->incltree); + + + } + opj_free(band->precincts); } - opj_free(band->precincts); } + opj_free(tilec->resolutions); } - opj_free(tilec->resolutions); + opj_free(tile->comps); + tile->comps = NULL; } - opj_free(tile->comps); } diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 32cf83a2b6a..6e7056f6c8a 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -370,6 +370,26 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): min=2, max=65536 ) + cls.dicing_rate = FloatProperty( + name="Dicing Rate", + description="Size of a micropolygon in pixels", + min=0.1, max=1000.0, + default=1.0, + ) + cls.preview_dicing_rate = FloatProperty( + name="Preview Dicing Rate", + description="Size of a micropolygon in pixels during preview render", + min=0.1, max=1000.0, + default=8.0, + ) + + cls.max_subdivisions = IntProperty( + name="Max Subdivisions", + description="Stop subdividing when this level is reached even if the dice rate would produce finer tessellation", + min=0, max=16, + default=12, + ) + cls.film_exposure = FloatProperty( name="Exposure", description="Image brightness scale", @@ -945,7 +965,7 @@ class CyclesMeshSettings(bpy.types.PropertyGroup): ) cls.dicing_rate = FloatProperty( name="Dicing Rate", - description="Size of a micropolygon in pixels", + description="Multiplier for scene dicing rate", min=0.1, max=1000.0, default=1.0, ) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index ae9b3615649..9cffa089ce8 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -209,8 +209,8 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel): draw_samples_info(layout, context) -class CyclesRender_PT_volume_sampling(CyclesButtonsPanel, Panel): - bl_label = "Volume Sampling" +class CyclesRender_PT_geometery(CyclesButtonsPanel, Panel): + bl_label = "Geometry" bl_options = {'DEFAULT_CLOSED'} def draw(self, context): @@ -219,11 +219,30 @@ class CyclesRender_PT_volume_sampling(CyclesButtonsPanel, Panel): scene = context.scene cscene = scene.cycles - row = layout.row() - row.label("Heterogeneous:") - row = layout.row() - row.prop(cscene, "volume_step_size") - row.prop(cscene, "volume_max_steps") + if cscene.feature_set == 'EXPERIMENTAL': + split = layout.split() + + col = split.column() + + sub = col.column(align=True) + sub.label("Volume Sampling:") + sub.prop(cscene, "volume_step_size") + sub.prop(cscene, "volume_max_steps") + + col = split.column() + + sub = col.column(align=True) + sub.label("Subdivision Rate:") + sub.prop(cscene, "dicing_rate", text="Render") + sub.prop(cscene, "preview_dicing_rate", text="Preview") + sub.separator() + sub.prop(cscene, "max_subdivisions") + else: + row = layout.row() + row.label("Volume Sampling:") + row = layout.row() + row.prop(cscene, "volume_step_size") + row.prop(cscene, "volume_max_steps") class CyclesRender_PT_light_paths(CyclesButtonsPanel, Panel): @@ -694,8 +713,7 @@ class Cycles_PT_mesh_displacement(CyclesButtonsPanel, Panel): sub.prop(cdata, "subdivision_type", text="") if cdata.subdivision_type != 'NONE': - sub.label(text="Subdivision Rate:") - sub.prop(cdata, "dicing_rate", text="Render") + sub.prop(cdata, "dicing_rate") class CyclesObject_PT_motion_blur(CyclesButtonsPanel, Panel): bl_label = "Motion Blur" diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index a590b5f5532..1f0aa5eef34 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -308,7 +308,7 @@ static void create_mesh_volume_attribute(BL::Object& b_ob, is_float, is_linear, INTERPOLATION_LINEAR, - EXTENSION_REPEAT, + EXTENSION_CLIP, true); } @@ -660,13 +660,16 @@ static void create_subd_mesh(Scene *scene, BL::Object b_ob, BL::Mesh& b_mesh, PointerRNA *cmesh, - const vector<uint>& used_shaders) + const vector<uint>& used_shaders, + float dicing_rate, + int max_subdivisions) { Mesh basemesh; create_mesh(scene, &basemesh, b_mesh, used_shaders); SubdParams sdparams(mesh, used_shaders[0], true, false); - sdparams.dicing_rate = RNA_float_get(cmesh, "dicing_rate"); + sdparams.dicing_rate = max(0.1f, RNA_float_get(cmesh, "dicing_rate") * dicing_rate); + sdparams.max_level = max_subdivisions; scene->camera->update(); sdparams.camera = scene->camera; @@ -783,7 +786,8 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob, if(b_mesh) { if(render_layer.use_surfaces && !hide_tris) { if(cmesh.data && experimental && RNA_enum_get(&cmesh, "subdivision_type")) - create_subd_mesh(scene, mesh, b_ob, b_mesh, &cmesh, used_shaders); + create_subd_mesh(scene, mesh, b_ob, b_mesh, &cmesh, used_shaders, + dicing_rate, max_subdivisions); else create_mesh(scene, mesh, b_mesh, used_shaders); diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index 989ba80c001..ceb9cbf242e 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -644,7 +644,20 @@ static PyObject *set_resumable_chunks_func(PyObject * /*self*/, PyObject *args) if(!PyArg_ParseTuple(args, "ii", &num_resumable_chunks, ¤t_resumable_chunk)) { - return NULL; + Py_RETURN_NONE; + } + + if(num_resumable_chunks <= 0) { + fprintf(stderr, "Cycles: Bad value for number of resumable chunks.\n"); + abort(); + Py_RETURN_NONE; + } + if(current_resumable_chunk < 1 || + current_resumable_chunk > num_resumable_chunks) + { + fprintf(stderr, "Cycles: Bad value for current resumable chunk number.\n"); + abort(); + Py_RETURN_NONE; } VLOG(1) << "Initialized resumable render: " @@ -653,6 +666,10 @@ static PyObject *set_resumable_chunks_func(PyObject * /*self*/, PyObject *args) BlenderSession::num_resumable_chunks = num_resumable_chunks; BlenderSession::current_resumable_chunk = current_resumable_chunk; + printf("Cycles: Will render chunk %d of %d\n", + current_resumable_chunk, + num_resumable_chunks); + Py_RETURN_NONE; } diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 71f4affd0e7..914b348d65d 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -62,8 +62,13 @@ BlenderSync::BlenderSync(BL::RenderEngine& b_engine, preview(preview), experimental(false), is_cpu(is_cpu), + dicing_rate(1.0f), + max_subdivisions(12), progress(progress) { + PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + dicing_rate = preview ? RNA_float_get(&cscene, "preview_dicing_rate") : RNA_float_get(&cscene, "dicing_rate"); + max_subdivisions = RNA_int_get(&cscene, "max_subdivisions"); } BlenderSync::~BlenderSync() @@ -117,11 +122,41 @@ bool BlenderSync::sync_recalc() } } + bool dicing_prop_changed = false; + + if(experimental) { + PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + + float updated_dicing_rate = preview ? RNA_float_get(&cscene, "preview_dicing_rate") + : RNA_float_get(&cscene, "dicing_rate"); + + if(dicing_rate != updated_dicing_rate) { + dicing_rate = updated_dicing_rate; + dicing_prop_changed = true; + } + + int updated_max_subdivisions = RNA_int_get(&cscene, "max_subdivisions"); + + if(max_subdivisions != updated_max_subdivisions) { + max_subdivisions = updated_max_subdivisions; + dicing_prop_changed = true; + } + } + BL::BlendData::meshes_iterator b_mesh; - for(b_data.meshes.begin(b_mesh); b_mesh != b_data.meshes.end(); ++b_mesh) - if(b_mesh->is_updated()) + for(b_data.meshes.begin(b_mesh); b_mesh != b_data.meshes.end(); ++b_mesh) { + if(b_mesh->is_updated()) { mesh_map.set_recalc(*b_mesh); + } + else if(dicing_prop_changed) { + PointerRNA cmesh = RNA_pointer_get(&b_mesh->ptr, "cycles"); + + if(RNA_enum_get(&cmesh, "subdivision_type")) + mesh_map.set_recalc(*b_mesh); + } + } + BL::BlendData::worlds_iterator b_world; diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 33c8223cb9d..9d36e5c0214 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -166,6 +166,9 @@ private: bool experimental; bool is_cpu; + float dicing_rate; + int max_subdivisions; + struct RenderLayerInfo { RenderLayerInfo() : scene_layer(0), layer(0), diff --git a/intern/cycles/bvh/bvh_binning.cpp b/intern/cycles/bvh/bvh_binning.cpp index 8745e39c21e..b07e870d759 100644 --- a/intern/cycles/bvh/bvh_binning.cpp +++ b/intern/cycles/bvh/bvh_binning.cpp @@ -176,7 +176,7 @@ void BVHObjectBinning::split(BVHReference* prims, BVHObjectBinning& left_o, BVHO prefetch_L2(&prims[start() + l + 8]); prefetch_L2(&prims[start() + r - 8]); - BVHReference prim = prims[start() + l]; + const BVHReference& prim = prims[start() + l]; float3 center = prim.bounds().center2(); if(get_bin(center)[dim] < pos) { diff --git a/intern/cycles/bvh/bvh_sort.cpp b/intern/cycles/bvh/bvh_sort.cpp index 3140bf23376..c12751979cd 100644 --- a/intern/cycles/bvh/bvh_sort.cpp +++ b/intern/cycles/bvh/bvh_sort.cpp @@ -14,22 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + #include "bvh_build.h" #include "bvh_sort.h" #include "util_algorithm.h" #include "util_debug.h" +#include "util_task.h" CCL_NAMESPACE_BEGIN -/* silly workaround for float extended precision that happens when compiling +static const int BVH_SORT_THRESHOLD = 4096; + +/* Silly workaround for float extended precision that happens when compiling * on x86, due to one float staying in 80 bit precision register and the other - * not, which causes the strictly weak ordering to break */ + * not, which causes the strictly weak ordering to break. + */ #if !defined(__i386__) -#define NO_EXTENDED_PRECISION +# define NO_EXTENDED_PRECISION #else -#define NO_EXTENDED_PRECISION volatile +# define NO_EXTENDED_PRECISION volatile #endif struct BVHReferenceCompare { @@ -41,28 +45,148 @@ public: dim = dim_; } - bool operator()(const BVHReference& ra, const BVHReference& rb) + /* Compare two references. + * + * Returns value is similar to return value of strcmp(). + */ + __forceinline int compare(const BVHReference& ra, + const BVHReference& rb) const { NO_EXTENDED_PRECISION float ca = ra.bounds().min[dim] + ra.bounds().max[dim]; NO_EXTENDED_PRECISION float cb = rb.bounds().min[dim] + rb.bounds().max[dim]; - if(ca < cb) return true; - else if(ca > cb) return false; - else if(ra.prim_object() < rb.prim_object()) return true; - else if(ra.prim_object() > rb.prim_object()) return false; - else if(ra.prim_index() < rb.prim_index()) return true; - else if(ra.prim_index() > rb.prim_index()) return false; - else if(ra.prim_type() < rb.prim_type()) return true; - else if(ra.prim_type() > rb.prim_type()) return false; + if(ca < cb) return -1; + else if(ca > cb) return 1; + else if(ra.prim_object() < rb.prim_object()) return -1; + else if(ra.prim_object() > rb.prim_object()) return 1; + else if(ra.prim_index() < rb.prim_index()) return -1; + else if(ra.prim_index() > rb.prim_index()) return 1; + else if(ra.prim_type() < rb.prim_type()) return -1; + else if(ra.prim_type() > rb.prim_type()) return 1; + + return 0; + } + + bool operator()(const BVHReference& ra, const BVHReference& rb) + { + return (compare(ra, rb) < 0); + } +}; + +static void bvh_reference_sort_threaded(TaskPool *task_pool, + BVHReference *data, + const int job_start, + const int job_end, + const BVHReferenceCompare& compare); - return false; +class BVHSortTask : public Task { +public: + BVHSortTask(TaskPool *task_pool, + BVHReference *data, + const int job_start, + const int job_end, + const BVHReferenceCompare& compare) + { + run = function_bind(bvh_reference_sort_threaded, + task_pool, + data, + job_start, + job_end, + compare); } }; +/* Multi-threaded reference sort. */ +static void bvh_reference_sort_threaded(TaskPool *task_pool, + BVHReference *data, + const int job_start, + const int job_end, + const BVHReferenceCompare& compare) +{ + int start = job_start, end = job_end; + bool have_work = (start < end); + while(have_work) { + const int count = job_end - job_start; + if(count < BVH_SORT_THRESHOLD) { + /* Number of reference low enough, faster to finish the job + * in one thread rather than to spawn more threads. + */ + sort(data+job_start, data+job_end+1, compare); + break; + } + /* Single QSort step. + * Use median-of-three method for the pivot point. + */ + int left = start, right = end; + int center = (left + right) >> 1; + if(compare.compare(data[left], data[center]) > 0) { + swap(data[left], data[center]); + } + if(compare.compare(data[left], data[right]) > 0) { + swap(data[left], data[right]); + } + if (compare.compare(data[center], data[right]) > 0) { + swap(data[center], data[right]); + } + swap(data[center], data[right - 1]); + BVHReference median = data[right - 1]; + do { + while(compare.compare(data[left], median) < 0) { + ++left; + } + while(compare.compare(data[right], median) > 0) { + --right; + } + if(left <= right) { + swap(data[left], data[right]); + ++left; + --right; + } + } while(left <= right); + /* We only create one new task here to reduce downside effects of + * latency in TaskScheduler. + * So generally current thread keeps working on the left part of the + * array, and we create new task for the right side. + * However, if there's nothing to be done in the left side of the array + * we don't create any tasks and make it so current thread works on the + * right side. + */ + have_work = false; + if(left < end) { + if(start < right) { + task_pool->push(new BVHSortTask(task_pool, + data, + left, end, + compare), true); + } + else { + start = left; + have_work = true; + } + } + if(start < right) { + end = right; + have_work = true; + } + } +} + void bvh_reference_sort(int start, int end, BVHReference *data, int dim) { + const int count = end - start; BVHReferenceCompare compare(dim); - sort(data+start, data+end, compare); + if(count < BVH_SORT_THRESHOLD) { + /* It is important to not use any mutex if array is small enough, + * otherwise we end up in situation when we're going to sleep far + * too often. + */ + sort(data+start, data+end, compare); + } + else { + TaskPool task_pool; + bvh_reference_sort_threaded(&task_pool, data, start, end - 1, dim); + task_pool.wait_work(); + } } CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp index 22365bbdbc3..f1a17d652da 100644 --- a/intern/cycles/device/device_cuda.cpp +++ b/intern/cycles/device/device_cuda.cpp @@ -606,22 +606,27 @@ public: cuda_assert(cuTexRefSetFlags(texref, CU_TRSF_READ_AS_INTEGER)); } + CUaddress_mode address_mode = CU_TR_ADDRESS_MODE_WRAP; switch(extension) { case EXTENSION_REPEAT: - cuda_assert(cuTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_WRAP)); - cuda_assert(cuTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_WRAP)); + address_mode = CU_TR_ADDRESS_MODE_WRAP; break; case EXTENSION_EXTEND: - cuda_assert(cuTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_CLAMP)); - cuda_assert(cuTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_CLAMP)); + address_mode = CU_TR_ADDRESS_MODE_CLAMP; break; case EXTENSION_CLIP: - cuda_assert(cuTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_BORDER)); - cuda_assert(cuTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_BORDER)); + address_mode = CU_TR_ADDRESS_MODE_BORDER; break; default: assert(0); + break; + } + cuda_assert(cuTexRefSetAddressMode(texref, 0, address_mode)); + cuda_assert(cuTexRefSetAddressMode(texref, 1, address_mode)); + if(mem.data_depth > 1) { + cuda_assert(cuTexRefSetAddressMode(texref, 2, address_mode)); } + cuda_assert(cuTexRefSetFormat(texref, format, mem.data_elements)); cuda_pop_context(); diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 3c17429fd09..9f3fb66c85a 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -165,6 +165,7 @@ set(SRC_UTIL_HEADERS ../util/util_math.h ../util/util_math_fast.h ../util/util_transform.h + ../util/util_texture.h ../util/util_types.h ) diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h index df7d39532aa..b96a84499b4 100644 --- a/intern/cycles/kernel/kernel_compat_cpu.h +++ b/intern/cycles/kernel/kernel_compat_cpu.h @@ -154,6 +154,7 @@ template<typename T> struct texture_image { break; default: kernel_assert(0); + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); } return read(data[ix + iy*width]); } @@ -183,6 +184,7 @@ template<typename T> struct texture_image { break; default: kernel_assert(0); + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); } float4 r = (1.0f - ty)*(1.0f - tx)*read(data[ix + iy*width]); @@ -231,6 +233,7 @@ template<typename T> struct texture_image { break; default: kernel_assert(0); + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); } const int xc[4] = {pix, ix, nix, nnix}; @@ -298,6 +301,7 @@ template<typename T> struct texture_image { break; default: kernel_assert(0); + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); } return read(data[ix + iy*width + iz*width*height]); @@ -335,6 +339,7 @@ template<typename T> struct texture_image { break; default: kernel_assert(0); + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); } float4 r; @@ -402,6 +407,7 @@ template<typename T> struct texture_image { break; default: kernel_assert(0); + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); } const int xc[4] = {pix, ix, nix, nnix}; diff --git a/intern/cycles/kernel/kernel_math.h b/intern/cycles/kernel/kernel_math.h index 453f4c8b421..9bee5603474 100644 --- a/intern/cycles/kernel/kernel_math.h +++ b/intern/cycles/kernel/kernel_math.h @@ -20,6 +20,7 @@ #include "util_color.h" #include "util_math.h" #include "util_math_fast.h" +#include "util_texture.h" #include "util_transform.h" #endif /* __KERNEL_MATH_H__ */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index cce4ded3013..c9a895d9aec 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -48,8 +48,6 @@ CCL_NAMESPACE_BEGIN #define BECKMANN_TABLE_SIZE 256 -#define TEX_NUM_FLOAT_IMAGES 5 - #define SHADER_NONE (~0) #define OBJECT_NONE (~0) #define PRIM_NONE (~0) diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h index ed4f6b2d99b..c499773b980 100644 --- a/intern/cycles/kernel/kernel_volume.h +++ b/intern/cycles/kernel/kernel_volume.h @@ -1061,14 +1061,23 @@ ccl_device void kernel_volume_stack_init(KernelGlobals *kg, /* If ray exited the volume and never entered to that volume * it means that camera is inside such a volume. */ - bool is_enclosed = false; - for(int i = 0; i < enclosed_index; ++i) { + bool need_add = true; + for(int i = 0; i < enclosed_index && need_add; ++i) { + /* If ray exited the volume and never entered to that volume + * it means that camera is inside such a volume. + */ if(enclosed_volumes[i] == sd.object) { - is_enclosed = true; + need_add = false; + } + } + for(int i = 0; i < stack_index && need_add; ++i) { + /* Don't add intersections twice. */ + if(stack[i].object == sd.object) { + need_add = false; break; } } - if(is_enclosed == false) { + if(need_add) { stack[stack_index].object = sd.object; stack[stack_index].shader = sd.shader; ++stack_index; diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index 8f2b9423b2c..07ab2f28577 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -16,6 +16,15 @@ CCL_NAMESPACE_BEGIN +/* Float textures on various devices. */ +#if defined(__KERNEL_CPU__) + #define TEX_NUM_FLOAT_IMAGES TEX_NUM_FLOAT_IMAGES_CPU +#elif defined(__KERNEL_CUDA__) + #define TEX_NUM_FLOAT_IMAGES TEX_NUM_FLOAT_IMAGES_CUDA +#else + #define TEX_NUM_FLOAT_IMAGES TEX_NUM_FLOAT_IMAGES_OPENCL +#endif + #ifdef __KERNEL_OPENCL__ /* For OpenCL all images are packed in a single array, and we do manual lookup @@ -50,12 +59,6 @@ ccl_device_inline float svm_image_texture_frac(float x, int *ix) ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint srgb, uint use_alpha) { - /* first slots are used by float textures, which are not supported here */ - if(id < TEX_NUM_FLOAT_IMAGES) - return make_float4(1.0f, 0.0f, 1.0f, 1.0f); - - id -= TEX_NUM_FLOAT_IMAGES; - uint4 info = kernel_tex_fetch(__tex_image_packed_info, id); uint width = info.x; uint height = info.y; diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 0bebdaf8a67..c0cbf0af968 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -22,6 +22,7 @@ #include "util_image.h" #include "util_path.h" #include "util_progress.h" +#include "util_texture.h" #ifdef WITH_OSL #include <OSL/oslexec.h> @@ -29,16 +30,46 @@ CCL_NAMESPACE_BEGIN -ImageManager::ImageManager() +ImageManager::ImageManager(const DeviceInfo& info) { need_update = true; pack_images = false; osl_texture_system = NULL; animation_frame = 0; - tex_num_images = TEX_NUM_IMAGES; - tex_num_float_images = TEX_NUM_FLOAT_IMAGES; - tex_image_byte_start = TEX_IMAGE_BYTE_START; + /* Set image limits */ + + /* CPU */ + if(info.type == DEVICE_CPU) { + tex_num_byte_images = TEX_NUM_BYTE_IMAGES_CPU; + tex_num_float_images = TEX_NUM_FLOAT_IMAGES_CPU; + tex_image_byte_start = TEX_IMAGE_BYTE_START_CPU; + } + /* CUDA (Fermi) */ + else if((info.type == DEVICE_CUDA || info.type == DEVICE_MULTI) && !info.extended_images) { + tex_num_byte_images = TEX_NUM_BYTE_IMAGES_CUDA; + tex_num_float_images = TEX_NUM_FLOAT_IMAGES_CUDA; + tex_image_byte_start = TEX_IMAGE_BYTE_START_CUDA; + } + /* CUDA (Kepler and above) */ + else if((info.type == DEVICE_CUDA || info.type == DEVICE_MULTI) && info.extended_images) { + tex_num_byte_images = TEX_NUM_BYTE_IMAGES_CUDA_KEPLER; + tex_num_float_images = TEX_NUM_FLOAT_IMAGES_CUDA_KEPLER; + tex_image_byte_start = TEX_IMAGE_BYTE_START_CUDA_KELPER; + } + /* OpenCL */ + else if(info.pack_images) { + tex_num_byte_images = TEX_NUM_BYTE_IMAGES_OPENCL; + tex_num_float_images = TEX_NUM_FLOAT_IMAGES_OPENCL; + tex_image_byte_start = TEX_IMAGE_BYTE_START_OPENCL; + } + /* Should never happen */ + else { + tex_num_byte_images = 0; + tex_num_float_images = 0; + tex_image_byte_start = 0; + assert(0); + } } ImageManager::~ImageManager() @@ -59,21 +90,6 @@ void ImageManager::set_osl_texture_system(void *texture_system) osl_texture_system = texture_system; } -void ImageManager::set_extended_image_limits(const DeviceInfo& info) -{ - if(info.type == DEVICE_CPU) { - tex_num_images = TEX_EXTENDED_NUM_IMAGES_CPU; - tex_num_float_images = TEX_EXTENDED_NUM_FLOAT_IMAGES; - tex_image_byte_start = TEX_EXTENDED_IMAGE_BYTE_START; - } - else if((info.type == DEVICE_CUDA || info.type == DEVICE_MULTI) && info.extended_images) { - tex_num_images = TEX_EXTENDED_NUM_IMAGES_GPU; - } - else if(info.pack_images) { - tex_num_images = TEX_PACKED_NUM_IMAGES; - } -} - bool ImageManager::set_animation_frame_update(int frame) { if(frame != animation_frame) { @@ -266,9 +282,9 @@ int ImageManager::add_image(const string& filename, if(slot == images.size()) { /* max images limit reached */ - if(images.size() == tex_num_images) { + if(images.size() == tex_num_byte_images) { printf("ImageManager::add_image: byte image limit reached %d, skipping '%s'\n", - tex_num_images, filename.c_str()); + tex_num_byte_images, filename.c_str()); return -1; } diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index c5561e16cb3..64798d75638 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -24,42 +24,15 @@ #include "util_thread.h" #include "util_vector.h" -#include "kernel_types.h" /* for TEX_NUM_FLOAT_IMAGES */ - CCL_NAMESPACE_BEGIN -/* generic */ -#define TEX_NUM_IMAGES 88 -#define TEX_IMAGE_BYTE_START TEX_NUM_FLOAT_IMAGES - -/* extended gpu */ -#define TEX_EXTENDED_NUM_IMAGES_GPU 145 - -/* extended cpu */ -#define TEX_EXTENDED_NUM_FLOAT_IMAGES 1024 -#define TEX_EXTENDED_NUM_IMAGES_CPU 1024 -#define TEX_EXTENDED_IMAGE_BYTE_START TEX_EXTENDED_NUM_FLOAT_IMAGES - -/* Limitations for packed images. - * - * Technically number of textures is unlimited, but it should in - * fact be in sync with CPU limitations. - */ -#define TEX_PACKED_NUM_IMAGES 1024 - -/* color to use when textures are not found */ -#define TEX_IMAGE_MISSING_R 1 -#define TEX_IMAGE_MISSING_G 0 -#define TEX_IMAGE_MISSING_B 1 -#define TEX_IMAGE_MISSING_A 1 - class Device; class DeviceScene; class Progress; class ImageManager { public: - ImageManager(); + ImageManager(const DeviceInfo& info); ~ImageManager(); int add_image(const string& filename, @@ -89,7 +62,6 @@ public: void set_osl_texture_system(void *texture_system); void set_pack_images(bool pack_images_); - void set_extended_image_limits(const DeviceInfo& info); bool set_animation_frame_update(int frame); bool need_update; @@ -113,7 +85,7 @@ public: }; private: - int tex_num_images; + int tex_num_byte_images; int tex_num_float_images; int tex_image_byte_start; thread_mutex device_mutex; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index ece4919dedb..29163c53109 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -54,7 +54,7 @@ Scene::Scene(const SceneParams& params_, const DeviceInfo& device_info_) mesh_manager = new MeshManager(); object_manager = new ObjectManager(); integrator = new Integrator(); - image_manager = new ImageManager(); + image_manager = new ImageManager(device_info_); particle_system_manager = new ParticleSystemManager(); curve_system_manager = new CurveSystemManager(); bake_manager = new BakeManager(); @@ -64,9 +64,6 @@ Scene::Scene(const SceneParams& params_, const DeviceInfo& device_info_) shader_manager = ShaderManager::create(this, params.shadingsystem); else shader_manager = ShaderManager::create(this, SHADINGSYSTEM_SVM); - - /* Extended image limits for CPU and GPUs */ - image_manager->set_extended_image_limits(device_info_); } Scene::~Scene() diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 026bef4088c..d30a0cb45fe 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -22,11 +22,10 @@ #include "device_memory.h" -#include "kernel_types.h" - #include "util_param.h" #include "util_string.h" #include "util_system.h" +#include "util_texture.h" #include "util_thread.h" #include "util_types.h" #include "util_vector.h" @@ -110,8 +109,8 @@ public: device_vector<uint> sobol_directions; /* cpu images */ - device_vector<uchar4> tex_image[TEX_EXTENDED_NUM_IMAGES_CPU]; - device_vector<float4> tex_float_image[TEX_EXTENDED_NUM_FLOAT_IMAGES]; + device_vector<uchar4> tex_image[TEX_NUM_BYTE_IMAGES_CPU]; + device_vector<float4> tex_float_image[TEX_NUM_FLOAT_IMAGES_CPU]; /* opencl images */ device_vector<uchar4> tex_image_packed; diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 84a420ce9b6..24f48b61349 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -831,7 +831,8 @@ void Session::update_status_time(bool show_pause, bool show_done) string status, substatus; if(!params.progressive) { - const int progress_sample = progress.get_sample(), num_samples = tile_manager.num_samples; + const int progress_sample = progress.get_sample(), + num_samples = tile_manager.get_num_effective_samples(); const bool is_gpu = params.device.type == DEVICE_CUDA || params.device.type == DEVICE_OPENCL; const bool is_multidevice = params.device.multi_devices.size() > 1; const bool is_cpu = params.device.type == DEVICE_CPU; @@ -873,7 +874,9 @@ void Session::update_status_time(bool show_pause, bool show_done) else if(tile_manager.num_samples == INT_MAX) substatus = string_printf("Path Tracing Sample %d", sample+1); else - substatus = string_printf("Path Tracing Sample %d/%d", sample+1, tile_manager.num_samples); + substatus = string_printf("Path Tracing Sample %d/%d", + sample+1, + tile_manager.get_num_effective_samples()); if(show_pause) { status = "Paused"; diff --git a/intern/cycles/subd/subd_dice.cpp b/intern/cycles/subd/subd_dice.cpp index 4f27eaf7d1b..a5dfcd21ceb 100644 --- a/intern/cycles/subd/subd_dice.cpp +++ b/intern/cycles/subd/subd_dice.cpp @@ -259,17 +259,10 @@ float QuadDice::scale_factor(SubPatch& sub, EdgeFactors& ef, int Mu, int Mv) void QuadDice::add_corners(SubPatch& sub) { /* add verts for patch corners */ - if(sub.patch->is_triangle()) { - add_vert(sub, 0.0f, 0.0f); - add_vert(sub, 1.0f, 0.0f); - add_vert(sub, 0.0f, 1.0f); - } - else { - add_vert(sub, 0.0f, 0.0f); - add_vert(sub, 1.0f, 0.0f); - add_vert(sub, 0.0f, 1.0f); - add_vert(sub, 1.0f, 1.0f); - } + add_vert(sub, 0.0f, 0.0f); + add_vert(sub, 1.0f, 0.0f); + add_vert(sub, 0.0f, 1.0f); + add_vert(sub, 1.0f, 1.0f); } void QuadDice::add_grid(SubPatch& sub, int Mu, int Mv, int offset) @@ -458,15 +451,21 @@ void TriangleDice::add_grid(SubPatch& sub, EdgeFactors& ef, int M) add_triangle(sub.patch, outer_w[0], outer_u[0], outer_v[0]); } else { - /* center vertex + 6 triangles */ + /* center vertex + up to 6 triangles */ int center = add_vert(sub, make_float2(1.0f/3.0f, 1.0f/3.0f)); add_triangle(sub.patch, outer_w[0], outer_w[1], center); - add_triangle(sub.patch, outer_w[1], outer_w[2], center); + /* if this is false then there is only one triangle on this side */ + if(outer_w.size() > 2) + add_triangle(sub.patch, outer_w[1], outer_w[2], center); + add_triangle(sub.patch, outer_u[0], outer_u[1], center); - add_triangle(sub.patch, outer_u[1], outer_u[2], center); + if(outer_u.size() > 2) + add_triangle(sub.patch, outer_u[1], outer_u[2], center); + add_triangle(sub.patch, outer_v[0], outer_v[1], center); - add_triangle(sub.patch, outer_v[1], outer_v[2], center); + if(outer_v.size() > 2) + add_triangle(sub.patch, outer_v[1], outer_v[2], center); } } @@ -475,6 +474,16 @@ void TriangleDice::dice(SubPatch& sub, EdgeFactors& ef) /* todo: handle 2 1 1 resolution */ int M = max(ef.tu, max(ef.tv, ef.tw)); + /* Due to the "slant" of the edges of a triangle compared to a quad, the internal + * triangles end up smaller, causing over-tessellation. This is to correct for this + * difference in area. Technically its only correct for equilateral triangles, but + * its better than how it was. + * + * (2*cos(radians(30))/3)**0.5 + */ + float S = 0.7598356856515927f; + M = max((int)ceil(S*M), 1); + reserve(ef, M); add_grid(sub, ef, M); diff --git a/intern/cycles/subd/subd_dice.h b/intern/cycles/subd/subd_dice.h index 2b11e4b0bf5..8450a43129e 100644 --- a/intern/cycles/subd/subd_dice.h +++ b/intern/cycles/subd/subd_dice.h @@ -40,6 +40,7 @@ struct SubdParams { int test_steps; int split_threshold; float dicing_rate; + int max_level; Camera *camera; Transform objecttoworld; @@ -53,6 +54,7 @@ struct SubdParams { test_steps = 3; split_threshold = 1; dicing_rate = 0.1f; + max_level = 12; camera = NULL; } diff --git a/intern/cycles/subd/subd_split.cpp b/intern/cycles/subd/subd_split.cpp index 70081673597..c4af8cc8c43 100644 --- a/intern/cycles/subd/subd_split.cpp +++ b/intern/cycles/subd/subd_split.cpp @@ -112,6 +112,59 @@ void DiagSplit::partition_edge(Patch *patch, float2 *P, int *t0, int *t1, float2 } } +static float2 right_to_equilateral(float2 P) +{ + static const float2 A = make_float2(1.0f, 0.5f); + static const float2 B = make_float2(0.0f, sinf(M_PI_F/3.0f)); + return make_float2(dot(P, A), dot(P, B)); +} + +static void limit_edge_factors(const TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef, int max_t) +{ + float2 Pu = sub.Pu; + float2 Pv = sub.Pv; + float2 Pw = sub.Pw; + + if(sub.patch->is_triangle()) { + Pu = right_to_equilateral(Pu); + Pv = right_to_equilateral(Pv); + Pw = right_to_equilateral(Pw); + } + + int tu = int(max_t * len(Pw - Pv)); + int tv = int(max_t * len(Pw - Pu)); + int tw = int(max_t * len(Pv - Pu)); + + ef.tu = tu <= 1 ? 1 : min(ef.tu, tu); + ef.tv = tv <= 1 ? 1 : min(ef.tv, tv); + ef.tw = tw <= 1 ? 1 : min(ef.tw, tw); +} + +static void limit_edge_factors(const QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int max_t) +{ + float2 P00 = sub.P00; + float2 P01 = sub.P01; + float2 P10 = sub.P10; + float2 P11 = sub.P11; + + if(sub.patch->is_triangle()) { + P00 = right_to_equilateral(P00); + P01 = right_to_equilateral(P01); + P10 = right_to_equilateral(P10); + P11 = right_to_equilateral(P11); + } + + int tu0 = int(max_t * len(P10 - P00)); + int tu1 = int(max_t * len(P11 - P01)); + int tv0 = int(max_t * len(P01 - P00)); + int tv1 = int(max_t * len(P11 - P10)); + + ef.tu0 = tu0 <= 1 ? 1 : min(ef.tu0, tu0); + ef.tu1 = tu1 <= 1 ? 1 : min(ef.tu1, tu1); + ef.tv0 = tv0 <= 1 ? 1 : min(ef.tv0, tv0); + ef.tv1 = tv1 <= 1 ? 1 : min(ef.tv1, tv1); +} + void DiagSplit::split(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef, int depth) { if(depth > 32) { @@ -128,82 +181,66 @@ void DiagSplit::split(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef assert(ef.tv == T(sub.patch, sub.Pw, sub.Pu)); assert(ef.tw == T(sub.patch, sub.Pu, sub.Pv)); - if(depth == 0) { - dispatch(sub, ef); - return; - } - - if(ef.tu == DSPLIT_NON_UNIFORM) { - /* partition edges */ - TriangleDice::EdgeFactors ef0, ef1; - float2 Psplit; - - partition_edge(sub.patch, - &Psplit, &ef1.tu, &ef0.tu, sub.Pv, sub.Pw, ef.tu); + int non_uniform_count = int(ef.tu == DSPLIT_NON_UNIFORM) + + int(ef.tv == DSPLIT_NON_UNIFORM) + + int(ef.tw == DSPLIT_NON_UNIFORM); - /* split */ - int tsplit = T(sub.patch, sub.Pu, Psplit); - ef0.tv = ef.tv; - ef0.tw = tsplit; - - ef1.tv = tsplit; - ef1.tw = ef.tw; - - /* create subpatches */ - TriangleDice::SubPatch sub0 = {sub.patch, sub.Pu, Psplit, sub.Pw}; - TriangleDice::SubPatch sub1 = {sub.patch, sub.Pu, sub.Pv, Psplit}; - - split(sub0, ef0, depth+1); - split(sub1, ef1, depth+1); - } - else if(ef.tv == DSPLIT_NON_UNIFORM) { - /* partition edges */ - TriangleDice::EdgeFactors ef0, ef1; - float2 Psplit; + switch(non_uniform_count) { + case 1: { + /* TODO(mai): one edge is non-uniform, split into two triangles */ + // fallthru + } + case 2: { + /* TODO(mai): two edges are non-uniform, split into triangle and quad */ + // fallthru + } + case 3: { + /* all three edges are non-uniform, split into three quads */ - partition_edge(sub.patch, - &Psplit, &ef1.tu, &ef0.tu, sub.Pw, sub.Pu, ef.tv); + /* partition edges */ + QuadDice::EdgeFactors ef0, ef1, ef2; + float2 Pu, Pv, Pw, Pcenter; - /* split */ - int tsplit = T(sub.patch, sub.Pv, Psplit); - ef0.tv = ef.tw; - ef0.tw = tsplit; + partition_edge(sub.patch, &Pu, &ef1.tv0, &ef2.tu0, sub.Pw, sub.Pv, ef.tu); + partition_edge(sub.patch, &Pv, &ef0.tv0, &ef1.tu0, sub.Pu, sub.Pw, ef.tv); + partition_edge(sub.patch, &Pw, &ef2.tv0, &ef0.tu0, sub.Pv, sub.Pu, ef.tw); + Pcenter = (Pu + Pv + Pw) * (1.0f / 3.0f); - ef1.tv = tsplit; - ef1.tw = ef.tu; + /* split */ + int tsplit01 = T(sub.patch, Pv, Pcenter); + int tsplit12 = T(sub.patch, Pu, Pcenter); + int tsplit20 = T(sub.patch, Pw, Pcenter); - /* create subpatches */ - TriangleDice::SubPatch sub0 = {sub.patch, sub.Pv, Psplit, sub.Pu}; - TriangleDice::SubPatch sub1 = {sub.patch, sub.Pv, sub.Pw, Psplit}; + ef0.tu1 = tsplit01; + ef0.tv1 = tsplit20; - split(sub0, ef0, depth+1); - split(sub1, ef1, depth+1); - } - else if(ef.tw == DSPLIT_NON_UNIFORM) { - /* partition edges */ - TriangleDice::EdgeFactors ef0, ef1; - float2 Psplit; + ef1.tu1 = tsplit12; + ef1.tv1 = tsplit01; - partition_edge(sub.patch, - &Psplit, &ef1.tu, &ef0.tu, sub.Pu, sub.Pv, ef.tw); + ef2.tu1 = tsplit20; + ef2.tv1 = tsplit12; - /* split */ - int tsplit = T(sub.patch, sub.Pw, Psplit); - ef0.tv = ef.tu; - ef0.tw = tsplit; + /* create subpatches */ + QuadDice::SubPatch sub0 = {sub.patch, sub.Pu, Pw, Pv, Pcenter}; + QuadDice::SubPatch sub1 = {sub.patch, sub.Pw, Pv, Pu, Pcenter}; + QuadDice::SubPatch sub2 = {sub.patch, sub.Pv, Pu, Pw, Pcenter}; - ef1.tv = tsplit; - ef1.tw = ef.tv; + limit_edge_factors(sub0, ef0, 1 << params.max_level); + limit_edge_factors(sub1, ef1, 1 << params.max_level); + limit_edge_factors(sub2, ef2, 1 << params.max_level); - /* create subpatches */ - TriangleDice::SubPatch sub0 = {sub.patch, sub.Pw, Psplit, sub.Pv}; - TriangleDice::SubPatch sub1 = {sub.patch, sub.Pw, sub.Pu, Psplit}; + split(sub0, ef0, depth+1); + split(sub1, ef1, depth+1); + split(sub2, ef2, depth+1); - split(sub0, ef0, depth+1); - split(sub1, ef1, depth+1); + break; + } + default: { + /* all edges uniform, no splitting needed */ + dispatch(sub, ef); + break; + } } - else - dispatch(sub, ef); } void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int depth) @@ -248,6 +285,9 @@ void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int de QuadDice::SubPatch sub0 = {sub.patch, sub.P00, Pu0, sub.P01, Pu1}; QuadDice::SubPatch sub1 = {sub.patch, Pu0, sub.P10, Pu1, sub.P11}; + limit_edge_factors(sub0, ef0, 1 << params.max_level); + limit_edge_factors(sub1, ef1, 1 << params.max_level); + split(sub0, ef0, depth+1); split(sub1, ef1, depth+1); } @@ -273,6 +313,9 @@ void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int de QuadDice::SubPatch sub0 = {sub.patch, sub.P00, sub.P10, Pv0, Pv1}; QuadDice::SubPatch sub1 = {sub.patch, Pv0, Pv1, sub.P01, sub.P11}; + limit_edge_factors(sub0, ef0, 1 << params.max_level); + limit_edge_factors(sub1, ef1, 1 << params.max_level); + split(sub0, ef0, depth+1); split(sub1, ef1, depth+1); } @@ -295,6 +338,8 @@ void DiagSplit::split_triangle(Patch *patch) ef_split.tv = T(patch, sub_split.Pw, sub_split.Pu); ef_split.tw = T(patch, sub_split.Pu, sub_split.Pv); + limit_edge_factors(sub_split, ef_split, 1 << params.max_level); + split(sub_split, ef_split); TriangleDice dice(params); @@ -303,10 +348,6 @@ void DiagSplit::split_triangle(Patch *patch) TriangleDice::SubPatch& sub = subpatches_triangle[i]; TriangleDice::EdgeFactors& ef = edgefactors_triangle[i]; - ef.tu = 4; - ef.tv = 4; - ef.tw = 4; - ef.tu = max(ef.tu, 1); ef.tv = max(ef.tv, 1); ef.tw = max(ef.tw, 1); @@ -316,6 +357,24 @@ void DiagSplit::split_triangle(Patch *patch) subpatches_triangle.clear(); edgefactors_triangle.clear(); + + /* triangle might be split into quads so dice quad subpatches as well */ + QuadDice qdice(params); + + for(size_t i = 0; i < subpatches_quad.size(); i++) { + QuadDice::SubPatch& sub = subpatches_quad[i]; + QuadDice::EdgeFactors& ef = edgefactors_quad[i]; + + ef.tu0 = max(ef.tu0, 1); + ef.tu1 = max(ef.tu1, 1); + ef.tv0 = max(ef.tv0, 1); + ef.tv1 = max(ef.tv1, 1); + + qdice.dice(sub, ef); + } + + subpatches_quad.clear(); + edgefactors_quad.clear(); } void DiagSplit::split_quad(Patch *patch) @@ -334,6 +393,8 @@ void DiagSplit::split_quad(Patch *patch) ef_split.tv0 = T(patch, sub_split.P00, sub_split.P01); ef_split.tv1 = T(patch, sub_split.P10, sub_split.P11); + limit_edge_factors(sub_split, ef_split, 1 << params.max_level); + split(sub_split, ef_split); QuadDice dice(params); diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index 15e6c88c30f..cceec8d444c 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -73,6 +73,7 @@ set(SRC_HEADERS util_string.h util_system.h util_task.h + util_texture.h util_thread.h util_time.h util_transform.h diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h new file mode 100644 index 00000000000..2b6b8e743fb --- /dev/null +++ b/intern/cycles/util/util_texture.h @@ -0,0 +1,53 @@ +/* + * Copyright 2011-2016 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UTIL_TEXTURE_H__ +#define __UTIL_TEXTURE_H__ + +CCL_NAMESPACE_BEGIN + +/* Texture limits on various devices. */ + +/* CPU */ +#define TEX_NUM_BYTE_IMAGES_CPU 1024 +#define TEX_NUM_FLOAT_IMAGES_CPU 1024 +#define TEX_IMAGE_BYTE_START_CPU TEX_NUM_FLOAT_IMAGES_CPU + +/* CUDA (Fermi) */ +#define TEX_NUM_BYTE_IMAGES_CUDA 88 +#define TEX_NUM_FLOAT_IMAGES_CUDA 5 +#define TEX_IMAGE_BYTE_START_CUDA TEX_NUM_FLOAT_IMAGES_CUDA + +/* CUDA (KEPLER and above) */ +#define TEX_NUM_BYTE_IMAGES_CUDA_KEPLER 145 +#define TEX_NUM_FLOAT_IMAGES_CUDA_KEPLER 5 +#define TEX_IMAGE_BYTE_START_CUDA_KELPER TEX_NUM_FLOAT_IMAGES_CUDA_KEPLER + +/* OpenCL */ +#define TEX_NUM_BYTE_IMAGES_OPENCL 1024 +#define TEX_NUM_FLOAT_IMAGES_OPENCL 0 +#define TEX_IMAGE_BYTE_START_OPENCL TEX_NUM_FLOAT_IMAGES_OPENCL + + +/* Color to use when textures are not found. */ +#define TEX_IMAGE_MISSING_R 1 +#define TEX_IMAGE_MISSING_G 0 +#define TEX_IMAGE_MISSING_B 1 +#define TEX_IMAGE_MISSING_A 1 + +CCL_NAMESPACE_END + +#endif /* __UTIL_TEXTURE_H__ */ diff --git a/intern/moto/include/MT_Vector3.h b/intern/moto/include/MT_Vector3.h index b06f345bdaf..545ca1fad0b 100644 --- a/intern/moto/include/MT_Vector3.h +++ b/intern/moto/include/MT_Vector3.h @@ -52,7 +52,6 @@ class MT_Vector3 : public MT_Tuple3 { public: - virtual ~MT_Vector3() {} MT_Vector3() {} MT_Vector3(const float *v) : MT_Tuple3(v) {} MT_Vector3(const double *v) : MT_Tuple3(v) {} diff --git a/intern/moto/include/MT_Vector4.h b/intern/moto/include/MT_Vector4.h index d157cefa946..440bf9b84f1 100644 --- a/intern/moto/include/MT_Vector4.h +++ b/intern/moto/include/MT_Vector4.h @@ -53,7 +53,6 @@ class MT_Vector4 : public MT_Tuple4 { public: - virtual ~MT_Vector4() {} MT_Vector4() {} MT_Vector4(const float *v) : MT_Tuple4(v) {} MT_Vector4(const double *v) : MT_Tuple4(v) {} diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py index 7ad83525656..52e7b0e0ae4 100644 --- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py +++ b/release/scripts/startup/bl_operators/uvcalc_smart_project.py @@ -23,7 +23,11 @@ import bpy from bpy.types import Operator DEG_TO_RAD = 0.017453292519943295 # pi/180.0 -SMALL_NUM = 0.00000001 # see bug [#31598] why we dont have smaller values +# see bugs: +# - T31598 (when too small). +# - T48086 (when too big). +SMALL_NUM = 1e-12 + global USER_FILL_HOLES global USER_FILL_HOLES_QUALITY @@ -813,9 +817,6 @@ def main(context, else: meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons)] - if not meshFaces: - continue - #XXX Window.DrawProgressBar(0.1, 'SmartProj UV Unwrapper, mapping "%s", %i faces.' % (me.name, len(meshFaces))) # ======= @@ -835,6 +836,9 @@ def main(context, uv.zero() meshFaces.pop() + if not meshFaces: + continue + # Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data. # Generate Projection Vecs diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 10b4e24efd9..fbea07a317e 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -113,6 +113,11 @@ class IMAGE_MT_view(Menu): layout.separator() if show_render: + layout.operator("image.render_border") + layout.operator("image.clear_render_border") + + layout.separator() + layout.operator("image.cycle_render_slot", text="Render Slot Cycle Next") layout.operator("image.cycle_render_slot", text="Render Slot Cycle Previous").reverse = True layout.separator() diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py index c083907f017..64d3b427260 100644 --- a/release/scripts/startup/bl_ui/space_nla.py +++ b/release/scripts/startup/bl_ui/space_nla.py @@ -86,6 +86,7 @@ class NLA_MT_view(Menu): layout.separator() layout.operator("nla.view_all") layout.operator("nla.view_selected") + layout.operator("nla.view_frame") layout.separator() layout.operator("screen.area_dupli") diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 387638404fd..233145819e7 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -180,6 +180,7 @@ class SEQUENCER_MT_view(Menu): layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("sequencer.view_all", text="View all Sequences") layout.operator("sequencer.view_selected") + layout.operator("sequencer.view_frame") layout.operator_context = 'INVOKE_DEFAULT' if is_preview: layout.operator_context = 'INVOKE_REGION_PREVIEW' diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index 8846ad7865a..d5bcbff67f1 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -130,7 +130,6 @@ class TIME_MT_view(Menu): layout.prop(st, "show_seconds") layout.prop(st, "show_locked_time") - layout.operator("time.view_all") layout.separator() @@ -143,6 +142,11 @@ class TIME_MT_view(Menu): layout.separator() + layout.operator("time.view_all") + layout.operator("time.view_frame") + + layout.separator() + layout.operator("marker.camera_bind") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 386a8af6b28..14d63f1e51d 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -435,6 +435,7 @@ class VIEW3D_MT_view(Menu): layout.operator("view3d.clip_border", text="Clipping Border...") layout.operator("view3d.zoom_border", text="Zoom Border...") layout.operator("view3d.render_border", text="Render Border...").camera_only = False + layout.operator("view3d.clear_render_border") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index c4c487f4438..bd7702017fc 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -597,13 +597,11 @@ class VIEW3D_PT_tools_curveedit_options_stroke(View3DPanel, Panel): col = layout.column() if cps.depth_mode == 'SURFACE': + col.prop(cps, "radius_offset") col.prop(cps, "use_stroke_endpoints") if cps.use_stroke_endpoints: colsub = layout.column(align=True) colsub.prop(cps, "surface_plane", expand=True) - else: - col.prop(cps, "radius_offset") - # ********** default tools for editmode_surface **************** diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 63cc57d0a21..cdb3d1afc29 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -4494,13 +4494,17 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *width, int *height) ImBuf *ibuf = NULL; void *lock; - ibuf = BKE_image_acquire_ibuf(image, iuser, &lock); + if (image != NULL) { + ibuf = BKE_image_acquire_ibuf(image, iuser, &lock); + } if (ibuf && ibuf->x > 0 && ibuf->y > 0) { *width = ibuf->x; *height = ibuf->y; } - else if (image->type == IMA_TYPE_R_RESULT && iuser != NULL && iuser->scene != NULL) { + else if (image != NULL && image->type == IMA_TYPE_R_RESULT && + iuser != NULL && iuser->scene != NULL) + { Scene *scene = iuser->scene; *width = (scene->r.xsch * scene->r.size) / 100; *height = (scene->r.ysch * scene->r.size) / 100; @@ -4514,7 +4518,9 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *width, int *height) *height = IMG_SIZE_FALLBACK; } - BKE_image_release_ibuf(image, ibuf, lock); + if (image != NULL) { + BKE_image_release_ibuf(image, ibuf, lock); + } } void BKE_image_get_size_fl(Image *image, ImageUser *iuser, float size[2]) diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index c51446d6cc8..c44fcf47fdb 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -272,6 +272,7 @@ float angle_normalized_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT; float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT; float cos_v3v3v3(const float p1[3], const float p2[3], const float p3[3]) ATTR_WARN_UNUSED_RESULT; +float cos_v2v2v2(const float p1[2], const float p2[2], const float p3[2]) ATTR_WARN_UNUSED_RESULT; float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT; float angle_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT; float angle_signed_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 8d33e04241a..72a3da265c7 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -397,6 +397,19 @@ float angle_v2v2v2(const float v1[2], const float v2[2], const float v3[2]) return angle_normalized_v2v2(vec1, vec2); } +/* Quicker than full angle computation */ +float cos_v2v2v2(const float p1[2], const float p2[2], const float p3[2]) +{ + float vec1[2], vec2[2]; + + sub_v2_v2v2(vec1, p2, p1); + sub_v2_v2v2(vec2, p2, p3); + normalize_v2(vec1); + normalize_v2(vec2); + + return dot_v2v2(vec1, vec2); +} + /* Return the shortest angle in radians between the 2 vectors */ float angle_v2v2(const float v1[2], const float v2[2]) { diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c index 8a96daeeb91..e913499ba2b 100644 --- a/source/blender/blenlib/intern/scanfill.c +++ b/source/blender/blenlib/intern/scanfill.c @@ -602,7 +602,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl else { /* test rest of vertices */ ScanFillVertLink *best_sc = NULL; - float best_angle = 3.14f; + float angle_best_cos = -1.0f; float miny; bool firsttime = false; @@ -633,21 +633,18 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl best_sc = sc1; } else { - float angle; - /* prevent angle calc for the simple cases only 1 vertex is found */ if (firsttime == false) { - best_angle = angle_v2v2v2(v2->xy, v1->xy, best_sc->vert->xy); + angle_best_cos = cos_v2v2v2(v2->xy, v1->xy, best_sc->vert->xy); firsttime = true; } - angle = angle_v2v2v2(v2->xy, v1->xy, sc1->vert->xy); - if (angle < best_angle) { + const float angle_test_cos = cos_v2v2v2(v2->xy, v1->xy, sc1->vert->xy); + if (angle_test_cos > angle_best_cos) { best_sc = sc1; - best_angle = angle; + angle_best_cos = angle_test_cos; } } - } } } diff --git a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c index a1460cec7d1..5a7a2f3ee29 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c +++ b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c @@ -37,6 +37,9 @@ #include "bmesh.h" #include "bmesh_decimate.h" /* own include */ +/* check that collapsing a vertex between 2 edges doesn't cause a degenerate face. */ +#define USE_DEGENERATE_CHECK + #define COST_INVALID FLT_MAX @@ -88,9 +91,6 @@ static float bm_edge_calc_dissolve_error( const BMEdge *e, const BMO_Delimit delimit, const struct DelimitData *delimit_data) { - const bool is_contig = BM_edge_is_contiguous(e); - float angle; - if (!BM_edge_is_manifold(e)) { goto fail; } @@ -113,6 +113,8 @@ static float bm_edge_calc_dissolve_error( goto fail; } + const bool is_contig = BM_edge_is_contiguous(e); + if ((delimit & BMO_DELIM_NORMAL) && (is_contig == false)) { @@ -125,18 +127,130 @@ static float bm_edge_calc_dissolve_error( goto fail; } - angle = BM_edge_calc_face_angle(e); - if (is_contig == false) { - angle = (float)M_PI - angle; + float angle_cos_neg = dot_v3v3(e->l->f->no, e->l->radial_next->f->no); + if (is_contig) { + angle_cos_neg *= -1; } - return angle; + return angle_cos_neg; fail: return COST_INVALID; } +#ifdef USE_DEGENERATE_CHECK + +static void mul_v2_m3v3_center(float r[2], float m[3][3], const float a[3], const float center[3]) +{ + BLI_assert(r != a); + BLI_assert(r != center); + + float co[3]; + sub_v3_v3v3(co, a, center); + + r[0] = m[0][0] * co[0] + m[1][0] * co[1] + m[2][0] * co[2]; + r[1] = m[0][1] * co[0] + m[1][1] * co[1] + m[2][1] * co[2]; +} + +static bool bm_loop_collapse_is_degenerate(BMLoop *l_ear) +{ + /* calculate relative to the centeral vertex for higher precision */ + const float *center = l_ear->v->co; + + float tri_2d[3][2]; + float axis_mat[3][3]; + + axis_dominant_v3_to_m3(axis_mat, l_ear->f->no); + + { + mul_v2_m3v3_center(tri_2d[0], axis_mat, l_ear->prev->v->co, center); +#if 0 + mul_v2_m3v3_center(tri_2d[1], axis_mat, l_ear->v->co, center); +#else + zero_v2(tri_2d[1]); +#endif + mul_v2_m3v3_center(tri_2d[2], axis_mat, l_ear->next->v->co, center); + } + + /* check we're not flipping face corners before or after the ear */ + { + float adjacent_2d[2]; + + if (!BM_vert_is_edge_pair(l_ear->prev->v)) { + mul_v2_m3v3_center(adjacent_2d, axis_mat, l_ear->prev->prev->v->co, center); + if (signum_i(cross_tri_v2(adjacent_2d, tri_2d[0], tri_2d[1])) != + signum_i(cross_tri_v2(adjacent_2d, tri_2d[0], tri_2d[2]))) + { + return true; + } + } + + if (!BM_vert_is_edge_pair(l_ear->next->v)) { + mul_v2_m3v3_center(adjacent_2d, axis_mat, l_ear->next->next->v->co, center); + if (signum_i(cross_tri_v2(adjacent_2d, tri_2d[2], tri_2d[1])) != + signum_i(cross_tri_v2(adjacent_2d, tri_2d[2], tri_2d[0]))) + { + return true; + } + } + } + + /* check no existing verts are inside the triangle */ + { + /* triangle may be concave, if so - flip so we can use clockwise check */ + if (cross_tri_v2(UNPACK3(tri_2d)) < 0.0f) { + swap_v2_v2(tri_2d[1], tri_2d[2]); + } + + /* skip l_ear and adjacent verts */ + BMLoop *l_iter, *l_first; + + l_iter = l_ear->next->next; + l_first = l_ear->prev; + do { + float co_2d[2]; + mul_v2_m3v3_center(co_2d, axis_mat, l_iter->v->co, center); + if (isect_point_tri_v2_cw(co_2d, tri_2d[0], tri_2d[1], tri_2d[2])) { + return true; + } + } while ((l_iter = l_iter->next) != l_first); + } + + return false; +} + +static bool bm_vert_collapse_is_degenerate(BMVert *v) +{ + BMEdge *e_pair[2]; + BMVert *v_pair[2]; + + if (BM_vert_edge_pair(v, &e_pair[0], &e_pair[1])) { + v_pair[0] = BM_edge_other_vert(e_pair[0], v); + v_pair[1] = BM_edge_other_vert(e_pair[1], v); + + if (fabsf(cos_v3v3v3(v_pair[0]->co, v->co, v_pair[1]->co)) < (1.0f - FLT_EPSILON)) { + BMLoop *l_iter, *l_first; + l_iter = l_first = e_pair[1]->l; + do { + if (l_iter->f->len > 3) { + BMLoop *l_pivot = (l_iter->v == v ? l_iter : l_iter->next); + BLI_assert(v == l_pivot->v); + if (bm_loop_collapse_is_degenerate(l_pivot)) { + return true; + } + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + return false; + } + else { + return true; + } +} +#endif /* USE_DEGENERATE_CHECK */ + + void BM_mesh_decimate_dissolve_ex( BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, BMO_Delimit delimit, @@ -144,6 +258,7 @@ void BM_mesh_decimate_dissolve_ex( BMEdge **einput_arr, const int einput_len, const short oflag_out) { + const float angle_limit_cos_neg = -cosf(angle_limit); struct DelimitData delimit_data = {0}; const int eheap_table_len = do_dissolve_boundaries ? einput_len : max_ii(einput_len, vinput_len); void *_heap_table = MEM_mallocN(sizeof(HeapNode *) * eheap_table_len, __func__); @@ -193,7 +308,7 @@ void BM_mesh_decimate_dissolve_ex( } while ((BLI_heap_is_empty(eheap) == false) && - (BLI_heap_node_value((enode_top = BLI_heap_top(eheap))) < angle_limit)) + (BLI_heap_node_value((enode_top = BLI_heap_top(eheap))) < angle_limit_cos_neg)) { BMFace *f_new = NULL; BMEdge *e; @@ -328,7 +443,14 @@ void BM_mesh_decimate_dissolve_ex( v = BLI_heap_node_ptr(vnode_top); i = BM_elem_index_get(v); - if (BM_vert_is_edge_pair(v)) { + if ( +#ifdef USE_DEGENERATE_CHECK + !bm_vert_collapse_is_degenerate(v) +#else + BM_vert_is_edge_pair(v) +#endif + ) + { e_new = BM_vert_collapse_edge(bm, v->e, v, true, true); /* join edges */ if (e_new) { @@ -343,7 +465,6 @@ void BM_mesh_decimate_dissolve_ex( do { BM_face_normal_update(l_iter->f); } while ((l_iter = l_iter->radial_next) != l_first); - } /* re-calculate costs */ @@ -355,6 +476,33 @@ void BM_mesh_decimate_dissolve_ex( vheap_table[j] = BLI_heap_insert(vheap, cost, v_iter); } } + +#ifdef USE_DEGENERATE_CHECK + /* dissolving a vertex may mean vertices we previously weren't able to dissolve + * can now be re-evaluated. */ + if (e_new->l) { + BMLoop *l_first, *l_iter; + l_iter = l_first = e_new->l; + do { + /* skip vertices part of this edge, evaluated above */ + BMLoop *l_cycle_first, *l_cycle_iter; + l_cycle_iter = l_iter->next->next; + l_cycle_first = l_iter->prev; + do { + const int j = BM_elem_index_get(l_cycle_iter->v); + if (j != -1 && vheap_table[j] && + (BLI_heap_node_value(vheap_table[j]) == COST_INVALID)) + { + const float cost = bm_vert_edge_face_angle(l_cycle_iter->v); + BLI_heap_remove(vheap, vheap_table[j]); + vheap_table[j] = BLI_heap_insert(vheap, cost, l_cycle_iter->v); + } + } while ((l_cycle_iter = l_cycle_iter->next) != l_cycle_first); + + } while ((l_iter = l_iter->radial_next) != l_first); + } +#endif /* USE_DEGENERATE_CHECK */ + } } diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index c7c945b492b..f92967ef5ff 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -748,7 +748,8 @@ EnumPropertyItem prop_driver_create_mapping_types[] = { "Drive all components of this property using the target picked"}, {CREATEDRIVER_MAPPING_1_1, "DIRECT", 0, "Single from Target", "Drive this component of this property using the target picked"}, - {CREATEDRIVER_MAPPING_N_N, "MATCH", 0, "Match Indices", + + {CREATEDRIVER_MAPPING_N_N, "MATCH", ICON_COLOR, "Match Indices", "Create drivers for each pair of corresponding elements"}, {CREATEDRIVER_MAPPING_NONE_ALL, "NONE_ALL", ICON_HAND, "Manually Create Later", diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 4af123c0f51..5c74a3e43c2 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -185,6 +185,10 @@ struct CurveDrawData { /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but thats OK. */ bool use_depth; + + /* offset projection by this value */ + bool use_offset; + float offset[3]; /* worldspace */ } project; /* cursor sampling */ @@ -287,6 +291,12 @@ static bool stroke_elem_project( } } + if (is_location_world_set) { + if (cdd->project.use_offset) { + add_v3_v3(r_location_world, cdd->project.offset); + } + } + return is_location_world_set; } @@ -582,6 +592,26 @@ static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event) normalize_v3_v3(cdd->project.plane, normal); cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, cdd->prev.location_world_valid); + + /* Special case for when we only have offset applied on the first-hit, + * the remaining stroke must be offset too. */ + if (cdd->radius.offset != 0.0f) { + const float mval_fl[2] = {UNPACK2(event->mval)}; + + float location_no_offset[3]; + + if (stroke_elem_project( + cdd, event->mval, mval_fl, 0.0f, 0.0f, + location_no_offset)) + { + sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset); + if (!is_zero_v3(cdd->project.offset)) { + cdd->project.use_offset = true; + } + } + } + /* end special case */ + } cdd->init_event_type = event->type; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 64cb0b5bb6a..f54da91af71 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -569,6 +569,10 @@ bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure) /* add the point itself */ madd_v3_v3fl(sco, &pt->x, average_fac); + if (affect_pressure) { + pressure += pt->pressure * average_fac; + } + /* n-steps before/after current point */ // XXX: review how the endpoints are treated by this algorithm // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 85370de0013..6a558d1c185 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -67,6 +67,7 @@ void ED_region_panels( const bool vertical); void ED_region_header_init(struct ARegion *ar); void ED_region_header(const struct bContext *C, struct ARegion *ar); +void ED_region_cursor_set(struct wmWindow *win, struct ScrArea *sa, struct ARegion *ar); void ED_region_toggle_hidden(struct bContext *C, struct ARegion *ar); void ED_region_info_draw(struct ARegion *ar, const char *text, float fill_color[4], const bool full_redraw); void ED_region_image_metadata_draw(int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index d59d29363e6..6c961179c6f 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -6537,7 +6537,6 @@ static void menu_add_shortcut_cancel(struct bContext *C, void *arg1) static void popup_change_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) { uiBut *but = (uiBut *)arg1; - UI_but_tooltip_timer_remove(C, but); UI_popup_block_invoke(C, menu_change_shortcut, but); } @@ -6559,7 +6558,6 @@ static void remove_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) { uiBut *but = (uiBut *)arg1; - UI_but_tooltip_timer_remove(C, but); UI_popup_block_ex(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but); } @@ -6608,8 +6606,6 @@ static bool ui_but_menu(bContext *C, uiBut *but) return false; } - UI_but_tooltip_timer_remove(C, but); - /* highly unlikely getting the label ever fails */ UI_but_string_info_get(C, but, &label, NULL); diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index abd395afbe0..b3972bebd96 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -1788,10 +1788,20 @@ void ui_popup_block_scrolltest(uiBlock *block) static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle) { - ui_region_temp_remove(C, CTX_wm_screen(C), handle->region); + wmWindow *win = CTX_wm_window(C); + bScreen *sc = CTX_wm_screen(C); + + ui_region_temp_remove(C, sc, handle->region); + + /* reset to region cursor (only if there's not another menu open) */ + if (BLI_listbase_is_empty(&sc->regionbase)) { + ED_region_cursor_set(win, CTX_wm_area(C), CTX_wm_region(C)); + /* in case cursor needs to be changed again */ + WM_event_add_mousemove(C); + } if (handle->scrolltimer) - WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer); + WM_event_remove_timer(CTX_wm_manager(C), win, handle->scrolltimer); } /** @@ -1965,11 +1975,19 @@ uiPopupBlockHandle *ui_popup_block_create( void *arg) { wmWindow *window = CTX_wm_window(C); + uiBut *activebut = UI_context_active_but_get(C); static ARegionType type; ARegion *ar; uiBlock *block; uiPopupBlockHandle *handle; + /* disable tooltips from buttons below */ + if (activebut) { + UI_but_tooltip_timer_remove(C, activebut); + } + /* standard cursor by default */ + WM_cursor_set(window, CURSOR_STD); + /* create handle */ handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle"); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 841b76c676b..d1461f1acec 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -3042,6 +3042,15 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat wcol->shaded = 0; wcol->alpha_check = (wcol->inner[3] < 255); + + if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + /* Now we reduce alpha of the inner color (i.e. the color shown) + * so that this setting can look greyed out, while retaining + * the checkboard (for transparent values). This is needed + * here as the effects of ui_widget_color_disabled() are overwritten. + */ + wcol->inner[3] /= 2; + } widgetbase_draw(&wtb, wcol); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 4ede89e1620..3511480821a 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1507,6 +1507,16 @@ void ED_region_init(bContext *C, ARegion *ar) region_update_rect(ar); } +void ED_region_cursor_set(wmWindow *win, ScrArea *sa, ARegion *ar) +{ + if (ar && sa && ar->type && ar->type->cursor) { + ar->type->cursor(win, sa, ar); + } + else { + WM_cursor_set(win, CURSOR_STD); + } +} + /* for quick toggle, can skip fades */ void region_toggle_hidden(bContext *C, ARegion *ar, const bool do_fade) { diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index cb07ce756a6..23c6aa37a83 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1064,17 +1064,11 @@ bScreen *ED_screen_duplicate(wmWindow *win, bScreen *sc) /* screen sets cursor based on swinid */ static void region_cursor_set(wmWindow *win, int swinid, int swin_changed) { - ScrArea *sa = win->screen->areabase.first; - - for (; sa; sa = sa->next) { - ARegion *ar = sa->regionbase.first; - for (; ar; ar = ar->next) { + for (ScrArea *sa = win->screen->areabase.first; sa; sa = sa->next) { + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->swinid == swinid) { if (swin_changed || (ar->type && ar->type->event_cursor)) { - if (ar->type && ar->type->cursor) - ar->type->cursor(win, sa, ar); - else - WM_cursor_set(win, CURSOR_STD); + ED_region_cursor_set(win, sa, ar); } return; } @@ -1146,7 +1140,7 @@ void ED_screen_draw(wmWindow *win) /* blended join arrow */ if (sa1 && sa2) { int dir = area_getorientation(sa1, sa2); - int dira; + int dira = -1; if (dir != -1) { switch (dir) { case 0: /* W */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 9cc138b2cc4..b69547b0506 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -431,13 +431,7 @@ static int actkeys_viewsel_exec(bContext *C, wmOperator *UNUSED(op)) return actkeys_viewall(C, true); } -static int actkeys_view_frame_exec(bContext *C, wmOperator *op) -{ - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - ANIM_center_frame(C, smooth_viewtx); - - return OPERATOR_FINISHED; -} +/* ......... */ void ACTION_OT_view_all(wmOperatorType *ot) { @@ -469,17 +463,27 @@ void ACTION_OT_view_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ****************** View-All Operator ****************** */ + +static int actkeys_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + + return OPERATOR_FINISHED; +} + void ACTION_OT_view_frame(wmOperatorType *ot) { /* identifiers */ ot->name = "View Frame"; ot->idname = "ACTION_OT_view_frame"; ot->description = "Reset viewable area to show range around current frame"; - + /* api callbacks */ ot->exec = actkeys_view_frame_exec; - ot->poll = ED_operator_action_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */ - + ot->poll = ED_operator_action_active; + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 5846a439b3c..a9ab1502e16 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -607,6 +607,7 @@ static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVa } /* settings for 'rotation difference' driver variable type */ +/* FIXME: 1) Must be same armature for both dtars, 2) Alignment issues... */ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *dvar) { DriverTarget *dtar = &dvar->targets[0]; @@ -623,7 +624,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar * /* Bone 1 */ col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Bone 1:")); + uiItemR(col, &dtar_ptr, "id", 0, IFACE_("Bone 1"), ICON_NONE); if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) { PointerRNA tar_ptr; @@ -634,7 +635,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar * col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Bone 2:")); + uiItemR(col, &dtar2_ptr, "id", 0, IFACE_("Bone 2"), ICON_NONE); if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) { PointerRNA tar_ptr; @@ -661,13 +662,13 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar * /* Bone 1 */ col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone 1:")); - + uiItemR(col, &dtar_ptr, "id", 0, IFACE_("Object 1"), ICON_NONE); + if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr); - uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); + uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA); } uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */ @@ -675,13 +676,13 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar * col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Ob/Bone 2:")); - + uiItemR(col, &dtar2_ptr, "id", 0, IFACE_("Object 2"), ICON_NONE); + if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr); - uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); + uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA); } uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */ @@ -702,13 +703,13 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar /* properties */ col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone:")); - + uiItemR(col, &dtar_ptr, "id", 0, IFACE_("Object"), ICON_NONE); + if (dtar->id && GS(dtar->id->name) == ID_OB && ob->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar->id, &RNA_Pose, ob->pose, &tar_ptr); - uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); + uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA); } sub = uiLayoutColumn(layout, true); @@ -850,21 +851,34 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) for (dvar = driver->variables.first; dvar; dvar = dvar->next) { PointerRNA dvar_ptr; uiLayout *box, *row; + uiLayout *subrow, *sub; /* sub-layout column for this variable's settings */ col = uiLayoutColumn(pa->layout, true); - /* header panel */ + /* 1) header panel */ box = uiLayoutBox(col); - /* first row context info for driver */ RNA_pointer_create(ale->id, &RNA_DriverVariable, dvar, &dvar_ptr); row = uiLayoutRow(box, false); block = uiLayoutGetBlock(row); - /* variable name */ - uiItemR(row, &dvar_ptr, "name", 0, "", ICON_NONE); - /* invalid name? */ + /* 1.1) variable type and name */ + subrow = uiLayoutRow(row, true); + + /* 1.1.1) variable type */ + sub = uiLayoutRow(subrow, true); /* HACK: special group just for the enum, otherwise we */ + uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT); /* we get ugly layout with text included too... */ + + uiItemR(sub, &dvar_ptr, "type", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); + + /* 1.1.2) variable name */ + sub = uiLayoutRow(subrow, true); /* HACK: special group to counteract the effects of the previous */ + uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_EXPAND); /* enum, which now pushes everything too far right */ + + uiItemR(sub, &dvar_ptr, "name", 0, "", ICON_NONE); + + /* 1.2) invalid name? */ UI_block_emboss_set(block, UI_EMBOSS_NONE); if (dvar->flag & DVAR_FLAG_INVALID_NAME) { @@ -873,17 +887,14 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) UI_but_func_set(but, driver_dvar_invalid_name_query_cb, dvar, NULL); // XXX: reports? } - /* remove button */ + /* 1.3) remove button */ but = uiDefIconBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_X, 290, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0.0, 0.0, IFACE_("Delete target variable")); UI_but_func_set(but, driver_delete_var_cb, driver, dvar); UI_block_emboss_set(block, UI_EMBOSS); - /* variable type */ - row = uiLayoutRow(box, false); - uiItemR(row, &dvar_ptr, "type", 0, "", ICON_NONE); - - /* variable type settings */ + + /* 2) variable type settings */ box = uiLayoutBox(col); /* controls to draw depends on the type of variable */ switch (dvar->type) { @@ -901,7 +912,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) break; } - /* value of variable */ + /* 3) value of variable */ if (driver->flag & DRIVER_FLAG_SHOWDEBUG) { char valBuf[32]; diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 19325962adc..e1cd1da3a25 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -265,13 +265,7 @@ static int graphkeys_view_selected_exec(bContext *C, wmOperator *op) return graphkeys_viewall(C, true, include_handles, smooth_viewtx); } -static int graphkeys_view_frame_exec(bContext *C, wmOperator *op) -{ - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - ANIM_center_frame(C, smooth_viewtx); - return OPERATOR_FINISHED; -} - +/* ......... */ void GRAPH_OT_view_all(wmOperatorType *ot) { @@ -311,17 +305,26 @@ void GRAPH_OT_view_selected(wmOperatorType *ot) "Include handles of keyframes when calculating extents"); } +/* ********************** View Frame Operator ****************************** */ + +static int graphkeys_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + return OPERATOR_FINISHED; +} + void GRAPH_OT_view_frame(wmOperatorType *ot) { /* identifiers */ ot->name = "View Frame"; ot->idname = "GRAPH_OT_view_frame"; ot->description = "Reset viewable area to show range around current frame"; - + /* api callbacks */ ot->exec = graphkeys_view_frame_exec; - ot->poll = ED_operator_graphedit_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */ - + ot->poll = ED_operator_graphedit_active; + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index eb74922a256..80dbfa140f6 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -3554,7 +3554,7 @@ static int clear_render_border_exec(bContext *C, wmOperator *UNUSED(op)) void IMAGE_OT_clear_render_border(wmOperatorType *ot) { /* identifiers */ - ot->name = "Render Border"; + ot->name = "Clear Render Border"; ot->description = "Clear the boundaries of the border render and disable border render"; ot->idname = "IMAGE_OT_clear_render_border"; diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 02814e385c0..baf87f3fee5 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -542,6 +542,30 @@ void NLA_OT_view_selected(wmOperatorType *ot) } /* *********************************************** */ + +static int nlaedit_viewframe_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + return OPERATOR_FINISHED; +} + +void NLA_OT_view_frame(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "View Frame"; + ot->idname = "NLA_OT_view_frame"; + ot->description = "Reset viewable area to show range around current frame"; + + /* api callbacks */ + ot->exec = nlaedit_viewframe_exec; + ot->poll = ED_operator_nla_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* *********************************************** */ /* NLA Editing Operations (Constructive/Destructive) */ /* ******************** Add Action-Clip Operator ***************************** */ diff --git a/source/blender/editors/space_nla/nla_intern.h b/source/blender/editors/space_nla/nla_intern.h index 344580c0d15..806fbe90ff2 100644 --- a/source/blender/editors/space_nla/nla_intern.h +++ b/source/blender/editors/space_nla/nla_intern.h @@ -94,6 +94,7 @@ void NLA_OT_previewrange_set(wmOperatorType *ot); void NLA_OT_view_all(wmOperatorType *ot); void NLA_OT_view_selected(wmOperatorType *ot); +void NLA_OT_view_frame(wmOperatorType *ot); void NLA_OT_actionclip_add(wmOperatorType *ot); void NLA_OT_transition_add(wmOperatorType *ot); diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index 98da10470f8..386950ead3a 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -130,6 +130,7 @@ void nla_operatortypes(void) /* view */ WM_operatortype_append(NLA_OT_view_all); WM_operatortype_append(NLA_OT_view_selected); + WM_operatortype_append(NLA_OT_view_frame); WM_operatortype_append(NLA_OT_previewrange_set); @@ -243,6 +244,7 @@ static void nla_keymap_main(wmKeyConfig *keyconf, wmKeyMap *keymap) WM_keymap_add_item(keymap, "NLA_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "NLA_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "NLA_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "NLA_OT_view_frame", PAD0, KM_PRESS, 0, 0); /* editing ------------------------------------------------ */ diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index df3e508ae0e..e3cdedf042b 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -61,6 +61,7 @@ /* for menu/popup icons etc etc*/ +#include "ED_anim_api.h" #include "ED_numinput.h" #include "ED_screen.h" #include "ED_transform.h" @@ -2697,6 +2698,29 @@ void SEQUENCER_OT_view_all(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER; } +static int sequencer_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_view_frame(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "View Frame"; + ot->idname = "SEQUENCER_OT_view_frame"; + ot->description = "Reset viewable area to show range around current frame"; + + /* api callbacks */ + ot->exec = sequencer_view_frame_exec; + ot->poll = ED_operator_sequencer_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* view_all operator */ static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op)) { diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 3e228fd0b31..730cc117287 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -121,6 +121,7 @@ void SEQUENCER_OT_rendersize(struct wmOperatorType *ot); void SEQUENCER_OT_view_toggle(struct wmOperatorType *ot); void SEQUENCER_OT_view_all(struct wmOperatorType *ot); void SEQUENCER_OT_view_selected(struct wmOperatorType *ot); +void SEQUENCER_OT_view_frame(struct wmOperatorType *ot); void SEQUENCER_OT_view_zoom_ratio(struct wmOperatorType *ot); void SEQUENCER_OT_view_ghost_border(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 3d08e0c5ed8..655e029cfdd 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -84,6 +84,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_view_all); WM_operatortype_append(SEQUENCER_OT_view_selected); + WM_operatortype_append(SEQUENCER_OT_view_frame); WM_operatortype_append(SEQUENCER_OT_view_all_preview); WM_operatortype_append(SEQUENCER_OT_view_toggle); WM_operatortype_append(SEQUENCER_OT_view_zoom_ratio); @@ -202,6 +203,7 @@ void sequencer_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "SEQUENCER_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "SEQUENCER_OT_view_frame", PAD0, KM_PRESS, 0, 0); kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_strip_jump", PAGEUPKEY, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "next", true); diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 87270bace9a..0a6a9a81e63 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -79,7 +79,15 @@ static SpaceLink *text_new(const bContext *UNUSED(C)) BLI_addtail(&stext->regionbase, ar); ar->regiontype = RGN_TYPE_HEADER; ar->alignment = RGN_ALIGN_BOTTOM; - + + /* properties region */ + ar = MEM_callocN(sizeof(ARegion), "properties region for text"); + + BLI_addtail(&stext->regionbase, ar); + ar->regiontype = RGN_TYPE_UI; + ar->alignment = RGN_ALIGN_LEFT; + ar->flag = RGN_FLAG_HIDDEN; + /* main region */ ar = MEM_callocN(sizeof(ARegion), "main region for text"); diff --git a/source/blender/editors/space_time/time_ops.c b/source/blender/editors/space_time/time_ops.c index e2e861fda38..a7f549b65ae 100644 --- a/source/blender/editors/space_time/time_ops.c +++ b/source/blender/editors/space_time/time_ops.c @@ -39,6 +39,7 @@ #include "BKE_context.h" +#include "ED_anim_api.h" #include "ED_screen.h" #include "WM_api.h" @@ -176,6 +177,31 @@ static void TIME_OT_view_all(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ************************ View Frame Operator *******************************/ + +static int time_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + + return OPERATOR_FINISHED; +} + +static void TIME_OT_view_frame(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "View Frame"; + ot->idname = "TIME_OT_view_frame"; + ot->description = "Reset viewable area to show range around current frame"; + + /* api callbacks */ + ot->exec = time_view_frame_exec; + ot->poll = ED_operator_timeline_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ************************** registration **********************************/ void time_operatortypes(void) @@ -183,6 +209,7 @@ void time_operatortypes(void) WM_operatortype_append(TIME_OT_start_frame_set); WM_operatortype_append(TIME_OT_end_frame_set); WM_operatortype_append(TIME_OT_view_all); + WM_operatortype_append(TIME_OT_view_frame); } void time_keymap(wmKeyConfig *keyconf) @@ -193,5 +220,6 @@ void time_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "TIME_OT_end_frame_set", EKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "TIME_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "TIME_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "TIME_OT_view_frame", PAD0, KM_PRESS, 0, 0); } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index e4b67e29935..0aa62c4467e 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -3676,29 +3676,10 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, { RegionView3D *rv3d = ar->regiondata; Mesh *me = ob->data; - BMFace *efa_act = BM_mesh_active_face_get(em->bm, false, true); /* annoying but active faces is stored differently */ - BMEdge *eed_act = NULL; - BMVert *eve_act = NULL; - bool use_occlude_wire = (v3d->flag2 & V3D_OCCLUDE_WIRE) && (dt > OB_WIRE); + const bool use_occlude_wire = (dt > OB_WIRE) && (v3d->flag2 & V3D_OCCLUDE_WIRE); + bool use_depth_offset = false; glLineWidth(1); - - if (em->bm->selected.last) { - BMEditSelection *ese = em->bm->selected.last; - /* face is handled above */ -#if 0 - if (ese->type == BM_FACE) { - efa_act = (BMFace *)ese->data; - } - else -#endif - if (ese->htype == BM_EDGE) { - eed_act = (BMEdge *)ese->ele; - } - else if (ese->htype == BM_VERT) { - eve_act = (BMVert *)ese->ele; - } - } BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE | BM_FACE); @@ -3708,6 +3689,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, ED_view3d_polygon_offset(rv3d, 1.0); glDepthMask(0); + use_depth_offset = true; } else { glEnable(GL_DEPTH_TEST); @@ -3754,6 +3736,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, ED_view3d_polygon_offset(rv3d, 1.0); glDepthMask(0); + use_depth_offset = true; } else { if (cageDM != finalDM) { @@ -3762,145 +3745,172 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, } } - if ((me->drawflag & ME_DRAWFACES) && (use_occlude_wire == false)) { /* transp faces */ - unsigned char col1[4], col2[4], col3[4]; + if ((dt > OB_WIRE) && (v3d->flag2 & V3D_RENDER_SHADOW)) { + /* pass */ + } + else { + /* annoying but active faces is stored differently */ + BMFace *efa_act = BM_mesh_active_face_get(em->bm, false, true); + BMEdge *eed_act = NULL; + BMVert *eve_act = NULL; + + if (em->bm->selected.last) { + BMEditSelection *ese = em->bm->selected.last; + /* face is handled above */ +#if 0 + if (ese->type == BM_FACE) { + efa_act = (BMFace *)ese->data; + } + else +#endif + if (ese->htype == BM_EDGE) { + eed_act = (BMEdge *)ese->ele; + } + else if (ese->htype == BM_VERT) { + eve_act = (BMVert *)ese->ele; + } + } + + if ((me->drawflag & ME_DRAWFACES) && (use_occlude_wire == false)) { /* transp faces */ + unsigned char col1[4], col2[4], col3[4]; #ifdef WITH_FREESTYLE - unsigned char col4[4]; + unsigned char col4[4]; #endif - UI_GetThemeColor4ubv(TH_FACE, col1); - UI_GetThemeColor4ubv(TH_FACE_SELECT, col2); - UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3); + UI_GetThemeColor4ubv(TH_FACE, col1); + UI_GetThemeColor4ubv(TH_FACE_SELECT, col2); + UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3); #ifdef WITH_FREESTYLE - UI_GetThemeColor4ubv(TH_FREESTYLE_FACE_MARK, col4); + UI_GetThemeColor4ubv(TH_FREESTYLE_FACE_MARK, col4); #endif - glEnable(GL_BLEND); - glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ - /* don't draw unselected faces, only selected, this is MUCH nicer when texturing */ - if (check_object_draw_texture(scene, v3d, dt)) - col1[3] = 0; + /* don't draw unselected faces, only selected, this is MUCH nicer when texturing */ + if (check_object_draw_texture(scene, v3d, dt)) + col1[3] = 0; #ifdef WITH_FREESTYLE - if (!(me->drawflag & ME_DRAW_FREESTYLE_FACE) || !CustomData_has_layer(&em->bm->pdata, CD_FREESTYLE_FACE)) - col4[3] = 0; + if (!(me->drawflag & ME_DRAW_FREESTYLE_FACE) || !CustomData_has_layer(&em->bm->pdata, CD_FREESTYLE_FACE)) + col4[3] = 0; - draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act); + draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act); #else - draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act); + draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act); #endif - glDisable(GL_BLEND); - glDepthMask(1); /* restore write in zbuffer */ - } - else if (efa_act) { - /* even if draw faces is off it would be nice to draw the stipple face - * Make all other faces zero alpha except for the active */ - unsigned char col1[4], col2[4], col3[4]; + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } + else if (efa_act) { + /* even if draw faces is off it would be nice to draw the stipple face + * Make all other faces zero alpha except for the active */ + unsigned char col1[4], col2[4], col3[4]; #ifdef WITH_FREESTYLE - unsigned char col4[4]; - col4[3] = 0; /* don't draw */ + unsigned char col4[4]; + col4[3] = 0; /* don't draw */ #endif - col1[3] = col2[3] = 0; /* don't draw */ + col1[3] = col2[3] = 0; /* don't draw */ - UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3); + UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3); - glEnable(GL_BLEND); - glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ #ifdef WITH_FREESTYLE - draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act); + draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act); #else - draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act); + draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act); #endif - glDisable(GL_BLEND); - glDepthMask(1); /* restore write in zbuffer */ - } + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } - /* here starts all fancy draw-extra over */ - if ((me->drawflag & ME_DRAWEDGES) == 0 && check_object_draw_texture(scene, v3d, dt)) { - /* we are drawing textures and 'ME_DRAWEDGES' is disabled, don't draw any edges */ - - /* only draw selected edges otherwise there is no way of telling if a face is selected */ - draw_em_fancy_edges(em, scene, v3d, me, cageDM, 1, eed_act); - - } - else { - if (me->drawflag & ME_DRAWSEAMS) { - UI_ThemeColor(TH_EDGE_SEAM); - glLineWidth(2); + /* here starts all fancy draw-extra over */ + if ((me->drawflag & ME_DRAWEDGES) == 0 && check_object_draw_texture(scene, v3d, dt)) { + /* we are drawing textures and 'ME_DRAWEDGES' is disabled, don't draw any edges */ - draw_dm_edges_seams(em, cageDM); + /* only draw selected edges otherwise there is no way of telling if a face is selected */ + draw_em_fancy_edges(em, scene, v3d, me, cageDM, 1, eed_act); - glColor3ub(0, 0, 0); } - - if (me->drawflag & ME_DRAWSHARP) { - UI_ThemeColor(TH_EDGE_SHARP); - glLineWidth(2); + else { + if (me->drawflag & ME_DRAWSEAMS) { + UI_ThemeColor(TH_EDGE_SEAM); + glLineWidth(2); - draw_dm_edges_sharp(em, cageDM); + draw_dm_edges_seams(em, cageDM); - glColor3ub(0, 0, 0); - } + glColor3ub(0, 0, 0); + } + + if (me->drawflag & ME_DRAWSHARP) { + UI_ThemeColor(TH_EDGE_SHARP); + glLineWidth(2); + + draw_dm_edges_sharp(em, cageDM); + + glColor3ub(0, 0, 0); + } #ifdef WITH_FREESTYLE - if (me->drawflag & ME_DRAW_FREESTYLE_EDGE && CustomData_has_layer(&em->bm->edata, CD_FREESTYLE_EDGE)) { - UI_ThemeColor(TH_FREESTYLE_EDGE_MARK); - glLineWidth(2); - - draw_dm_edges_freestyle(em, cageDM); - - glColor3ub(0, 0, 0); - } -#endif - - if (me->drawflag & ME_DRAWCREASES) { - draw_dm_creases(em, cageDM); - } - if (me->drawflag & ME_DRAWBWEIGHTS) { - draw_dm_bweights(em, scene, cageDM); - } + if (me->drawflag & ME_DRAW_FREESTYLE_EDGE && CustomData_has_layer(&em->bm->edata, CD_FREESTYLE_EDGE)) { + UI_ThemeColor(TH_FREESTYLE_EDGE_MARK); + glLineWidth(2); - glLineWidth(1); - draw_em_fancy_edges(em, scene, v3d, me, cageDM, 0, eed_act); - } + draw_dm_edges_freestyle(em, cageDM); - { - draw_em_fancy_verts(scene, v3d, ob, em, cageDM, eve_act, rv3d); + glColor3ub(0, 0, 0); + } +#endif - if (me->drawflag & ME_DRAWNORMALS) { - UI_ThemeColor(TH_NORMAL); - draw_dm_face_normals(em, scene, ob, cageDM); - } - if (me->drawflag & ME_DRAW_VNORMALS) { - UI_ThemeColor(TH_VNORMAL); - draw_dm_vert_normals(em, scene, ob, cageDM); - } - if (me->drawflag & ME_DRAW_LNORMALS) { - UI_ThemeColor(TH_LNORMAL); - draw_dm_loop_normals(em, scene, ob, cageDM); - } + if (me->drawflag & ME_DRAWCREASES) { + draw_dm_creases(em, cageDM); + } + if (me->drawflag & ME_DRAWBWEIGHTS) { + draw_dm_bweights(em, scene, cageDM); + } - if ((me->drawflag & (ME_DRAWEXTRA_EDGELEN | - ME_DRAWEXTRA_FACEAREA | - ME_DRAWEXTRA_FACEANG | - ME_DRAWEXTRA_EDGEANG)) && - !(v3d->flag2 & V3D_RENDER_OVERRIDE)) - { - draw_em_measure_stats(ar, v3d, ob, em, &scene->unit); + glLineWidth(1); + draw_em_fancy_edges(em, scene, v3d, me, cageDM, 0, eed_act); } - if ((G.debug & G_DEBUG) && (me->drawflag & ME_DRAWEXTRA_INDICES) && - !(v3d->flag2 & V3D_RENDER_OVERRIDE)) { - draw_em_indices(em); + draw_em_fancy_verts(scene, v3d, ob, em, cageDM, eve_act, rv3d); + + if (me->drawflag & ME_DRAWNORMALS) { + UI_ThemeColor(TH_NORMAL); + draw_dm_face_normals(em, scene, ob, cageDM); + } + if (me->drawflag & ME_DRAW_VNORMALS) { + UI_ThemeColor(TH_VNORMAL); + draw_dm_vert_normals(em, scene, ob, cageDM); + } + if (me->drawflag & ME_DRAW_LNORMALS) { + UI_ThemeColor(TH_LNORMAL); + draw_dm_loop_normals(em, scene, ob, cageDM); + } + + if ((me->drawflag & (ME_DRAWEXTRA_EDGELEN | + ME_DRAWEXTRA_FACEAREA | + ME_DRAWEXTRA_FACEANG | + ME_DRAWEXTRA_EDGEANG)) && + !(v3d->flag2 & V3D_RENDER_OVERRIDE)) + { + draw_em_measure_stats(ar, v3d, ob, em, &scene->unit); + } + + if ((G.debug & G_DEBUG) && (me->drawflag & ME_DRAWEXTRA_INDICES) && + !(v3d->flag2 & V3D_RENDER_OVERRIDE)) + { + draw_em_indices(em); + } } } - if (dt > OB_WIRE) { + if (use_depth_offset) { glDepthMask(1); ED_view3d_polygon_offset(rv3d, 0.0); GPU_object_material_unbind(); diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 32008640930..b428ab30784 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -61,7 +61,7 @@ EnumPropertyItem rna_enum_fmodifier_type_items[] = { {FMODIFIER_TYPE_NOISE, "NOISE", 0, "Noise", "Add pseudo-random noise on top of F-Curves"}, /*{FMODIFIER_TYPE_FILTER, "FILTER", 0, "Filter", ""},*/ /* FIXME: not implemented yet! */ - {FMODIFIER_TYPE_PYTHON, "PYTHON", 0, "Python", ""}, + /*{FMODIFIER_TYPE_PYTHON, "PYTHON", 0, "Python", ""},*/ /* FIXME: not implemented yet! */ {FMODIFIER_TYPE_LIMITS, "LIMITS", 0, "Limits", "Restrict maximum and minimum values of F-Curve"}, {FMODIFIER_TYPE_STEPPED, "STEPPED", 0, "Stepped Interpolation", @@ -1467,11 +1467,11 @@ static void rna_def_drivervar(BlenderRNA *brna) PropertyRNA *prop; static EnumPropertyItem prop_type_items[] = { - {DVAR_TYPE_SINGLE_PROP, "SINGLE_PROP", 0, "Single Property", "Use the value from some RNA property (Default)"}, - {DVAR_TYPE_TRANSFORM_CHAN, "TRANSFORMS", 0, "Transform Channel", + {DVAR_TYPE_SINGLE_PROP, "SINGLE_PROP", ICON_RNA, "Single Property", "Use the value from some RNA property (Default)"}, + {DVAR_TYPE_TRANSFORM_CHAN, "TRANSFORMS", ICON_MANIPUL, "Transform Channel", "Final transformation value of object or bone"}, - {DVAR_TYPE_ROT_DIFF, "ROTATION_DIFF", 0, "Rotational Difference", "Use the angle between two bones"}, - {DVAR_TYPE_LOC_DIFF, "LOC_DIFF", 0, "Distance", "Distance between two bones or objects"}, + {DVAR_TYPE_ROT_DIFF, "ROTATION_DIFF", ICON_PARTICLE_TIP, "Rotational Difference", "Use the angle between two bones"}, /* XXX: Icon... */ + {DVAR_TYPE_LOC_DIFF, "LOC_DIFF", ICON_FULLSCREEN_ENTER, "Distance", "Distance between two bones or objects"}, /* XXX: Icon... */ {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index e74a5185ab0..2ec73c3c9b7 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -188,18 +188,19 @@ char pyrna_struct_keyframe_insert_doc[] = "\n" " :arg data_path: path to the property to key, analogous to the fcurve's data path.\n" " :type data_path: string\n" -" :arg index: array index of the property to key. Defaults to -1 which will key all indices or a single channel " - "if the property is not an array.\n" +" :arg index: array index of the property to key.\n" +" Defaults to -1 which will key all indices or a single channel if the property is not an array.\n" " :type index: int\n" " :arg frame: The frame on which the keyframe is inserted, defaulting to the current frame.\n" " :type frame: float\n" " :arg group: The name of the group the F-Curve should be added to if it doesn't exist yet.\n" " :type group: str\n" -" :arg options: Some optional flags:\n" -" 'NEEDED': Only insert keyframes where they're needed in the relevant F-Curves.\n" -" 'VISUAL': Insert keyframes based on 'visual transforms'.\n" -" 'XYZ_TO_RGB': Color for newly added transformation F-Curves (Location, Rotation, Scale) " - "and also Color is based on the transform axis.\n" +" :arg options: Optional flags:\n" +"\n" +" - ``INSERTKEY_NEEDED`` Only insert keyframes where they're needed in the relevant F-Curves.\n" +" - ``INSERTKEY_VISUAL`` Insert keyframes based on 'visual transforms'.\n" +" - ``INSERTKEY_XYZ_TO_RGB`` Color for newly added transformation F-Curves (Location, Rotation, Scale)\n" +" and also Color is based on the transform axis.\n" " :type flag: set\n" " :return: Success of keyframe insertion.\n" " :rtype: boolean\n" diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index af8a7cca8dd..01188cb7f65 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -58,6 +58,7 @@ set(SRC intern/wm_draw.c intern/wm_event_system.c intern/wm_files.c + intern/wm_files_link.c intern/wm_gesture.c intern/wm_init_exit.c intern/wm_jobs.c diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index d9466cbd035..d4c3928bd6c 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -253,7 +253,7 @@ static void wm_cursor_warp_relative(wmWindow *win, int x, int y) } /* give it a modal keymap one day? */ -int wm_cursor_arrow_move(wmWindow *win, wmEvent *event) +bool wm_cursor_arrow_move(wmWindow *win, const wmEvent *event) { if (win && event->val == KM_PRESS) { if (event->type == UPARROWKEY) { diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index f8a879d0485..8f15d94f538 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -43,7 +43,6 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" -#include "BLI_math_base.h" #include "BIF_gl.h" diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 24d4144ec17..e5b98a1b735 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -410,15 +410,15 @@ void wm_event_do_notifiers(bContext *C) CTX_wm_window_set(C, NULL); } -static int wm_event_always_pass(wmEvent *event) +static int wm_event_always_pass(const wmEvent *event) { /* some events we always pass on, to ensure proper communication */ - return ISTIMER(event->type) || (event->type == WINDEACTIVATE) || (event->type == EVT_BUT_OPEN); + return ISTIMER(event->type) || (event->type == WINDEACTIVATE); } /* ********************* ui handler ******************* */ -static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event, int always_pass) +static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, const wmEvent *event, int always_pass) { ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); @@ -1526,7 +1526,7 @@ int WM_userdef_event_map(int kmitype) } -static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi) +static int wm_eventmatch(const wmEvent *winevent, wmKeyMapItem *kmi) { int kmitype = WM_userdef_event_map(kmi->type); @@ -1921,7 +1921,7 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand return action; } -static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event) +static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, const wmEvent *event) { int action = WM_HANDLER_CONTINUE; @@ -1933,7 +1933,7 @@ static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHa return wm_handler_fileselect_do(C, handlers, handler, event->val); } -static bool handler_boundbox_test(wmEventHandler *handler, wmEvent *event) +static bool handler_boundbox_test(wmEventHandler *handler, const wmEvent *event) { if (handler->bbwin) { if (handler->bblocal) { @@ -2245,7 +2245,7 @@ static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar) /* called on mousemove, check updates for paintcursors */ /* context was set on active area and region */ -static void wm_paintcursor_test(bContext *C, wmEvent *event) +static void wm_paintcursor_test(bContext *C, const wmEvent *event) { wmWindowManager *wm = CTX_wm_manager(C); @@ -2312,7 +2312,7 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even } /* filter out all events of the pie that spawned the last pie unless it's a release event */ -static bool wm_event_pie_filter(wmWindow *win, wmEvent *event) +static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event) { if (win->lock_pie_event && win->lock_pie_event == event->type) { if (event->val == KM_RELEASE) { @@ -3124,7 +3124,7 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi return NULL; } -static bool wm_event_is_double_click(wmEvent *event, wmEvent *event_state) +static bool wm_event_is_double_click(wmEvent *event, const wmEvent *event_state) { if ((event->type == event_state->prevtype) && (event_state->prevval == KM_RELEASE) && diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index ce722c732e6..498a3f5bdda 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -26,7 +26,7 @@ /** \file blender/windowmanager/intern/wm_files.c * \ingroup wm * - * User level access for blend file read/write, file-history and userprefs. + * User level access for blend file read/write, file-history and userprefs (including relevant operators). */ @@ -62,6 +62,7 @@ #include "BLT_translation.h" +#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */ #include "DNA_object_types.h" #include "DNA_space_types.h" #include "DNA_userdef_types.h" @@ -88,6 +89,7 @@ #include "BLO_writefile.h" #include "RNA_access.h" +#include "RNA_define.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -103,6 +105,7 @@ #include "GHOST_Path-api.h" #include "UI_interface.h" +#include "UI_resources.h" #include "UI_view2d.h" #include "GPU_draw.h" @@ -749,44 +752,6 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c return true; } -int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) -{ - ED_file_read_bookmarks(); - wm_history_file_read(); - return OPERATOR_FINISHED; -} - -int wm_homefile_read_exec(bContext *C, wmOperator *op) -{ - const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings")); - char filepath_buf[FILE_MAX]; - const char *filepath = NULL; - - if (!from_memory) { - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath"); - - /* This can be used when loading of a start-up file should only change - * the scene content but keep the blender UI as it is. */ - wm_open_init_load_ui(op, true); - BKE_BIT_TEST_SET(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI); - - if (RNA_property_is_set(op->ptr, prop)) { - RNA_property_string_get(op->ptr, prop, filepath_buf); - filepath = filepath_buf; - if (BLI_access(filepath, R_OK)) { - BKE_reportf(op->reports, RPT_ERROR, "Can't read alternative start-up file: '%s'", filepath); - return OPERATOR_CANCELLED; - } - } - } - else { - /* always load UI for factory settings (prefs will re-init) */ - G.fileflags &= ~G_FILE_NO_UI; - } - - return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; -} - /** \name WM History File API * \{ */ @@ -1005,7 +970,7 @@ bool write_crash_blend(void) /** * \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way. */ -int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports) +static int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports) { Library *li; int len; @@ -1117,69 +1082,6 @@ int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList * return ret; } -/** - * \see #wm_file_write wraps #BLO_write_file in a similar way. - */ -int wm_homefile_write_exec(bContext *C, wmOperator *op) -{ - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - char filepath[FILE_MAX]; - int fileflags; - - BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); - - /* check current window and close it if temp */ - if (win && win->screen->temp) - wm_window_close(C, wm, win); - - /* update keymaps in user preferences */ - WM_keyconfig_update(wm); - - BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE); - printf("trying to save homefile at %s ", filepath); - - ED_editors_flush_edits(C, false); - - /* force save as regular blend file */ - fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); - - if (BLO_write_file(CTX_data_main(C), filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) { - printf("fail\n"); - return OPERATOR_CANCELLED; - } - - printf("ok\n"); - - G.save_over = 0; - - BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST); - - return OPERATOR_FINISHED; -} - -/* Only save the prefs block. operator entry */ -int wm_userpref_write_exec(bContext *C, wmOperator *op) -{ - wmWindowManager *wm = CTX_wm_manager(C); - char filepath[FILE_MAX]; - - /* update keymaps in user preferences */ - WM_keyconfig_update(wm); - - BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE); - printf("trying to save userpref at %s ", filepath); - - if (BKE_write_file_userdef(filepath, op->reports) == 0) { - printf("fail\n"); - return OPERATOR_CANCELLED; - } - - printf("ok\n"); - - return OPERATOR_FINISHED; -} - /************************ autosave ****************************/ void wm_autosave_location(char *filepath) @@ -1341,3 +1243,728 @@ void WM_file_tag_modified(const bContext *C) WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, NULL); } } + +/** \name Preferences/startup save & load. + * + * \{ */ + +/** + * \see #wm_file_write wraps #BLO_write_file in a similar way. + */ +static int wm_homefile_write_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + char filepath[FILE_MAX]; + int fileflags; + + BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); + + /* check current window and close it if temp */ + if (win && win->screen->temp) + wm_window_close(C, wm, win); + + /* update keymaps in user preferences */ + WM_keyconfig_update(wm); + + BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE); + printf("trying to save homefile at %s ", filepath); + + ED_editors_flush_edits(C, false); + + /* force save as regular blend file */ + fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); + + if (BLO_write_file(CTX_data_main(C), filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) { + printf("fail\n"); + return OPERATOR_CANCELLED; + } + + printf("ok\n"); + + G.save_over = 0; + + BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST); + + return OPERATOR_FINISHED; +} + +void WM_OT_save_homefile(wmOperatorType *ot) +{ + ot->name = "Save Startup File"; + ot->idname = "WM_OT_save_homefile"; + ot->description = "Make the current file the default .blend file, includes preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_homefile_write_exec; +} + +static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare"); + BLI_addtail(&U.autoexec_paths, path_cmp); + return OPERATOR_FINISHED; +} + +void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot) +{ + ot->name = "Add Autoexec Path"; + ot->idname = "WM_OT_userpref_autoexec_path_add"; + ot->description = "Add path to exclude from autoexecution"; + + ot->exec = wm_userpref_autoexec_add_exec; + + ot->flag = OPTYPE_INTERNAL; +} + +static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op) +{ + const int index = RNA_int_get(op->ptr, "index"); + bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index); + if (path_cmp) { + BLI_freelinkN(&U.autoexec_paths, path_cmp); + } + return OPERATOR_FINISHED; +} + +void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot) +{ + ot->name = "Remove Autoexec Path"; + ot->idname = "WM_OT_userpref_autoexec_path_remove"; + ot->description = "Remove path to exclude from autoexecution"; + + ot->exec = wm_userpref_autoexec_remove_exec; + + ot->flag = OPTYPE_INTERNAL; + + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000); +} + +/* Only save the prefs block. operator entry */ +static int wm_userpref_write_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + char filepath[FILE_MAX]; + + /* update keymaps in user preferences */ + WM_keyconfig_update(wm); + + BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE); + printf("trying to save userpref at %s ", filepath); + + if (BKE_write_file_userdef(filepath, op->reports) == 0) { + printf("fail\n"); + return OPERATOR_CANCELLED; + } + + printf("ok\n"); + + return OPERATOR_FINISHED; +} + +void WM_OT_save_userpref(wmOperatorType *ot) +{ + ot->name = "Save User Settings"; + ot->idname = "WM_OT_save_userpref"; + ot->description = "Save user preferences separately, overrides startup file preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_userpref_write_exec; +} + +static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + ED_file_read_bookmarks(); + wm_history_file_read(); + return OPERATOR_FINISHED; +} + +void WM_OT_read_history(wmOperatorType *ot) +{ + ot->name = "Reload History File"; + ot->idname = "WM_OT_read_history"; + ot->description = "Reloads history and bookmarks"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_history_file_read_exec; + + /* this operator is only used for loading settings from a previous blender install */ + ot->flag = OPTYPE_INTERNAL; +} + +static int wm_homefile_read_exec(bContext *C, wmOperator *op) +{ + const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings")); + char filepath_buf[FILE_MAX]; + const char *filepath = NULL; + + if (!from_memory) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath"); + + /* This can be used when loading of a start-up file should only change + * the scene content but keep the blender UI as it is. */ + wm_open_init_load_ui(op, true); + BKE_BIT_TEST_SET(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI); + + if (RNA_property_is_set(op->ptr, prop)) { + RNA_property_string_get(op->ptr, prop, filepath_buf); + filepath = filepath_buf; + if (BLI_access(filepath, R_OK)) { + BKE_reportf(op->reports, RPT_ERROR, "Can't read alternative start-up file: '%s'", filepath); + return OPERATOR_CANCELLED; + } + } + } + else { + /* always load UI for factory settings (prefs will re-init) */ + G.fileflags &= ~G_FILE_NO_UI; + } + + return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +void WM_OT_read_homefile(wmOperatorType *ot) +{ + PropertyRNA *prop; + ot->name = "Reload Start-Up File"; + ot->idname = "WM_OT_read_homefile"; + ot->description = "Open the default file (doesn't save the current file)"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_homefile_read_exec; + + prop = RNA_def_string_file_path(ot->srna, "filepath", NULL, + FILE_MAX, "File Path", + "Path to an alternative start-up file"); + RNA_def_property_flag(prop, PROP_HIDDEN); + + /* So scripts can use an alternative start-up file without the UI */ + prop = RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", + "Load user interface setup from the .blend file"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + /* omit poll to run in background mode */ +} + +void WM_OT_read_factory_settings(wmOperatorType *ot) +{ + ot->name = "Load Factory Settings"; + ot->idname = "WM_OT_read_factory_settings"; + ot->description = "Load default file and user preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_homefile_read_exec; + /* omit poll to run in background mode */ +} + +/** \} */ + +/** \name Open main .blend file. + * + * \{ */ + +/** + * Wrap #WM_file_read, shared by file reading operators. + */ +static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports, + const bool autoexec_init) +{ + bool success; + + /* XXX wm in context is not set correctly after WM_file_read -> crash */ + /* do it before for now, but is this correct with multiple windows? */ + WM_event_add_notifier(C, NC_WINDOW, NULL); + + if (autoexec_init) { + WM_file_autoexec_init(filepath); + } + + success = WM_file_read(C, filepath, reports); + + return success; +} + +/* currently fits in a pointer */ +struct FileRuntime { + bool is_untrusted; +}; + +static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + const char *openname = G.main->name; + + if (CTX_wm_window(C) == NULL) { + /* in rare cases this could happen, when trying to invoke in background + * mode on load for example. Don't use poll for this because exec() + * can still run without a window */ + BKE_report(op->reports, RPT_ERROR, "Context window not set"); + return OPERATOR_CANCELLED; + } + + /* if possible, get the name of the most recently used .blend file */ + if (G.recent_files.first) { + struct RecentFile *recent = G.recent_files.first; + openname = recent->filepath; + } + + RNA_string_set(op->ptr, "filepath", openname); + wm_open_init_load_ui(op, true); + wm_open_init_use_scripts(op, true); + op->customdata = NULL; + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_open_mainfile_exec(bContext *C, wmOperator *op) +{ + char filepath[FILE_MAX]; + bool success; + + RNA_string_get(op->ptr, "filepath", filepath); + + /* re-use last loaded setting so we can reload a file without changing */ + wm_open_init_load_ui(op, false); + wm_open_init_use_scripts(op, false); + + if (RNA_boolean_get(op->ptr, "load_ui")) + G.fileflags &= ~G_FILE_NO_UI; + else + G.fileflags |= G_FILE_NO_UI; + + if (RNA_boolean_get(op->ptr, "use_scripts")) + G.f |= G_SCRIPT_AUTOEXEC; + else + G.f &= ~G_SCRIPT_AUTOEXEC; + + success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); + + /* for file open also popup for warnings, not only errors */ + BKE_report_print_level_set(op->reports, RPT_WARNING); + + if (success) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op) +{ + struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts"); + bool is_untrusted = false; + char path[FILE_MAX]; + char *lslash; + + RNA_string_get(op->ptr, "filepath", path); + + /* get the dir */ + lslash = (char *)BLI_last_slash(path); + if (lslash) *(lslash + 1) = '\0'; + + if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) { + if (BKE_autoexec_match(path) == true) { + RNA_property_boolean_set(op->ptr, prop, false); + is_untrusted = true; + } + } + + if (file_info) { + file_info->is_untrusted = is_untrusted; + } + + return is_untrusted; +} + +static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op) +{ + struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; + uiLayout *layout = op->layout; + uiLayout *col = op->layout; + const char *autoexec_text; + + uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, false); + if (file_info->is_untrusted) { + autoexec_text = IFACE_("Trusted Source [Untrusted Path]"); + uiLayoutSetActive(col, false); + uiLayoutSetEnabled(col, false); + } + else { + autoexec_text = IFACE_("Trusted Source"); + } + + uiItemR(col, op->ptr, "use_scripts", 0, autoexec_text, ICON_NONE); +} + +void WM_OT_open_mainfile(wmOperatorType *ot) +{ + ot->name = "Open Blender File"; + ot->idname = "WM_OT_open_mainfile"; + ot->description = "Open a Blender file"; + + ot->invoke = wm_open_mainfile_invoke; + ot->exec = wm_open_mainfile_exec; + ot->check = wm_open_mainfile_check; + ot->ui = wm_open_mainfile_ui; + /* omit window poll so this can work in background mode */ + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file"); + RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", + "Allow .blend file to execute scripts automatically, default available from system preferences"); +} + +/** \} */ + +/** \name Reload (revert) main .blend file. + * + * \{ */ + +static int wm_revert_mainfile_exec(bContext *C, wmOperator *op) +{ + bool success; + + wm_open_init_use_scripts(op, false); + + if (RNA_boolean_get(op->ptr, "use_scripts")) + G.f |= G_SCRIPT_AUTOEXEC; + else + G.f &= ~G_SCRIPT_AUTOEXEC; + + success = wm_file_read_opwrap(C, G.main->name, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); + + if (success) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int wm_revert_mainfile_poll(bContext *UNUSED(C)) +{ + return G.relbase_valid; +} + +void WM_OT_revert_mainfile(wmOperatorType *ot) +{ + ot->name = "Revert"; + ot->idname = "WM_OT_revert_mainfile"; + ot->description = "Reload the saved file"; + ot->invoke = WM_operator_confirm; + + RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", + "Allow .blend file to execute scripts automatically, default available from system preferences"); + + ot->exec = wm_revert_mainfile_exec; + ot->poll = wm_revert_mainfile_poll; +} + +/** \} */ + +/** \name Recover last session & auto-save. + * + * \{ */ + +void WM_recover_last_session(bContext *C, ReportList *reports) +{ + char filepath[FILE_MAX]; + + BLI_make_file_string("/", filepath, BKE_tempdir_base(), BLENDER_QUIT_FILE); + /* if reports==NULL, it's called directly without operator, we add a quick check here */ + if (reports || BLI_exists(filepath)) { + G.fileflags |= G_FILE_RECOVER; + + wm_file_read_opwrap(C, filepath, reports, true); + + G.fileflags &= ~G_FILE_RECOVER; + + /* XXX bad global... fixme */ + if (G.main->name[0]) + G.file_loaded = 1; /* prevents splash to show */ + else { + G.relbase_valid = 0; + G.save_over = 0; /* start with save preference untitled.blend */ + } + + } +} + +static int wm_recover_last_session_exec(bContext *C, wmOperator *op) +{ + WM_recover_last_session(C, op->reports); + return OPERATOR_FINISHED; +} + +void WM_OT_recover_last_session(wmOperatorType *ot) +{ + ot->name = "Recover Last Session"; + ot->idname = "WM_OT_recover_last_session"; + ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")"; + ot->invoke = WM_operator_confirm; + + ot->exec = wm_recover_last_session_exec; +} + +static int wm_recover_auto_save_exec(bContext *C, wmOperator *op) +{ + char filepath[FILE_MAX]; + bool success; + + RNA_string_get(op->ptr, "filepath", filepath); + + G.fileflags |= G_FILE_RECOVER; + + success = wm_file_read_opwrap(C, filepath, op->reports, true); + + G.fileflags &= ~G_FILE_RECOVER; + + if (success) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + char filename[FILE_MAX]; + + wm_autosave_location(filename); + RNA_string_set(op->ptr, "filepath", filename); + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void WM_OT_recover_auto_save(wmOperatorType *ot) +{ + ot->name = "Recover Auto Save"; + ot->idname = "WM_OT_recover_auto_save"; + ot->description = "Open an automatically saved file to recover it"; + + ot->exec = wm_recover_auto_save_exec; + ot->invoke = wm_recover_auto_save_invoke; + + WM_operator_properties_filesel( + ot, FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, + WM_FILESEL_FILEPATH, FILE_LONGDISPLAY, FILE_SORT_TIME); +} + +/** \} */ + +/** \name Save main .blend file. + * + * \{ */ + +static void wm_filepath_default(char *filepath) +{ + if (G.save_over == false) { + BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend"); + } +} + +static void save_set_compress(wmOperator *op) +{ + PropertyRNA *prop; + + prop = RNA_struct_find_property(op->ptr, "compress"); + if (!RNA_property_is_set(op->ptr, prop)) { + if (G.save_over) { /* keep flag for existing file */ + RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0); + } + else { /* use userdef for new file */ + RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0); + } + } +} + +static void save_set_filepath(wmOperator *op) +{ + PropertyRNA *prop; + char name[FILE_MAX]; + + prop = RNA_struct_find_property(op->ptr, "filepath"); + if (!RNA_property_is_set(op->ptr, prop)) { + /* if not saved before, get the name of the most recently used .blend file */ + if (G.main->name[0] == 0 && G.recent_files.first) { + struct RecentFile *recent = G.recent_files.first; + BLI_strncpy(name, recent->filepath, FILE_MAX); + } + else { + BLI_strncpy(name, G.main->name, FILE_MAX); + } + + wm_filepath_default(name); + RNA_property_string_set(op->ptr, prop, name); + } +} + +static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + + save_set_compress(op); + save_set_filepath(op); + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* function used for WM_OT_save_mainfile too */ +static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) +{ + char path[FILE_MAX]; + int fileflags; + + save_set_compress(op); + + if (RNA_struct_property_is_set(op->ptr, "filepath")) { + RNA_string_get(op->ptr, "filepath", path); + } + else { + BLI_strncpy(path, G.main->name, FILE_MAX); + wm_filepath_default(path); + } + + fileflags = G.fileflags & ~G_FILE_USERPREFS; + + /* set compression flag */ + BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"), + G_FILE_COMPRESS); + BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), + G_FILE_RELATIVE_REMAP); + BKE_BIT_TEST_SET(fileflags, + (RNA_struct_property_is_set(op->ptr, "copy") && + RNA_boolean_get(op->ptr, "copy")), + G_FILE_SAVE_COPY); + +#ifdef USE_BMESH_SAVE_AS_COMPAT + BKE_BIT_TEST_SET(fileflags, + (RNA_struct_find_property(op->ptr, "use_mesh_compat") && + RNA_boolean_get(op->ptr, "use_mesh_compat")), + G_FILE_MESH_COMPAT); +#else +# error "don't remove by accident" +#endif + + if (wm_file_write(C, path, fileflags, op->reports) != 0) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL); + + return OPERATOR_FINISHED; +} + +/* function used for WM_OT_save_mainfile too */ +static bool blend_save_check(bContext *UNUSED(C), wmOperator *op) +{ + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + if (!BLO_has_bfile_extension(filepath)) { + /* some users would prefer BLI_replace_extension(), + * we keep getting nitpicking bug reports about this - campbell */ + BLI_ensure_extension(filepath, FILE_MAX, ".blend"); + RNA_string_set(op->ptr, "filepath", filepath); + return true; + } + return false; +} + +void WM_OT_save_as_mainfile(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Save As Blender File"; + ot->idname = "WM_OT_save_as_mainfile"; + ot->description = "Save the current file in the desired location"; + + ot->invoke = wm_save_as_mainfile_invoke; + ot->exec = wm_save_as_mainfile_exec; + ot->check = blend_save_check; + /* omit window poll so this can work in background mode */ + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); + RNA_def_boolean(ot->srna, "relative_remap", true, "Remap Relative", + "Remap relative paths when saving in a different directory"); + prop = RNA_def_boolean(ot->srna, "copy", false, "Save Copy", + "Save a copy of the actual working state but does not make saved file active"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +#ifdef USE_BMESH_SAVE_AS_COMPAT + RNA_def_boolean(ot->srna, "use_mesh_compat", false, "Legacy Mesh Format", + "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will " + "be lost (no implicit triangulation)"); +#endif +} + +static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + int ret; + + /* cancel if no active window */ + if (CTX_wm_window(C) == NULL) + return OPERATOR_CANCELLED; + + save_set_compress(op); + save_set_filepath(op); + + /* if we're saving for the first time and prefer relative paths - any existing paths will be absolute, + * enable the option to remap paths to avoid confusion [#37240] */ + if ((G.relbase_valid == false) && (U.flag & USER_RELPATHS)) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(op->ptr, prop, true); + } + } + + if (G.save_over) { + char path[FILE_MAX]; + + RNA_string_get(op->ptr, "filepath", path); + if (BLI_exists(path)) { + ret = WM_operator_confirm_message_ex(C, op, IFACE_("Save Over?"), ICON_QUESTION, path); + } + else { + ret = wm_save_as_mainfile_exec(C, op); + } + } + else { + WM_event_add_fileselect(C, op); + ret = OPERATOR_RUNNING_MODAL; + } + + return ret; +} + +void WM_OT_save_mainfile(wmOperatorType *ot) +{ + ot->name = "Save Blender File"; + ot->idname = "WM_OT_save_mainfile"; + ot->description = "Save the current Blender file"; + + ot->invoke = wm_save_mainfile_invoke; + ot->exec = wm_save_as_mainfile_exec; + ot->check = blend_save_check; + /* omit window poll so this can work in background mode */ + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); + RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative", + "Remap relative paths when saving in a different directory"); +} + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c new file mode 100644 index 00000000000..5041eebd126 --- /dev/null +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -0,0 +1,497 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_files_link.c + * \ingroup wm + * + * Functions for dealing with append/link operators and helpers. + */ + + +#include <float.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <stddef.h> +#include <assert.h> +#include <errno.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_ID.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_windowmanager_types.h" + + + +#include "BLI_blenlib.h" +#include "BLI_bitmap.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + +#include "BLO_readfile.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_library.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "BKE_idcode.h" + + +#include "IMB_colormanagement.h" + +#include "ED_screen.h" + +#include "GPU_material.h" + +#include "RNA_access.h" +#include "RNA_define.h" + + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_files.h" + +/* **************** link/append *************** */ + +static int wm_link_append_poll(bContext *C) +{ + if (WM_operator_winactive(C)) { + /* linking changes active object which is pretty useful in general, + * but which totally confuses edit mode (i.e. it becoming not so obvious + * to leave from edit mode and invalid tools in toolbar might be displayed) + * so disable link/append when in edit mode (sergey) */ + if (CTX_data_edit_object(C)) + return 0; + + return 1; + } + + return 0; +} + +static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (RNA_struct_property_is_set(op->ptr, "filepath")) { + return WM_operator_call_notest(C, op); + } + else { + /* XXX TODO solve where to get last linked library from */ + if (G.lib[0] != '\0') { + RNA_string_set(op->ptr, "filepath", G.lib); + } + else if (G.relbase_valid) { + char path[FILE_MAX]; + BLI_strncpy(path, G.main->name, sizeof(G.main->name)); + BLI_parent_dir(path); + RNA_string_set(op->ptr, "filepath", path); + } + WM_event_add_fileselect(C, op); + return OPERATOR_RUNNING_MODAL; + } +} + +static short wm_link_append_flag(wmOperator *op) +{ + PropertyRNA *prop; + short flag = 0; + + if (RNA_boolean_get(op->ptr, "autoselect")) + flag |= FILE_AUTOSELECT; + if (RNA_boolean_get(op->ptr, "active_layer")) + flag |= FILE_ACTIVELAY; + if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop)) + flag |= FILE_RELPATH; + if (RNA_boolean_get(op->ptr, "link")) + flag |= FILE_LINK; + if (RNA_boolean_get(op->ptr, "instance_groups")) + flag |= FILE_GROUP_INSTANCE; + + return flag; +} + +typedef struct WMLinkAppendDataItem { + char *name; + BLI_bitmap *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ + short idcode; + + ID *new_id; + void *customdata; +} WMLinkAppendDataItem; + +typedef struct WMLinkAppendData { + LinkNodePair libraries; + LinkNodePair items; + int num_libraries; + int num_items; + short flag; + + /* Internal 'private' data */ + MemArena *memarena; +} WMLinkAppendData; + +static WMLinkAppendData *wm_link_append_data_new(const int flag) +{ + MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data)); + + lapp_data->flag = flag; + lapp_data->memarena = ma; + + return lapp_data; +} + +static void wm_link_append_data_free(WMLinkAppendData *lapp_data) +{ + BLI_memarena_free(lapp_data->memarena); +} + +/* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */ + +static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname) +{ + size_t len = strlen(libname) + 1; + char *libpath = BLI_memarena_alloc(lapp_data->memarena, len); + + BLI_strncpy(libpath, libname, len); + BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena); + lapp_data->num_libraries++; +} + +static WMLinkAppendDataItem *wm_link_append_data_item_add( + WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata) +{ + WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item)); + size_t len = strlen(idname) + 1; + + item->name = BLI_memarena_alloc(lapp_data->memarena, len); + BLI_strncpy(item->name, idname, len); + item->idcode = idcode; + item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); + + item->new_id = NULL; + item->customdata = customdata; + + BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); + lapp_data->num_items++; + + return item; +} + +static void wm_link_do( + WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d) +{ + Main *mainl; + BlendHandle *bh; + Library *lib; + + const int flag = lapp_data->flag; + + LinkNode *liblink, *itemlink; + int lib_idx, item_idx; + + BLI_assert(lapp_data->num_items && lapp_data->num_libraries); + + for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) { + char *libname = liblink->link; + + bh = BLO_blendhandle_from_file(libname, reports); + + if (bh == NULL) { + /* Unlikely since we just browsed it, but possible + * Error reports will have been made by BLO_blendhandle_from_file() */ + continue; + } + + /* here appending/linking starts */ + mainl = BLO_library_link_begin(bmain, &bh, libname); + lib = mainl->curlib; + BLI_assert(lib); + UNUSED_VARS_NDEBUG(lib); + + if (mainl->versionfile < 250) { + BKE_reportf(reports, RPT_WARNING, + "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will " + "be done! You may want to re-save your lib file with current Blender", + mainl->versionfile, mainl->subversionfile); + } + + /* For each lib file, we try to link all items belonging to that lib, + * and tag those successful to not try to load them again with the other libs. */ + for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *new_id; + + if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) { + continue; + } + + new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d); + if (new_id) { + /* If the link is sucessful, clear item's libs 'todo' flags. + * This avoids trying to link same item with other libraries to come. */ + BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries); + item->new_id = new_id; + } + } + + BLO_library_link_end(mainl, &bh, flag, scene, v3d); + BLO_blendhandle_close(bh); + } +} + +static int wm_link_append_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + PropertyRNA *prop; + WMLinkAppendData *lapp_data; + char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; + char *group, *name; + int totfiles = 0; + short flag; + + RNA_string_get(op->ptr, "filename", relname); + RNA_string_get(op->ptr, "directory", root); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + /* test if we have a valid data */ + if (!BLO_library_path_explode(path, libname, &group, &name)) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path); + return OPERATOR_CANCELLED; + } + else if (!group) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); + return OPERATOR_CANCELLED; + } + else if (BLI_path_cmp(bmain->name, libname) == 0) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path); + return OPERATOR_CANCELLED; + } + + /* check if something is indicated for append/link */ + prop = RNA_struct_find_property(op->ptr, "files"); + if (prop) { + totfiles = RNA_property_collection_length(op->ptr, prop); + if (totfiles == 0) { + if (!name) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); + return OPERATOR_CANCELLED; + } + } + } + else if (!name) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); + return OPERATOR_CANCELLED; + } + + flag = wm_link_append_flag(op); + + /* sanity checks for flag */ + if (scene && scene->id.lib) { + BKE_reportf(op->reports, RPT_WARNING, + "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2); + flag &= ~FILE_GROUP_INSTANCE; + scene = NULL; + } + + /* from here down, no error returns */ + + if (scene && RNA_boolean_get(op->ptr, "autoselect")) { + BKE_scene_base_deselect_all(scene); + } + + /* tag everything, all untagged data can be made local + * its also generally useful to know what is new + * + * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); + + /* We define our working data... + * Note that here, each item 'uses' one library, and only one. */ + lapp_data = wm_link_append_data_new(flag); + if (totfiles != 0) { + GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); + int lib_idx = 0; + + RNA_BEGIN (op->ptr, itemptr, "files") + { + RNA_string_get(&itemptr, "name", relname); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + if (BLO_library_path_explode(path, libname, &group, &name)) { + if (!group || !name) { + continue; + } + + if (!BLI_ghash_haskey(libraries, libname)) { + BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx)); + lib_idx++; + wm_link_append_data_library_add(lapp_data, libname); + } + } + } + RNA_END; + + RNA_BEGIN (op->ptr, itemptr, "files") + { + RNA_string_get(&itemptr, "name", relname); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + if (BLO_library_path_explode(path, libname, &group, &name)) { + WMLinkAppendDataItem *item; + if (!group || !name) { + printf("skipping %s\n", path); + continue; + } + + lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname)); + + item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); + BLI_BITMAP_ENABLE(item->libraries, lib_idx); + } + } + RNA_END; + + BLI_ghash_free(libraries, MEM_freeN, NULL); + } + else { + WMLinkAppendDataItem *item; + + wm_link_append_data_library_add(lapp_data, libname); + item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); + BLI_BITMAP_ENABLE(item->libraries, 0); + } + + /* XXX We'd need re-entrant locking on Main for this to work... */ + /* BKE_main_lock(bmain); */ + + wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C)); + + /* BKE_main_unlock(bmain); */ + + wm_link_append_data_free(lapp_data); + + /* mark all library linked objects to be updated */ + BKE_main_lib_objects_recalc_all(bmain); + IMB_colormanagement_check_file_config(bmain); + + /* append, rather than linking */ + if ((flag & FILE_LINK) == 0) { + bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); + BKE_library_make_local(bmain, NULL, true, set_fake); + } + + /* important we unset, otherwise these object wont + * link into other scenes from this blend file */ + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + + /* recreate dependency graph to include new objects */ + DAG_scene_relations_rebuild(bmain, scene); + + /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ + GPU_materials_free(); + + /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ + BLI_strncpy(G.lib, root, FILE_MAX); + + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; +} + +static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) +{ + PropertyRNA *prop; + + /* better not save _any_ settings for this operator */ + /* properties */ + prop = RNA_def_boolean(ot->srna, "link", is_link, + "Link", "Link the objects or datablocks rather than appending"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "autoselect", true, + "Select", "Select new objects"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "active_layer", true, + "Active Layer", "Put new objects on the active layer"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "instance_groups", is_link, + "Instance Groups", "Create Dupli-Group instances for each group"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +void WM_OT_link(wmOperatorType *ot) +{ + ot->name = "Link from Library"; + ot->idname = "WM_OT_link"; + ot->description = "Link from a Library .blend file"; + + ot->invoke = wm_link_append_invoke; + ot->exec = wm_link_append_exec; + ot->poll = wm_link_append_poll; + + ot->flag |= OPTYPE_UNDO; + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + wm_link_append_properties_common(ot, true); +} + +void WM_OT_append(wmOperatorType *ot) +{ + ot->name = "Append from Library"; + ot->idname = "WM_OT_append"; + ot->description = "Append from a Library .blend file"; + + ot->invoke = wm_link_append_invoke; + ot->exec = wm_link_append_exec; + ot->poll = wm_link_append_poll; + + ot->flag |= OPTYPE_UNDO; + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + wm_link_append_properties_common(ot, false); + RNA_def_boolean(ot->srna, "set_fake", false, "Fake User", "Set Fake User for appended items (except Objects and Groups)"); +} diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index b5ad027148a..8a15237078a 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -52,26 +52,21 @@ #include "DNA_scene_types.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" -#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */ #include "BLT_translation.h" #include "PIL_time.h" #include "BLI_blenlib.h" -#include "BLI_bitmap.h" #include "BLI_dial.h" #include "BLI_dynstr.h" /*for WM_operator_pystring */ -#include "BLI_linklist.h" #include "BLI_math.h" -#include "BLI_memarena.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BLO_readfile.h" #include "BKE_appdir.h" -#include "BKE_autoexec.h" #include "BKE_blender.h" #include "BKE_brush.h" #include "BKE_context.h" @@ -88,14 +83,12 @@ #include "BKE_scene.h" #include "BKE_screen.h" /* BKE_ST_MAXNAME */ #include "BKE_unit.h" -#include "BKE_utildefines.h" #include "BKE_idcode.h" #include "BIF_glutil.h" /* for paint cursor */ #include "BLF_api.h" -#include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -105,7 +98,6 @@ #include "ED_view3d.h" #include "GPU_basic_shader.h" -#include "GPU_material.h" #include "RNA_access.h" #include "RNA_define.h" @@ -2073,1025 +2065,6 @@ static void WM_OT_window_duplicate(wmOperatorType *ot) ot->poll = wm_operator_winactive_normal; } -static void WM_OT_save_homefile(wmOperatorType *ot) -{ - ot->name = "Save Startup File"; - ot->idname = "WM_OT_save_homefile"; - ot->description = "Make the current file the default .blend file, includes preferences"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_homefile_write_exec; -} - -static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) -{ - bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare"); - BLI_addtail(&U.autoexec_paths, path_cmp); - return OPERATOR_FINISHED; -} - -static void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot) -{ - ot->name = "Add Autoexec Path"; - ot->idname = "WM_OT_userpref_autoexec_path_add"; - ot->description = "Add path to exclude from autoexecution"; - - ot->exec = wm_userpref_autoexec_add_exec; - - ot->flag = OPTYPE_INTERNAL; -} - -static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op) -{ - const int index = RNA_int_get(op->ptr, "index"); - bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index); - if (path_cmp) { - BLI_freelinkN(&U.autoexec_paths, path_cmp); - } - return OPERATOR_FINISHED; -} - -static void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot) -{ - ot->name = "Remove Autoexec Path"; - ot->idname = "WM_OT_userpref_autoexec_path_remove"; - ot->description = "Remove path to exclude from autoexecution"; - - ot->exec = wm_userpref_autoexec_remove_exec; - - ot->flag = OPTYPE_INTERNAL; - - RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000); -} - -static void WM_OT_save_userpref(wmOperatorType *ot) -{ - ot->name = "Save User Settings"; - ot->idname = "WM_OT_save_userpref"; - ot->description = "Save user preferences separately, overrides startup file preferences"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_userpref_write_exec; -} - -static void WM_OT_read_history(wmOperatorType *ot) -{ - ot->name = "Reload History File"; - ot->idname = "WM_OT_read_history"; - ot->description = "Reloads history and bookmarks"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_history_file_read_exec; - - /* this operator is only used for loading settings from a previous blender install */ - ot->flag = OPTYPE_INTERNAL; -} - -static void WM_OT_read_homefile(wmOperatorType *ot) -{ - PropertyRNA *prop; - ot->name = "Reload Start-Up File"; - ot->idname = "WM_OT_read_homefile"; - ot->description = "Open the default file (doesn't save the current file)"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_homefile_read_exec; - - prop = RNA_def_string_file_path(ot->srna, "filepath", NULL, - FILE_MAX, "File Path", - "Path to an alternative start-up file"); - RNA_def_property_flag(prop, PROP_HIDDEN); - - /* So scripts can use an alternative start-up file without the UI */ - prop = RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", - "Load user interface setup from the .blend file"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - /* omit poll to run in background mode */ -} - -static void WM_OT_read_factory_settings(wmOperatorType *ot) -{ - ot->name = "Load Factory Settings"; - ot->idname = "WM_OT_read_factory_settings"; - ot->description = "Load default file and user preferences"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_homefile_read_exec; - /* omit poll to run in background mode */ -} - -/* *************** open file **************** */ - -/** - * Wrap #WM_file_read, shared by file reading operators. - */ -static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports, - const bool autoexec_init) -{ - bool success; - - /* XXX wm in context is not set correctly after WM_file_read -> crash */ - /* do it before for now, but is this correct with multiple windows? */ - WM_event_add_notifier(C, NC_WINDOW, NULL); - - if (autoexec_init) { - WM_file_autoexec_init(filepath); - } - - success = WM_file_read(C, filepath, reports); - - return success; -} - -/* currently fits in a pointer */ -struct FileRuntime { - bool is_untrusted; -}; - -static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - const char *openname = G.main->name; - - if (CTX_wm_window(C) == NULL) { - /* in rare cases this could happen, when trying to invoke in background - * mode on load for example. Don't use poll for this because exec() - * can still run without a window */ - BKE_report(op->reports, RPT_ERROR, "Context window not set"); - return OPERATOR_CANCELLED; - } - - /* if possible, get the name of the most recently used .blend file */ - if (G.recent_files.first) { - struct RecentFile *recent = G.recent_files.first; - openname = recent->filepath; - } - - RNA_string_set(op->ptr, "filepath", openname); - wm_open_init_load_ui(op, true); - wm_open_init_use_scripts(op, true); - op->customdata = NULL; - - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static int wm_open_mainfile_exec(bContext *C, wmOperator *op) -{ - char filepath[FILE_MAX]; - bool success; - - RNA_string_get(op->ptr, "filepath", filepath); - - /* re-use last loaded setting so we can reload a file without changing */ - wm_open_init_load_ui(op, false); - wm_open_init_use_scripts(op, false); - - if (RNA_boolean_get(op->ptr, "load_ui")) - G.fileflags &= ~G_FILE_NO_UI; - else - G.fileflags |= G_FILE_NO_UI; - - if (RNA_boolean_get(op->ptr, "use_scripts")) - G.f |= G_SCRIPT_AUTOEXEC; - else - G.f &= ~G_SCRIPT_AUTOEXEC; - - success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); - - /* for file open also popup for warnings, not only errors */ - BKE_report_print_level_set(op->reports, RPT_WARNING); - - if (success) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op) -{ - struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts"); - bool is_untrusted = false; - char path[FILE_MAX]; - char *lslash; - - RNA_string_get(op->ptr, "filepath", path); - - /* get the dir */ - lslash = (char *)BLI_last_slash(path); - if (lslash) *(lslash + 1) = '\0'; - - if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) { - if (BKE_autoexec_match(path) == true) { - RNA_property_boolean_set(op->ptr, prop, false); - is_untrusted = true; - } - } - - if (file_info) { - file_info->is_untrusted = is_untrusted; - } - - return is_untrusted; -} - -static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op) -{ - struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; - uiLayout *layout = op->layout; - uiLayout *col = op->layout; - const char *autoexec_text; - - uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - if (file_info->is_untrusted) { - autoexec_text = IFACE_("Trusted Source [Untrusted Path]"); - uiLayoutSetActive(col, false); - uiLayoutSetEnabled(col, false); - } - else { - autoexec_text = IFACE_("Trusted Source"); - } - - uiItemR(col, op->ptr, "use_scripts", 0, autoexec_text, ICON_NONE); -} - -static void WM_OT_open_mainfile(wmOperatorType *ot) -{ - ot->name = "Open Blender File"; - ot->idname = "WM_OT_open_mainfile"; - ot->description = "Open a Blender file"; - - ot->invoke = wm_open_mainfile_invoke; - ot->exec = wm_open_mainfile_exec; - ot->check = wm_open_mainfile_check; - ot->ui = wm_open_mainfile_ui; - /* omit window poll so this can work in background mode */ - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - - RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file"); - RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", - "Allow .blend file to execute scripts automatically, default available from system preferences"); -} - - -/* *************** revert file **************** */ - -static int wm_revert_mainfile_exec(bContext *C, wmOperator *op) -{ - bool success; - - wm_open_init_use_scripts(op, false); - - if (RNA_boolean_get(op->ptr, "use_scripts")) - G.f |= G_SCRIPT_AUTOEXEC; - else - G.f &= ~G_SCRIPT_AUTOEXEC; - - success = wm_file_read_opwrap(C, G.main->name, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); - - if (success) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static int wm_revert_mainfile_poll(bContext *UNUSED(C)) -{ - return G.relbase_valid; -} - -static void WM_OT_revert_mainfile(wmOperatorType *ot) -{ - ot->name = "Revert"; - ot->idname = "WM_OT_revert_mainfile"; - ot->description = "Reload the saved file"; - ot->invoke = WM_operator_confirm; - - RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", - "Allow .blend file to execute scripts automatically, default available from system preferences"); - - ot->exec = wm_revert_mainfile_exec; - ot->poll = wm_revert_mainfile_poll; -} - -/* **************** link/append *************** */ - -static int wm_link_append_poll(bContext *C) -{ - if (WM_operator_winactive(C)) { - /* linking changes active object which is pretty useful in general, - * but which totally confuses edit mode (i.e. it becoming not so obvious - * to leave from edit mode and invalid tools in toolbar might be displayed) - * so disable link/append when in edit mode (sergey) */ - if (CTX_data_edit_object(C)) - return 0; - - return 1; - } - - return 0; -} - -static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - if (RNA_struct_property_is_set(op->ptr, "filepath")) { - return WM_operator_call_notest(C, op); - } - else { - /* XXX TODO solve where to get last linked library from */ - if (G.lib[0] != '\0') { - RNA_string_set(op->ptr, "filepath", G.lib); - } - else if (G.relbase_valid) { - char path[FILE_MAX]; - BLI_strncpy(path, G.main->name, sizeof(G.main->name)); - BLI_parent_dir(path); - RNA_string_set(op->ptr, "filepath", path); - } - WM_event_add_fileselect(C, op); - return OPERATOR_RUNNING_MODAL; - } -} - -static short wm_link_append_flag(wmOperator *op) -{ - PropertyRNA *prop; - short flag = 0; - - if (RNA_boolean_get(op->ptr, "autoselect")) - flag |= FILE_AUTOSELECT; - if (RNA_boolean_get(op->ptr, "active_layer")) - flag |= FILE_ACTIVELAY; - if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop)) - flag |= FILE_RELPATH; - if (RNA_boolean_get(op->ptr, "link")) - flag |= FILE_LINK; - if (RNA_boolean_get(op->ptr, "instance_groups")) - flag |= FILE_GROUP_INSTANCE; - - return flag; -} - -typedef struct WMLinkAppendDataItem { - char *name; - BLI_bitmap *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ - short idcode; - - ID *new_id; - void *customdata; -} WMLinkAppendDataItem; - -typedef struct WMLinkAppendData { - LinkNodePair libraries; - LinkNodePair items; - int num_libraries; - int num_items; - short flag; - - /* Internal 'private' data */ - MemArena *memarena; -} WMLinkAppendData; - -static WMLinkAppendData *wm_link_append_data_new(const int flag) -{ - MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data)); - - lapp_data->flag = flag; - lapp_data->memarena = ma; - - return lapp_data; -} - -static void wm_link_append_data_free(WMLinkAppendData *lapp_data) -{ - BLI_memarena_free(lapp_data->memarena); -} - -/* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */ - -static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname) -{ - size_t len = strlen(libname) + 1; - char *libpath = BLI_memarena_alloc(lapp_data->memarena, len); - - BLI_strncpy(libpath, libname, len); - BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena); - lapp_data->num_libraries++; -} - -static WMLinkAppendDataItem *wm_link_append_data_item_add( - WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata) -{ - WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item)); - size_t len = strlen(idname) + 1; - - item->name = BLI_memarena_alloc(lapp_data->memarena, len); - BLI_strncpy(item->name, idname, len); - item->idcode = idcode; - item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); - - item->new_id = NULL; - item->customdata = customdata; - - BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); - lapp_data->num_items++; - - return item; -} - -static void wm_link_do( - WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d) -{ - Main *mainl; - BlendHandle *bh; - Library *lib; - - const int flag = lapp_data->flag; - - LinkNode *liblink, *itemlink; - int lib_idx, item_idx; - - BLI_assert(lapp_data->num_items && lapp_data->num_libraries); - - for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) { - char *libname = liblink->link; - - bh = BLO_blendhandle_from_file(libname, reports); - - if (bh == NULL) { - /* Unlikely since we just browsed it, but possible - * Error reports will have been made by BLO_blendhandle_from_file() */ - continue; - } - - /* here appending/linking starts */ - mainl = BLO_library_link_begin(bmain, &bh, libname); - lib = mainl->curlib; - BLI_assert(lib); - UNUSED_VARS_NDEBUG(lib); - - if (mainl->versionfile < 250) { - BKE_reportf(reports, RPT_WARNING, - "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will " - "be done! You may want to re-save your lib file with current Blender", - mainl->versionfile, mainl->subversionfile); - } - - /* For each lib file, we try to link all items belonging to that lib, - * and tag those successful to not try to load them again with the other libs. */ - for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { - WMLinkAppendDataItem *item = itemlink->link; - ID *new_id; - - if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) { - continue; - } - - new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d); - if (new_id) { - /* If the link is sucessful, clear item's libs 'todo' flags. - * This avoids trying to link same item with other libraries to come. */ - BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries); - item->new_id = new_id; - } - } - - BLO_library_link_end(mainl, &bh, flag, scene, v3d); - BLO_blendhandle_close(bh); - } -} - -static int wm_link_append_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - PropertyRNA *prop; - WMLinkAppendData *lapp_data; - char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; - char *group, *name; - int totfiles = 0; - short flag; - - RNA_string_get(op->ptr, "filename", relname); - RNA_string_get(op->ptr, "directory", root); - - BLI_join_dirfile(path, sizeof(path), root, relname); - - /* test if we have a valid data */ - if (!BLO_library_path_explode(path, libname, &group, &name)) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path); - return OPERATOR_CANCELLED; - } - else if (!group) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); - return OPERATOR_CANCELLED; - } - else if (BLI_path_cmp(bmain->name, libname) == 0) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path); - return OPERATOR_CANCELLED; - } - - /* check if something is indicated for append/link */ - prop = RNA_struct_find_property(op->ptr, "files"); - if (prop) { - totfiles = RNA_property_collection_length(op->ptr, prop); - if (totfiles == 0) { - if (!name) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); - return OPERATOR_CANCELLED; - } - } - } - else if (!name) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); - return OPERATOR_CANCELLED; - } - - flag = wm_link_append_flag(op); - - /* sanity checks for flag */ - if (scene && scene->id.lib) { - BKE_reportf(op->reports, RPT_WARNING, - "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2); - flag &= ~FILE_GROUP_INSTANCE; - scene = NULL; - } - - /* from here down, no error returns */ - - if (scene && RNA_boolean_get(op->ptr, "autoselect")) { - BKE_scene_base_deselect_all(scene); - } - - /* tag everything, all untagged data can be made local - * its also generally useful to know what is new - * - * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); - - /* We define our working data... - * Note that here, each item 'uses' one library, and only one. */ - lapp_data = wm_link_append_data_new(flag); - if (totfiles != 0) { - GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); - int lib_idx = 0; - - RNA_BEGIN (op->ptr, itemptr, "files") - { - RNA_string_get(&itemptr, "name", relname); - - BLI_join_dirfile(path, sizeof(path), root, relname); - - if (BLO_library_path_explode(path, libname, &group, &name)) { - if (!group || !name) { - continue; - } - - if (!BLI_ghash_haskey(libraries, libname)) { - BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx)); - lib_idx++; - wm_link_append_data_library_add(lapp_data, libname); - } - } - } - RNA_END; - - RNA_BEGIN (op->ptr, itemptr, "files") - { - RNA_string_get(&itemptr, "name", relname); - - BLI_join_dirfile(path, sizeof(path), root, relname); - - if (BLO_library_path_explode(path, libname, &group, &name)) { - WMLinkAppendDataItem *item; - if (!group || !name) { - printf("skipping %s\n", path); - continue; - } - - lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname)); - - item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); - BLI_BITMAP_ENABLE(item->libraries, lib_idx); - } - } - RNA_END; - - BLI_ghash_free(libraries, MEM_freeN, NULL); - } - else { - WMLinkAppendDataItem *item; - - wm_link_append_data_library_add(lapp_data, libname); - item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); - BLI_BITMAP_ENABLE(item->libraries, 0); - } - - /* XXX We'd need re-entrant locking on Main for this to work... */ - /* BKE_main_lock(bmain); */ - - wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C)); - - /* BKE_main_unlock(bmain); */ - - wm_link_append_data_free(lapp_data); - - /* mark all library linked objects to be updated */ - BKE_main_lib_objects_recalc_all(bmain); - IMB_colormanagement_check_file_config(bmain); - - /* append, rather than linking */ - if ((flag & FILE_LINK) == 0) { - bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); - BKE_library_make_local(bmain, NULL, true, set_fake); - } - - /* important we unset, otherwise these object wont - * link into other scenes from this blend file */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); - - /* recreate dependency graph to include new objects */ - DAG_scene_relations_rebuild(bmain, scene); - - /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ - GPU_materials_free(); - - /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ - BLI_strncpy(G.lib, root, FILE_MAX); - - WM_event_add_notifier(C, NC_WINDOW, NULL); - - return OPERATOR_FINISHED; -} - -static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) -{ - PropertyRNA *prop; - - /* better not save _any_ settings for this operator */ - /* properties */ - prop = RNA_def_boolean(ot->srna, "link", is_link, - "Link", "Link the objects or datablocks rather than appending"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); - prop = RNA_def_boolean(ot->srna, "autoselect", true, - "Select", "Select new objects"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "active_layer", true, - "Active Layer", "Put new objects on the active layer"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "instance_groups", is_link, - "Instance Groups", "Create Dupli-Group instances for each group"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -static void WM_OT_link(wmOperatorType *ot) -{ - ot->name = "Link from Library"; - ot->idname = "WM_OT_link"; - ot->description = "Link from a Library .blend file"; - - ot->invoke = wm_link_append_invoke; - ot->exec = wm_link_append_exec; - ot->poll = wm_link_append_poll; - - ot->flag |= OPTYPE_UNDO; - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES, - FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - - wm_link_append_properties_common(ot, true); -} - -static void WM_OT_append(wmOperatorType *ot) -{ - ot->name = "Append from Library"; - ot->idname = "WM_OT_append"; - ot->description = "Append from a Library .blend file"; - - ot->invoke = wm_link_append_invoke; - ot->exec = wm_link_append_exec; - ot->poll = wm_link_append_poll; - - ot->flag |= OPTYPE_UNDO; - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES, - FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - - wm_link_append_properties_common(ot, false); - RNA_def_boolean(ot->srna, "set_fake", false, "Fake User", "Set Fake User for appended items (except Objects and Groups)"); -} - -/* *************** recover last session **************** */ - -void WM_recover_last_session(bContext *C, ReportList *reports) -{ - char filepath[FILE_MAX]; - - BLI_make_file_string("/", filepath, BKE_tempdir_base(), BLENDER_QUIT_FILE); - /* if reports==NULL, it's called directly without operator, we add a quick check here */ - if (reports || BLI_exists(filepath)) { - G.fileflags |= G_FILE_RECOVER; - - wm_file_read_opwrap(C, filepath, reports, true); - - G.fileflags &= ~G_FILE_RECOVER; - - /* XXX bad global... fixme */ - if (G.main->name[0]) - G.file_loaded = 1; /* prevents splash to show */ - else { - G.relbase_valid = 0; - G.save_over = 0; /* start with save preference untitled.blend */ - } - - } -} - -static int wm_recover_last_session_exec(bContext *C, wmOperator *op) -{ - WM_recover_last_session(C, op->reports); - return OPERATOR_FINISHED; -} - -static void WM_OT_recover_last_session(wmOperatorType *ot) -{ - ot->name = "Recover Last Session"; - ot->idname = "WM_OT_recover_last_session"; - ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")"; - ot->invoke = WM_operator_confirm; - - ot->exec = wm_recover_last_session_exec; -} - -/* *************** recover auto save **************** */ - -static int wm_recover_auto_save_exec(bContext *C, wmOperator *op) -{ - char filepath[FILE_MAX]; - bool success; - - RNA_string_get(op->ptr, "filepath", filepath); - - G.fileflags |= G_FILE_RECOVER; - - success = wm_file_read_opwrap(C, filepath, op->reports, true); - - G.fileflags &= ~G_FILE_RECOVER; - - if (success) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - char filename[FILE_MAX]; - - wm_autosave_location(filename); - RNA_string_set(op->ptr, "filepath", filename); - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static void WM_OT_recover_auto_save(wmOperatorType *ot) -{ - ot->name = "Recover Auto Save"; - ot->idname = "WM_OT_recover_auto_save"; - ot->description = "Open an automatically saved file to recover it"; - - ot->exec = wm_recover_auto_save_exec; - ot->invoke = wm_recover_auto_save_invoke; - - WM_operator_properties_filesel( - ot, FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, FILE_LONGDISPLAY, FILE_SORT_TIME); -} - -/* *************** save file as **************** */ - -static void wm_filepath_default(char *filepath) -{ - if (G.save_over == false) { - BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend"); - } -} - -static void save_set_compress(wmOperator *op) -{ - PropertyRNA *prop; - - prop = RNA_struct_find_property(op->ptr, "compress"); - if (!RNA_property_is_set(op->ptr, prop)) { - if (G.save_over) { /* keep flag for existing file */ - RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0); - } - else { /* use userdef for new file */ - RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0); - } - } -} - -static void save_set_filepath(wmOperator *op) -{ - PropertyRNA *prop; - char name[FILE_MAX]; - - prop = RNA_struct_find_property(op->ptr, "filepath"); - if (!RNA_property_is_set(op->ptr, prop)) { - /* if not saved before, get the name of the most recently used .blend file */ - if (G.main->name[0] == 0 && G.recent_files.first) { - struct RecentFile *recent = G.recent_files.first; - BLI_strncpy(name, recent->filepath, FILE_MAX); - } - else { - BLI_strncpy(name, G.main->name, FILE_MAX); - } - - wm_filepath_default(name); - RNA_property_string_set(op->ptr, prop, name); - } -} - -static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - - save_set_compress(op); - save_set_filepath(op); - - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -/* function used for WM_OT_save_mainfile too */ -static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) -{ - char path[FILE_MAX]; - int fileflags; - - save_set_compress(op); - - if (RNA_struct_property_is_set(op->ptr, "filepath")) { - RNA_string_get(op->ptr, "filepath", path); - } - else { - BLI_strncpy(path, G.main->name, FILE_MAX); - wm_filepath_default(path); - } - - fileflags = G.fileflags & ~G_FILE_USERPREFS; - - /* set compression flag */ - BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"), - G_FILE_COMPRESS); - BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), - G_FILE_RELATIVE_REMAP); - BKE_BIT_TEST_SET(fileflags, - (RNA_struct_property_is_set(op->ptr, "copy") && - RNA_boolean_get(op->ptr, "copy")), - G_FILE_SAVE_COPY); - -#ifdef USE_BMESH_SAVE_AS_COMPAT - BKE_BIT_TEST_SET(fileflags, - (RNA_struct_find_property(op->ptr, "use_mesh_compat") && - RNA_boolean_get(op->ptr, "use_mesh_compat")), - G_FILE_MESH_COMPAT); -#else -# error "don't remove by accident" -#endif - - if (wm_file_write(C, path, fileflags, op->reports) != 0) - return OPERATOR_CANCELLED; - - WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL); - - return OPERATOR_FINISHED; -} - -/* function used for WM_OT_save_mainfile too */ -static bool blend_save_check(bContext *UNUSED(C), wmOperator *op) -{ - char filepath[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filepath); - if (!BLO_has_bfile_extension(filepath)) { - /* some users would prefer BLI_replace_extension(), - * we keep getting nitpicking bug reports about this - campbell */ - BLI_ensure_extension(filepath, FILE_MAX, ".blend"); - RNA_string_set(op->ptr, "filepath", filepath); - return true; - } - return false; -} - -static void WM_OT_save_as_mainfile(wmOperatorType *ot) -{ - PropertyRNA *prop; - - ot->name = "Save As Blender File"; - ot->idname = "WM_OT_save_as_mainfile"; - ot->description = "Save the current file in the desired location"; - - ot->invoke = wm_save_as_mainfile_invoke; - ot->exec = wm_save_as_mainfile_exec; - ot->check = blend_save_check; - /* omit window poll so this can work in background mode */ - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); - RNA_def_boolean(ot->srna, "relative_remap", true, "Remap Relative", - "Remap relative paths when saving in a different directory"); - prop = RNA_def_boolean(ot->srna, "copy", false, "Save Copy", - "Save a copy of the actual working state but does not make saved file active"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -#ifdef USE_BMESH_SAVE_AS_COMPAT - RNA_def_boolean(ot->srna, "use_mesh_compat", false, "Legacy Mesh Format", - "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will " - "be lost (no implicit triangulation)"); -#endif -} - -/* *************** save file directly ******** */ - -static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - int ret; - - /* cancel if no active window */ - if (CTX_wm_window(C) == NULL) - return OPERATOR_CANCELLED; - - save_set_compress(op); - save_set_filepath(op); - - /* if we're saving for the first time and prefer relative paths - any existing paths will be absolute, - * enable the option to remap paths to avoid confusion [#37240] */ - if ((G.relbase_valid == false) && (U.flag & USER_RELPATHS)) { - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(op->ptr, prop, true); - } - } - - if (G.save_over) { - char path[FILE_MAX]; - - RNA_string_get(op->ptr, "filepath", path); - if (BLI_exists(path)) { - ret = WM_operator_confirm_message_ex(C, op, IFACE_("Save Over?"), ICON_QUESTION, path); - } - else { - ret = wm_save_as_mainfile_exec(C, op); - } - } - else { - WM_event_add_fileselect(C, op); - ret = OPERATOR_RUNNING_MODAL; - } - - return ret; -} - -static void WM_OT_save_mainfile(wmOperatorType *ot) -{ - ot->name = "Save Blender File"; - ot->idname = "WM_OT_save_mainfile"; - ot->description = "Save the current Blender file"; - - ot->invoke = wm_save_mainfile_invoke; - ot->exec = wm_save_as_mainfile_exec; - ot->check = blend_save_check; - /* omit window poll so this can work in background mode */ - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); - RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative", - "Remap relative paths when saving in a different directory"); -} - static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot) { ot->name = "Toggle Window Fullscreen"; diff --git a/source/blender/windowmanager/wm_cursors.h b/source/blender/windowmanager/wm_cursors.h index e99f80f53cd..c695a12f52c 100644 --- a/source/blender/windowmanager/wm_cursors.h +++ b/source/blender/windowmanager/wm_cursors.h @@ -111,7 +111,7 @@ enum { struct wmWindow; struct wmEvent; -int wm_cursor_arrow_move(struct wmWindow *win, struct wmEvent *event); +bool wm_cursor_arrow_move(struct wmWindow *win, const struct wmEvent *event); #endif /* __WM_CURSORS_H__ */ diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 4b35f662a99..2eae9cdb012 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -31,15 +31,33 @@ #ifndef __WM_FILES_H__ #define __WM_FILES_H__ +struct wmOperatorType; + +/* wm_files.c */ void wm_history_file_read(void); -int wm_history_file_read_exec(bContext *C, wmOperator *op); -int wm_file_write(struct bContext *C, const char *target, int fileflags, struct ReportList *reports); -int wm_homefile_read_exec(struct bContext *C, struct wmOperator *op); int wm_homefile_read(struct bContext *C, struct ReportList *reports, bool from_memory, const char *filepath); -int wm_homefile_write_exec(struct bContext *C, struct wmOperator *op); -int wm_userpref_write_exec(struct bContext *C, struct wmOperator *op); void wm_file_read_report(bContext *C); +void WM_OT_save_homefile(struct wmOperatorType *ot); +void WM_OT_userpref_autoexec_path_add(struct wmOperatorType *ot); +void WM_OT_userpref_autoexec_path_remove(struct wmOperatorType *ot); +void WM_OT_save_userpref(struct wmOperatorType *ot); +void WM_OT_read_history(struct wmOperatorType *ot); +void WM_OT_read_homefile(struct wmOperatorType *ot); +void WM_OT_read_factory_settings(struct wmOperatorType *ot); + +void WM_OT_open_mainfile(struct wmOperatorType *ot); + +void WM_OT_revert_mainfile(struct wmOperatorType *ot); +void WM_OT_recover_last_session(struct wmOperatorType *ot); +void WM_OT_recover_auto_save(struct wmOperatorType *ot); + +void WM_OT_save_as_mainfile(struct wmOperatorType *ot); +void WM_OT_save_mainfile(struct wmOperatorType *ot); + +/* wm_files_link.c */ +void WM_OT_link(struct wmOperatorType *ot); +void WM_OT_append(struct wmOperatorType *ot); #endif /* __WM_FILES_H__ */ |