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:
authorBastien Montagne <montagne29@wanadoo.fr>2017-04-10 17:53:12 +0300
committerBastien Montagne <montagne29@wanadoo.fr>2017-04-10 17:53:12 +0300
commit346964eb3f3c3074d30676f27ed477ce542299ea (patch)
tree24112aba81885de0ccb4e21030df67635bae819c
parent39451ac7128ef753da24ae74486c51a8a40e2719 (diff)
parentfd203a09330ce88725e8c5c8d9b1f9ce7b2f3c10 (diff)
Merge branch 'master' into blender2.8
Conflicts: source/blender/editors/gpencil/drawgpencil.c
-rwxr-xr-xbuild_files/build_environment/install_deps.sh6
-rw-r--r--intern/cycles/device/opencl/opencl_util.cpp7
-rw-r--r--intern/cycles/kernel/geom/geom_curve.h4
-rw-r--r--intern/cycles/kernel/geom/geom_motion_curve.h2
-rw-r--r--intern/cycles/kernel/kernel_compat_cpu.h42
-rw-r--r--source/blender/blenkernel/intern/idprop.c2
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c17
-rw-r--r--source/blender/editors/space_clip/clip_utils.c5
-rw-r--r--source/blender/python/generic/idprop_py_api.c419
-rw-r--r--tests/python/bl_pyapi_idprop.py62
10 files changed, 401 insertions, 165 deletions
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 479585656d1..b10ae4e7a4a 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -2262,7 +2262,9 @@ compile_ALEMBIC() {
fi
if [ -d $INST/boost ]; then
- cmake_d="$cmake_d -D BOOST_ROOT=$INST/boost"
+ if [ -d $INST/boost ]; then
+ cmake_d="$cmake_d -D BOOST_ROOT=$INST/boost"
+ fi
cmake_d="$cmake_d -D USE_STATIC_BOOST=ON"
else
cmake_d="$cmake_d -D USE_STATIC_BOOST=OFF"
@@ -2770,7 +2772,7 @@ install_DEB() {
boost_version=$(echo `get_package_version_DEB libboost-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
- install_packages_DEB libboost-{filesystem,iostreams,locale,regex,system,thread,wave}$boost_version-dev
+ install_packages_DEB libboost-{filesystem,iostreams,locale,regex,system,thread,wave,program-options}$boost_version-dev
clean_Boost
else
compile_Boost
diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp
index 6dca642f3f3..fe1c65a2224 100644
--- a/intern/cycles/device/opencl/opencl_util.cpp
+++ b/intern/cycles/device/opencl/opencl_util.cpp
@@ -1058,13 +1058,16 @@ cl_device_type OpenCLInfo::get_device_type(cl_device_id device_id)
string OpenCLInfo::get_readable_device_name(cl_device_id device_id)
{
char board_name[1024];
+ size_t length = 0;
if(clGetDeviceInfo(device_id,
CL_DEVICE_BOARD_NAME_AMD,
sizeof(board_name),
&board_name,
- NULL) == CL_SUCCESS)
+ &length) == CL_SUCCESS)
{
- return board_name;
+ if(length != 0 && board_name[0] != '\0') {
+ return board_name;
+ }
}
/* Fallback to standard device name API. */
return get_device_name(device_id);
diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h
index bb33b91847e..8888000f0e6 100644
--- a/intern/cycles/kernel/geom/geom_curve.h
+++ b/intern/cycles/kernel/geom/geom_curve.h
@@ -270,7 +270,7 @@ ccl_device_curveintersect bool bvh_cardinal_curve_intersect(KernelGlobals *kg, I
int ka = max(k0 - 1, v00.x);
int kb = min(k1 + 1, v00.x + v00.y - 1);
-#if defined(__KERNEL_AVX2__) && (!defined(_MSC_VER) || _MSC_VER > 1800)
+#if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__) && (!defined(_MSC_VER) || _MSC_VER > 1800)
avxf P_curve_0_1, P_curve_2_3;
if(is_curve_primitive) {
P_curve_0_1 = _mm256_loadu2_m128(&kg->__curve_keys.data[k0].x, &kg->__curve_keys.data[ka].x);
@@ -305,7 +305,7 @@ ccl_device_curveintersect bool bvh_cardinal_curve_intersect(KernelGlobals *kg, I
ssef htfm1 = shuffle<1, 0, 1, 3>(load1f_first(extract<0>(d_ss)), vdir0);
ssef htfm2 = shuffle<1, 3, 2, 3>(mul_shuf, vdir0);
-#if defined(__KERNEL_AVX2__) && (!defined(_MSC_VER) || _MSC_VER > 1800)
+#if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__) && (!defined(_MSC_VER) || _MSC_VER > 1800)
const avxf vPP = _mm256_broadcast_ps(&P.m128);
const avxf htfm00 = avxf(htfm0.m128, htfm0.m128);
const avxf htfm11 = avxf(htfm1.m128, htfm1.m128);
diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/geom_motion_curve.h
index dc1388b6643..119bdb2f15c 100644
--- a/intern/cycles/kernel/geom/geom_motion_curve.h
+++ b/intern/cycles/kernel/geom/geom_motion_curve.h
@@ -152,7 +152,7 @@ ccl_device_inline void motion_cardinal_curve_keys(KernelGlobals *kg,
keys[3] = (1.0f - t)*keys[3] + t*next_keys[3];
}
-#ifdef __KERNEL_AVX2__
+#if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__)
/* Similar to above, but returns keys as pair of two AVX registers with each
* holding two float4.
*/
diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h
index cad5f4d2959..21da180bb8e 100644
--- a/intern/cycles/kernel/kernel_compat_cpu.h
+++ b/intern/cycles/kernel/kernel_compat_cpu.h
@@ -353,7 +353,7 @@ template<typename T> struct texture_image {
{
int ix, iy, iz;
int nix, niy, niz;
-
+
float tx = frac(x*(float)width - 0.5f, &ix);
float ty = frac(y*(float)height - 0.5f, &iy);
float tz = frac(z*(float)depth - 0.5f, &iz);
@@ -404,7 +404,18 @@ template<typename T> struct texture_image {
return r;
}
- ccl_never_inline float4 interp_3d_ex_tricubic(float x, float y, float z)
+ /* TODO(sergey): For some unspeakable reason both GCC-6 and Clang-3.9 are
+ * causing stack overflow issue in this function unless it is inlined.
+ *
+ * Only happens for AVX2 kernel and global __KERNEL_SSE__ vectorization
+ * enabled.
+ */
+#ifdef __GNUC__
+ ccl_always_inline
+#else
+ ccl_never_inline
+#endif
+ float4 interp_3d_ex_tricubic(float x, float y, float z)
{
int ix, iy, iz;
int nix, niy, niz;
@@ -463,13 +474,13 @@ template<typename T> struct texture_image {
const int xc[4] = {pix, ix, nix, nnix};
const int yc[4] = {width * piy,
- width * iy,
- width * niy,
- width * nniy};
+ width * iy,
+ width * niy,
+ width * nniy};
const int zc[4] = {width * height * piz,
- width * height * iz,
- width * height * niz,
- width * height * nniz};
+ width * height * iz,
+ width * height * niz,
+ width * height * nniz};
float u[4], v[4], w[4];
/* Some helper macro to keep code reasonable size,
@@ -478,14 +489,14 @@ template<typename T> struct texture_image {
#define DATA(x, y, z) (read(data[xc[x] + yc[y] + zc[z]]))
#define COL_TERM(col, row) \
(v[col] * (u[0] * DATA(0, col, row) + \
- u[1] * DATA(1, col, row) + \
- u[2] * DATA(2, col, row) + \
- u[3] * DATA(3, col, row)))
+ u[1] * DATA(1, col, row) + \
+ u[2] * DATA(2, col, row) + \
+ u[3] * DATA(3, col, row)))
#define ROW_TERM(row) \
(w[row] * (COL_TERM(0, row) + \
- COL_TERM(1, row) + \
- COL_TERM(2, row) + \
- COL_TERM(3, row)))
+ COL_TERM(1, row) + \
+ COL_TERM(2, row) + \
+ COL_TERM(3, row)))
SET_CUBIC_SPLINE_WEIGHTS(u, tx);
SET_CUBIC_SPLINE_WEIGHTS(v, ty);
@@ -502,11 +513,10 @@ template<typename T> struct texture_image {
ccl_always_inline float4 interp_3d_ex(float x, float y, float z,
int interpolation = INTERPOLATION_LINEAR)
{
-
if(UNLIKELY(!data))
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
- switch(interpolation) {
+ switch(interpolation) {
case INTERPOLATION_CLOSEST:
return interp_3d_ex_closest(x, y, z);
case INTERPOLATION_LINEAR:
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index e51c47d7b19..7c79e13cb01 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -891,7 +891,7 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
*(float *)&prop->data.val = val->f;
break;
case IDP_DOUBLE:
- prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
+ prop = MEM_callocN(sizeof(IDProperty), "IDProperty double");
*(double *)&prop->data.val = val->d;
break;
case IDP_ARRAY:
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 19c717b4ecb..11881a4a019 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -731,7 +731,8 @@ static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thi
*/
{
const bGPDspoint *pt1, *pt2;
- float pm[2];
+ float s0[2], s1[2]; /* segment 'center' points */
+ float pm[2]; /* normal from previous segment. */
int i;
float fpt[3];
@@ -743,17 +744,17 @@ static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thi
#ifdef WITH_GL_PROFILE_COMPAT
immBegin(PRIM_QUADS_XXX, (totpoints - 2) * 4 + 12);
+ /* get x and y coordinates from first point */
+ mul_v3_m4v3(fpt, diff_mat, &points->x);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0);
+
for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) {
- float s0[2], s1[2]; /* segment 'center' points */
float t0[2], t1[2]; /* tessellated coordinates */
float m1[2], m2[2]; /* gradient and normal */
float mt[2], sc[2]; /* gradient for thickness, point for end-cap */
float pthick; /* thickness at segment point */
- /* get x and y coordinates from points */
- mul_v3_m4v3(fpt, diff_mat, &pt1->x);
- gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0);
-
+ /* get x and y coordinates from point2 (point1 has already been computed in previous iteration). */
mul_v3_m4v3(fpt, diff_mat, &pt2->x);
gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1);
@@ -879,7 +880,9 @@ static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thi
immVertex2fv(pos, t1);
immVertex2fv(pos, t0);
}
-
+
+ /* store computed point2 coordinates as point1 ones of next segment. */
+ copy_v2_v2(s0, s1);
/* store stroke's 'natural' normal for next stroke to use */
copy_v2_v2(pm, m2);
}
diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c
index 462be829a23..272d99c2b0e 100644
--- a/source/blender/editors/space_clip/clip_utils.c
+++ b/source/blender/editors/space_clip/clip_utils.c
@@ -71,7 +71,7 @@ void clip_graph_tracking_values_iterate_track(
BKE_movieclip_get_size(clip, &sc->user, &width, &height);
for (coord = 0; coord < 2; coord++) {
- int i, prevfra = 0;
+ int i, prevfra = track->markers[0].framenr;
bool open = false;
float prevval = 0.0f;
@@ -184,6 +184,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track)
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
bool has_bundle = false;
char track_name_escaped[MAX_NAME], prefix[MAX_NAME * 2];
+ const bool used_for_stabilization = (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT));
if (track == act_track)
tracking->act_track = NULL;
@@ -205,7 +206,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track)
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
- if (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT)) {
+ if (used_for_stabilization) {
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
}
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 2e15b7b1413..0a9931f2683 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -335,19 +335,9 @@ static char idp_sequence_type(PyObject *seq_fast)
return type;
}
-/**
- * \note group can be a pointer array or a group.
- * assume we already checked key is a string.
- *
- * \return success.
- */
-bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob)
+static const char *idp_try_read_name(PyObject *name_obj)
{
- IDProperty *prop = NULL;
- IDPropertyTemplate val = {0};
-
- const char *name;
-
+ const char *name = NULL;
if (name_obj) {
Py_ssize_t name_size;
name = _PyUnicode_AsStringAndSize(name_obj, &name_size);
@@ -356,168 +346,297 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group,
PyErr_Format(PyExc_KeyError,
"invalid id-property key, expected a string, not a %.200s",
Py_TYPE(name_obj)->tp_name);
- return false;
+ return NULL;
}
if (name_size > MAX_IDPROP_NAME) {
PyErr_SetString(PyExc_KeyError, "the length of IDProperty names is limited to 63 characters");
- return false;
+ return NULL;
}
}
else {
name = "";
}
+ return name;
+}
- if (PyFloat_Check(ob)) {
- val.d = PyFloat_AsDouble(ob);
- prop = IDP_New(IDP_DOUBLE, &val, name);
- }
- else if (PyLong_Check(ob)) {
- val.i = _PyLong_AsInt(ob);
- if (val.i == -1 && PyErr_Occurred()) {
- return false;
- }
- prop = IDP_New(IDP_INT, &val, name);
+/* -------------------------------------------------------------------------- */
+
+/**
+ * The 'idp_from_Py*' functions expect that the input type has been checked before
+ * and return NULL if the IDProperty can't be created.
+ */
+
+static IDProperty *idp_from_PyFloat(const char *name, PyObject *ob)
+{
+ IDPropertyTemplate val = {0};
+ val.d = PyFloat_AsDouble(ob);
+ return IDP_New(IDP_DOUBLE, &val, name);
+}
+
+static IDProperty *idp_from_PyLong(const char *name, PyObject *ob)
+{
+ IDPropertyTemplate val = {0};
+ val.i = _PyLong_AsInt(ob);
+ if (val.i == -1 && PyErr_Occurred()) {
+ return NULL;
}
- else if (PyUnicode_Check(ob)) {
+ return IDP_New(IDP_INT, &val, name);
+}
+
+static IDProperty *idp_from_PyUnicode(const char *name, PyObject *ob)
+{
+ IDProperty *prop;
+ IDPropertyTemplate val = {0};
#ifdef USE_STRING_COERCE
- Py_ssize_t value_size;
- PyObject *value_coerce = NULL;
- val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce);
- val.string.len = (int)value_size + 1;
- val.string.subtype = IDP_STRING_SUB_UTF8;
- prop = IDP_New(IDP_STRING, &val, name);
- Py_XDECREF(value_coerce);
+ Py_ssize_t value_size;
+ PyObject *value_coerce = NULL;
+ val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce);
+ val.string.len = (int)value_size + 1;
+ val.string.subtype = IDP_STRING_SUB_UTF8;
+ prop = IDP_New(IDP_STRING, &val, name);
+ Py_XDECREF(value_coerce);
#else
- val.str = _PyUnicode_AsString(ob);
- prop = IDP_New(IDP_STRING, val, name);
+ val.str = _PyUnicode_AsString(ob);
+ prop = IDP_New(IDP_STRING, val, name);
#endif
- }
- else if (PyBytes_Check(ob)) {
- val.string.str = PyBytes_AS_STRING(ob);
- val.string.len = PyBytes_GET_SIZE(ob);
- val.string.subtype = IDP_STRING_SUB_BYTE;
+ return prop;
+}
- prop = IDP_New(IDP_STRING, &val, name);
- //prop = IDP_NewString(PyBytes_AS_STRING(ob), name, PyBytes_GET_SIZE(ob));
- //prop->subtype = IDP_STRING_SUB_BYTE;
+static IDProperty *idp_from_PyBytes(const char *name, PyObject *ob)
+{
+ IDPropertyTemplate val = {0};
+ val.string.str = PyBytes_AS_STRING(ob);
+ val.string.len = PyBytes_GET_SIZE(ob);
+ val.string.subtype = IDP_STRING_SUB_BYTE;
+ return IDP_New(IDP_STRING, &val, name);
+}
+
+static int idp_array_type_from_format_char(char format)
+{
+ if (format == 'i') return IDP_INT;
+ if (format == 'f') return IDP_FLOAT;
+ if (format == 'd') return IDP_DOUBLE;
+ return -1;
+}
+
+static const char *idp_format_from_array_type(int type)
+{
+ if (type == IDP_INT) return "i";
+ if (type == IDP_FLOAT) return "f";
+ if (type == IDP_DOUBLE) return "d";
+ return NULL;
+}
+
+static IDProperty *idp_from_PySequence_Buffer(const char *name, Py_buffer *buffer)
+{
+ IDProperty *prop;
+ IDPropertyTemplate val = {0};
+
+ int format = idp_array_type_from_format_char(*buffer->format);
+ if (format == -1) {
+ /* should never happen as the type has been checked before */
+ return NULL;
}
- else if (PySequence_Check(ob)) {
- PyObject *ob_seq_fast;
- PyObject **ob_seq_fast_items;
- PyObject *item;
- int i;
+ else {
+ val.array.type = format;
+ val.array.len = buffer->len / buffer->itemsize;
+ }
+ prop = IDP_New(IDP_ARRAY, &val, name);
+ memcpy(IDP_Array(prop), buffer->buf, buffer->len);
+ return prop;
+}
- if (!(ob_seq_fast = PySequence_Fast(ob, "py -> idprop"))) {
- return false;
- }
+static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob)
+{
+ IDProperty *prop;
+ IDPropertyTemplate val = {0};
- ob_seq_fast_items = PySequence_Fast_ITEMS(ob_seq_fast);
+ PyObject **ob_seq_fast_items;
+ PyObject *item;
+ int i;
- if ((val.array.type = idp_sequence_type(ob_seq_fast)) == (char)-1) {
- Py_DECREF(ob_seq_fast);
- PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays");
- return false;
- }
+ ob_seq_fast_items = PySequence_Fast_ITEMS(ob);
- /* validate sequence and derive type.
- * we assume IDP_INT unless we hit a float
- * number; then we assume it's */
+ if ((val.array.type = idp_sequence_type(ob)) == (char)-1) {
+ PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays");
+ return NULL;
+ }
- val.array.len = PySequence_Fast_GET_SIZE(ob_seq_fast);
+ /* validate sequence and derive type.
+ * we assume IDP_INT unless we hit a float
+ * number; then we assume it's */
- switch (val.array.type) {
- case IDP_DOUBLE:
- {
- double *prop_data;
-
- prop = IDP_New(IDP_ARRAY, &val, name);
- prop_data = IDP_Array(prop);
- for (i = 0; i < val.array.len; i++) {
- item = ob_seq_fast_items[i];
- if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) {
- Py_DECREF(ob_seq_fast);
- return false;
- }
+ val.array.len = PySequence_Fast_GET_SIZE(ob);
+
+ switch (val.array.type) {
+ case IDP_DOUBLE:
+ {
+ double *prop_data;
+ prop = IDP_New(IDP_ARRAY, &val, name);
+ prop_data = IDP_Array(prop);
+ for (i = 0; i < val.array.len; i++) {
+ item = ob_seq_fast_items[i];
+ if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) {
+ return NULL;
}
- break;
}
- case IDP_INT:
- {
- int *prop_data;
- prop = IDP_New(IDP_ARRAY, &val, name);
- prop_data = IDP_Array(prop);
- for (i = 0; i < val.array.len; i++) {
- item = ob_seq_fast_items[i];
- if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) {
- Py_DECREF(ob_seq_fast);
- return false;
- }
+ break;
+ }
+ case IDP_INT:
+ {
+ int *prop_data;
+ prop = IDP_New(IDP_ARRAY, &val, name);
+ prop_data = IDP_Array(prop);
+ for (i = 0; i < val.array.len; i++) {
+ item = ob_seq_fast_items[i];
+ if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) {
+ return NULL;
}
- break;
}
- case IDP_IDPARRAY:
- {
- prop = IDP_NewIDPArray(name);
- for (i = 0; i < val.array.len; i++) {
- item = ob_seq_fast_items[i];
-
- if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) {
- Py_DECREF(ob_seq_fast);
- return false;
- }
+ break;
+ }
+ case IDP_IDPARRAY:
+ {
+ prop = IDP_NewIDPArray(name);
+ for (i = 0; i < val.array.len; i++) {
+ item = ob_seq_fast_items[i];
+ if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) {
+ return NULL;
}
- break;
}
- default:
- /* should never happen */
- Py_DECREF(ob_seq_fast);
- PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type");
- return false;
+ break;
+ }
+ default:
+ /* should never happen */
+ PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type");
+ return NULL;
+ }
+ return prop;
+}
+
+
+static IDProperty *idp_from_PySequence(const char *name, PyObject *ob)
+{
+ Py_buffer buffer;
+ bool use_buffer = false;
+
+ if (PyObject_CheckBuffer(ob)) {
+ PyObject_GetBuffer(ob, &buffer, PyBUF_SIMPLE | PyBUF_FORMAT);
+ char format = *buffer.format;
+ if (ELEM(format, 'i', 'f', 'd')) {
+ use_buffer = true;
}
+ else {
+ PyBuffer_Release(&buffer);
+ }
+ }
- Py_DECREF(ob_seq_fast);
+ if (use_buffer) {
+ IDProperty *prop = idp_from_PySequence_Buffer(name, &buffer);
+ PyBuffer_Release(&buffer);
+ return prop;
}
- else if (PyMapping_Check(ob)) {
- PyObject *keys, *vals, *key, *pval;
- int i, len;
- /*yay! we get into recursive stuff now!*/
- keys = PyMapping_Keys(ob);
- vals = PyMapping_Values(ob);
-
- /* we allocate the group first; if we hit any invalid data,
- * we can delete it easily enough.*/
- prop = IDP_New(IDP_GROUP, &val, name);
- len = PyMapping_Length(ob);
- for (i = 0; i < len; i++) {
- key = PySequence_GetItem(keys, i);
- pval = PySequence_GetItem(vals, i);
- if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) {
- IDP_FreeProperty(prop);
- MEM_freeN(prop);
- Py_XDECREF(keys);
- Py_XDECREF(vals);
- Py_XDECREF(key);
- Py_XDECREF(pval);
- /* error is already set */
- return false;
- }
+ else {
+ PyObject *ob_seq_fast = PySequence_Fast(ob, "py -> idprop");
+ if (ob_seq_fast != NULL) {
+ IDProperty *prop = idp_from_PySequence_Fast(name, ob_seq_fast);
+ Py_DECREF(ob_seq_fast);
+ return prop;
+ }
+ else {
+ return NULL;
+ }
+ }
+}
+
+static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob)
+{
+ IDProperty *prop;
+ IDPropertyTemplate val = {0};
+
+ PyObject *keys, *vals, *key, *pval;
+ int i, len;
+ /* yay! we get into recursive stuff now! */
+ keys = PyMapping_Keys(ob);
+ vals = PyMapping_Values(ob);
+
+ /* we allocate the group first; if we hit any invalid data,
+ * we can delete it easily enough.*/
+ prop = IDP_New(IDP_GROUP, &val, name);
+ len = PyMapping_Length(ob);
+ for (i = 0; i < len; i++) {
+ key = PySequence_GetItem(keys, i);
+ pval = PySequence_GetItem(vals, i);
+ if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) {
+ IDP_FreeProperty(prop);
+ MEM_freeN(prop);
+ Py_XDECREF(keys);
+ Py_XDECREF(vals);
Py_XDECREF(key);
Py_XDECREF(pval);
+ /* error is already set */
+ return NULL;
}
- Py_XDECREF(keys);
- Py_XDECREF(vals);
+ Py_XDECREF(key);
+ Py_XDECREF(pval);
+ }
+ Py_XDECREF(keys);
+ Py_XDECREF(vals);
+ return prop;
+}
+
+static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob)
+{
+ const char *name = idp_try_read_name(name_obj);
+ if (name == NULL) {
+ return NULL;
+ }
+
+ if (PyFloat_Check(ob)) {
+ return idp_from_PyFloat(name, ob);
+ }
+ else if (PyLong_Check(ob)) {
+ return idp_from_PyLong(name, ob);
+ }
+ else if (PyUnicode_Check(ob)) {
+ return idp_from_PyUnicode(name, ob);
+ }
+ else if (PyBytes_Check(ob)) {
+ return idp_from_PyBytes(name, ob);
+ }
+ else if (PySequence_Check(ob)) {
+ return idp_from_PySequence(name, ob);
+ }
+ else if (PyMapping_Check(ob)) {
+ return idp_from_PyMapping(name, ob);
}
else {
PyErr_Format(PyExc_TypeError,
"invalid id-property type %.200s not supported",
Py_TYPE(ob)->tp_name);
+ return NULL;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * \note group can be a pointer array or a group.
+ * assume we already checked key is a string.
+ *
+ * \return success.
+ */
+bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob)
+{
+ IDProperty *prop = idp_from_PyObject(name_obj, ob);
+ if (prop == NULL) {
return false;
}
if (group->type == IDP_IDPARRAY) {
IDP_AppendArray(group, prop);
- // IDP_FreeProperty(item); /* IDP_AppendArray does a shallow copy (memcpy), only free memory */
+ /* IDP_AppendArray does a shallow copy (memcpy), only free memory */
MEM_freeN(prop);
}
else {
@@ -1371,6 +1490,44 @@ static PyMappingMethods BPy_IDArray_AsMapping = {
(objobjargproc)BPy_IDArray_ass_subscript
};
+static int itemsize_by_idarray_type(int array_type)
+{
+ if (array_type == IDP_INT) return sizeof(int);
+ if (array_type == IDP_FLOAT) return sizeof(float);
+ if (array_type == IDP_DOUBLE) return sizeof(double);
+ return -1; /* should never happen */
+}
+
+static int BPy_IDArray_getbuffer(BPy_IDArray *self, Py_buffer *view, int flags)
+{
+ IDProperty *prop = self->prop;
+ int itemsize = itemsize_by_idarray_type(prop->subtype);
+ int length = itemsize * prop->len;
+
+ if (PyBuffer_FillInfo(view, (PyObject *)self, IDP_Array(prop), length, false, flags) == -1) {
+ return -1;
+ }
+
+ view->itemsize = itemsize;
+ view->format = (char *)idp_format_from_array_type(prop->subtype);
+
+ Py_ssize_t *shape = MEM_mallocN(sizeof(Py_ssize_t), __func__);
+ shape[0] = prop->len;
+ view->shape = shape;
+
+ return 0;
+}
+
+static void BPy_IDArray_releasebuffer(BPy_IDArray *UNUSED(self), Py_buffer *view)
+{
+ MEM_freeN(view->shape);
+}
+
+static PyBufferProcs BPy_IDArray_Buffer = {
+ (getbufferproc)BPy_IDArray_getbuffer,
+ (releasebufferproc)BPy_IDArray_releasebuffer,
+};
+
PyTypeObject BPy_IDArray_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
@@ -1403,7 +1560,7 @@ PyTypeObject BPy_IDArray_Type = {
NULL, /* setattrofunc tp_setattro; */
/* Functions to access object as input/output buffer */
- NULL, /* PyBufferProcs *tp_as_buffer; */
+ &BPy_IDArray_Buffer, /* PyBufferProcs *tp_as_buffer; */
/*** Flags to define presence of optional/expanded features ***/
Py_TPFLAGS_DEFAULT, /* long tp_flags; */
diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py
index 0a9cb044571..7bf68c16cc7 100644
--- a/tests/python/bl_pyapi_idprop.py
+++ b/tests/python/bl_pyapi_idprop.py
@@ -3,6 +3,7 @@
# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop.py -- --verbose
import bpy
import unittest
+import numpy as np
from array import array
@@ -75,7 +76,7 @@ class TestIdPropertyCreation(TestHelper, unittest.TestCase):
mylist = [1.2, 3.4, 5.6]
self.id["a"] = array("f", mylist)
self.assertAlmostEqualSeq(self.id["a"].to_list(), mylist)
- self.assertEqual(self.id["a"].typecode, "d")
+ self.assertEqual(self.id["a"].typecode, "f")
def test_sequence_double_array(self):
mylist = [1.2, 3.4, 5.6]
@@ -138,6 +139,65 @@ class TestIdPropertyCreation(TestHelper, unittest.TestCase):
self.id["a"] = self
+class TestBufferProtocol(TestHelper, unittest.TestCase):
+
+ def test_int(self):
+ self.id["a"] = array("i", [1, 2, 3, 4, 5])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ self.assertEqual(len(a), 5)
+ a[2] = 10
+ self.assertEqual(self.id["a"].to_list(), [1, 2, 10, 4, 5])
+
+ def test_float(self):
+ self.id["a"] = array("f", [1.0, 2.0, 3.0, 4.0])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ self.assertEqual(len(a), 4)
+ a[-1] = 10
+ self.assertEqual(self.id["a"].to_list(), [1.0, 2.0, 3.0, 10.0])
+
+ def test_double(self):
+ self.id["a"] = array("d", [1.0, 2.0, 3.0, 4.0])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ a[1] = 10
+ self.assertEqual(self.id["a"].to_list(), [1.0, 10.0, 3.0, 4.0])
+
+ def test_full_update(self):
+ self.id["a"] = array("i", [1, 2, 3, 4, 5, 6])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ a[:] = [10, 20, 30, 40, 50, 60]
+ self.assertEqual(self.id["a"].to_list(), [10, 20, 30, 40, 50, 60])
+
+ def test_partial_update(self):
+ self.id["a"] = array("i", [1, 2, 3, 4, 5, 6, 7, 8])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ a[1:5] = [10, 20, 30, 40]
+ self.assertEqual(self.id["a"].to_list(), [1, 10, 20, 30, 40, 6, 7, 8])
+
+ def test_copy(self):
+ self.id["a"] = array("i", [1, 2, 3, 4, 5])
+ self.id["b"] = self.id["a"]
+ self.assertEqual(self.id["a"].to_list(), self.id["b"].to_list())
+
+ def test_memview_attributes(self):
+ mylist = [1, 2, 3]
+ self.id["a"] = mylist
+
+ view1 = memoryview(self.id["a"])
+ view2 = memoryview(array("i", mylist))
+
+ self.assertEqualMemviews(view1, view2)
+
+ def assertEqualMemviews(self, view1, view2):
+ props_to_compare = (
+ "contiguous", "format", "itemsize", "nbytes", "ndim",
+ "readonly", "shape", "strides", "suboffsets"
+ )
+ for attr in props_to_compare:
+ self.assertEqual(getattr(view1, attr), getattr(view2, attr))
+
+ self.assertEqual(list(view1), list(view2))
+ self.assertEqual(view1.tobytes(), view2.tobytes())
+
if __name__ == '__main__':
import sys
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])