Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extern/curve_fit_nd/intern/curve_fit_corners_detect.c8
-rw-r--r--extern/libopenjpeg/bio.c8
-rw-r--r--extern/libopenjpeg/cidx_manager.c2
-rw-r--r--extern/libopenjpeg/cio.c12
-rw-r--r--extern/libopenjpeg/cio.h9
-rw-r--r--extern/libopenjpeg/event.c9
-rw-r--r--extern/libopenjpeg/image.c3
-rw-r--r--extern/libopenjpeg/j2k.c207
-rw-r--r--extern/libopenjpeg/jp2.c265
-rw-r--r--extern/libopenjpeg/openjpeg.h4
-rw-r--r--extern/libopenjpeg/opj_config.h3
-rw-r--r--extern/libopenjpeg/opj_includes.h11
-rw-r--r--extern/libopenjpeg/opj_malloc.h4
-rw-r--r--extern/libopenjpeg/patches/fbsd.patch13
-rw-r--r--extern/libopenjpeg/patches/osx.patch17
-rw-r--r--extern/libopenjpeg/t1.c1
-rw-r--r--extern/libopenjpeg/t2.c52
-rw-r--r--extern/libopenjpeg/tcd.c119
-rw-r--r--intern/cycles/blender/addon/properties.py22
-rw-r--r--intern/cycles/blender/addon/ui.py36
-rw-r--r--intern/cycles/blender/blender_mesh.cpp12
-rw-r--r--intern/cycles/blender/blender_python.cpp19
-rw-r--r--intern/cycles/blender/blender_sync.cpp39
-rw-r--r--intern/cycles/blender/blender_sync.h3
-rw-r--r--intern/cycles/bvh/bvh_binning.cpp2
-rw-r--r--intern/cycles/bvh/bvh_sort.cpp156
-rw-r--r--intern/cycles/device/device_cuda.cpp17
-rw-r--r--intern/cycles/kernel/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/kernel_compat_cpu.h6
-rw-r--r--intern/cycles/kernel/kernel_math.h1
-rw-r--r--intern/cycles/kernel/kernel_types.h2
-rw-r--r--intern/cycles/kernel/kernel_volume.h17
-rw-r--r--intern/cycles/kernel/svm/svm_image.h15
-rw-r--r--intern/cycles/render/image.cpp58
-rw-r--r--intern/cycles/render/image.h32
-rw-r--r--intern/cycles/render/scene.cpp5
-rw-r--r--intern/cycles/render/scene.h7
-rw-r--r--intern/cycles/render/session.cpp7
-rw-r--r--intern/cycles/subd/subd_dice.cpp39
-rw-r--r--intern/cycles/subd/subd_dice.h2
-rw-r--r--intern/cycles/subd/subd_split.cpp197
-rw-r--r--intern/cycles/util/CMakeLists.txt1
-rw-r--r--intern/cycles/util/util_texture.h53
-rw-r--r--intern/moto/include/MT_Vector3.h1
-rw-r--r--intern/moto/include/MT_Vector4.h1
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_smart_project.py12
-rw-r--r--release/scripts/startup/bl_ui/space_image.py5
-rw-r--r--release/scripts/startup/bl_ui/space_nla.py1
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py1
-rw-r--r--release/scripts/startup/bl_ui/space_time.py6
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py4
-rw-r--r--source/blender/blenkernel/intern/image.c12
-rw-r--r--source/blender/blenlib/BLI_math_vector.h1
-rw-r--r--source/blender/blenlib/intern/math_vector.c13
-rw-r--r--source/blender/blenlib/intern/scanfill.c13
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_dissolve.c168
-rw-r--r--source/blender/editors/animation/drivers.c3
-rw-r--r--source/blender/editors/curve/editcurve_paint.c30
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c4
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/interface/interface_handlers.c4
-rw-r--r--source/blender/editors/interface/interface_regions.c22
-rw-r--r--source/blender/editors/interface/interface_widgets.c9
-rw-r--r--source/blender/editors/screen/area.c10
-rw-r--r--source/blender/editors/screen/screen_edit.c14
-rw-r--r--source/blender/editors/space_action/action_edit.c24
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c57
-rw-r--r--source/blender/editors/space_graph/graph_edit.c23
-rw-r--r--source/blender/editors/space_image/image_ops.c2
-rw-r--r--source/blender/editors/space_nla/nla_edit.c24
-rw-r--r--source/blender/editors/space_nla/nla_intern.h1
-rw-r--r--source/blender/editors/space_nla/nla_ops.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c24
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_ops.c2
-rw-r--r--source/blender/editors/space_text/space_text.c10
-rw-r--r--source/blender/editors/space_time/time_ops.c28
-rw-r--r--source/blender/editors/space_view3d/drawobject.c250
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c10
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c15
-rw-r--r--source/blender/windowmanager/CMakeLists.txt1
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c2
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c1
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c18
-rw-r--r--source/blender/windowmanager/intern/wm_files.c833
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c497
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c1027
-rw-r--r--source/blender/windowmanager/wm_cursors.h2
-rw-r--r--source/blender/windowmanager/wm_files.h28
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,
&current_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__ */