Merge branch 'master' into blender2.8
authorBastien Montagne <montagne29@wanadoo.fr>
Mon, 10 Apr 2017 14:53:12 +0000 (16:53 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Mon, 10 Apr 2017 14:53:12 +0000 (16:53 +0200)
Conflicts:
source/blender/editors/gpencil/drawgpencil.c

build_files/build_environment/install_deps.sh
intern/cycles/device/opencl/opencl_util.cpp
intern/cycles/kernel/geom/geom_curve.h
intern/cycles/kernel/geom/geom_motion_curve.h
intern/cycles/kernel/kernel_compat_cpu.h
source/blender/blenkernel/intern/idprop.c
source/blender/editors/gpencil/drawgpencil.c
source/blender/editors/space_clip/clip_utils.c
source/blender/python/generic/idprop_py_api.c
tests/python/bl_pyapi_idprop.py

index 479585656d1cd0f53d9d153aa0e380a00d969449..b10ae4e7a4aab0094c7bc530f3426877a46528b5 100755 (executable)
@@ -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
index 6dca642f3f37df854c7bb030a7337271046e20f3..fe1c65a2224d3c2217a8e16f6d0af6f29fd96a20 100644 (file)
@@ -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);
index bb33b91847eb0e29ab900e2d669e0e941e5c1919..8888000f0e65740d5af83f7b9ef31f4f826024a0 100644 (file)
@@ -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);
index dc1388b66439357b0aa865e35fee5a02d0392f40..119bdb2f15c2e5e1ad7289b7f869e4a7d7fd7de2 100644 (file)
@@ -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.
  */
index cad5f4d295938884fc71014330cb2099911748b2..21da180bb8efdec846e9562bf88881907a35483e 100644 (file)
@@ -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:
index e51c47d7b197690276fec4f48b2ace0271a39858..7c79e13cb01c7f4b88f02e4fd1881eebf6471d63 100644 (file)
@@ -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:
index 19c717b4ecb788c0f14dac0dc0f19dc8d249be23..11881a4a019d3e938064bc417c96a30c94bed7c0 100644 (file)
@@ -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);
                }
index 462be829a23e93faf14a6639a696b05561efb524..272d99c2b0e11e0505bfbf4ea2cec7ac6adc810d 100644 (file)
@@ -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);
        }
 
index 2e15b7b14132bf2e8831207e67f9371a56c2eb97..0a9931f2683e4abf2beb2c586e62077cc140e109 100644 (file)
@@ -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; */
index 0a9cb044571c9cfebf8a3060efa877b46e16b977..7bf68c16cc72966068de391b355084f12dd7acaa 100644 (file)
@@ -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 [])