kuuko pushed a commit to branch master.

http://git.enlightenment.org/bindings/python/python-efl.git/commit/?id=b05187b52e8d825fafa514afa7ad22afd7482ae8

commit b05187b52e8d825fafa514afa7ad22afd7482ae8
Author: Kai Huuhko <kai.huu...@gmail.com>
Date:   Thu Nov 7 11:30:02 2013 +0200

    Evas.Image: Update to new Python buffer API, fix doc issues.
    
    Needs testing.
---
 doc/evas/class-object-image.rst    |   6 +
 efl/evas/efl.evas_object_image.pxi | 240 ++++++++++++++++++++++++-------------
 2 files changed, 163 insertions(+), 83 deletions(-)

diff --git a/doc/evas/class-object-image.rst b/doc/evas/class-object-image.rst
index 47e7aaa..b30cbd5 100644
--- a/doc/evas/class-object-image.rst
+++ b/doc/evas/class-object-image.rst
@@ -2,3 +2,9 @@
 =============================
 
 .. autoclass:: efl.evas.Image
+
+
+:class:`efl.evas.FilledImage` Class
+===================================
+
+.. autoclass:: efl.evas.FilledImage
diff --git a/efl/evas/efl.evas_object_image.pxi 
b/efl/evas/efl.evas_object_image.pxi
index 684a3d0..f698db9 100644
--- a/efl/evas/efl.evas_object_image.pxi
+++ b/efl/evas/efl.evas_object_image.pxi
@@ -15,16 +15,14 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with this Python-EFL.  If not, see <http://www.gnu.org/licenses/>.
 
-
-# TODO: remove me after usage is update to new buffer api
 cdef extern from "Python.h":
-    int PyObject_AsReadBuffer(obj, void **buffer, Py_ssize_t *buffer_len) 
except -1
+    PyObject * PyMemoryView_FromBuffer(Py_buffer *info)
+
+from cpython.buffer cimport Py_buffer, PyObject_CheckBuffer, \
+    PyObject_GetBuffer, PyBuffer_Release, PyBUF_SIMPLE
 
+from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
 
-# TODO needed? neem like the frong place to define fill/rotation stuff...
-# def image_mask_fill(Image source, Image mask, Image surface, int x_mask, int 
y_mask, int x_surface, int y_surface):
-#     evas_object_image_mask_fill(source.obj, mask.obj, surface.obj,
-#                                 x_mask, y_mask, x_surface, y_surface)
 
 cdef int _data_size_get(Evas_Object *obj):
     cdef int stride, h, bpp, cspace, have_alpha
@@ -52,38 +50,38 @@ cdef class Image(Object):
 
     .. rubric:: Introduction
 
-    Image will consider the object's :py:func:`geometry<geometry_set()>`
-    as the area to paint with tiles as described by :py:func:`fill_set()` and 
the
+    Image will consider the object's :py:attr:`geometry`
+    as the area to paint with tiles as described by :py:attr:`fill` and the
     real pixels (image data) will be stored as described by
-    :py:func:`image_size<image_size_set()>`. This can be tricky to understand 
at
+    :py:attr:`image_size`. This can be tricky to understand at
     first, but gives flexibility to do everything.
 
     If an image is loaded from file, it will have
-    :py:func:`image_size<image_size_set()>` set to its original size, unless 
some
-    other size was set with :py:func:`load_size_set()`, 
:py:func:`load_dpi_set()` or
-    :py:func:`load_scale_down_set()`.
+    :py:attr:`image_size` set to its original size, unless some
+    other size was set with :py:attr:`load_size`, :py:attr:`load_dpi` or
+    :py:attr:`load_scale_down`.
 
-    Pixels will be scaled to match size specified by :py:func:`fill_set()`
+    Pixels will be scaled to match size specified by :py:attr:`fill`
     using either sampled or smooth methods, these can be specified with
-    :py:func:`smooth_scale_set()`. The scale will consider borders as 
specified by
-    :py:func:`border_set()` and :py:func:`border_center_fill_set()`, while the 
former specify
+    :py:attr:`smooth_scale`. The scale will consider borders as specified by
+    :py:attr:`border` and :py:attr:`border_center_fill`, while the former 
specify
     the border dimensions (top and bottom will scale horizontally, while
     right and left will do vertically, corners are kept unscaled), the latter
     says whenever the center of the image will be used (useful to create
     frames).
 
-    Contents will be tiled using :py::`fill_set()` information in order to 
paint
-    :py:func:`geometry<Object.geometry_set()>`, so if you want an image to be 
drawn
-    just once, you should match every :py:func:`geometry_set(x, y, w, h)` by a 
call
-    to :py:func:`fill_set(0, 0, w, h)`. :py:class:`FilledImage` does that for 
you.
+    Contents will be tiled using :py:attr:`fill` information in order to paint
+    :py:attr:`geometry<Object.geometry>`, so if you want an image to be drawn
+    just once, you should match every :py:attr:`geometry` = **x, y, w, h** by 
a call
+    to :py:attr:`fill` = **0, 0, w, h**. :py:class:`FilledImage` does that for 
you.
 
     .. rubric:: Pixel data and buffer API
 
     Images implement the Python Buffer API, so it's possible to use it
     where buffers are expected (ie: file.write()). Available data will
-    depend on :py:func:`alpha<alpha_set()>`, 
:py:func:`colorspace<colorspace_set()>` and
-    :py:func:`image_size<image_size_set()>`, lines should be considered 
multiple
-    of :py:func:`stride<stride_get()>`, with the following considerations about
+    depend on :py:attr:`alpha`, :py:attr:`colorspace` and
+    :py:attr:`image_size`, lines should be considered multiple
+    of :py:attr:`stride`, with the following considerations about
     colorspace:
 
     - **EVAS_COLORSPACE_ARGB8888:** This pixel format is a linear block of
@@ -101,7 +99,8 @@ cdef class Image(Object):
         So 50% transparent blue will be: 0x80000080. This will not be "dark" -
         just 50% transparent. Values are 0 == black, 255 == solid or full
         red, green or blue.
-    - **EVAS_COLORSPACE_RGB565_A5P:** In the process of being implemented in
+
+    .. **EVAS_COLORSPACE_RGB565_A5P:** In the process of being implemented in
         1 engine only. This may change. This is a pointer to image data for
         16-bit half-word pixel data in 16bpp RGB 565 format (5 bits red,
         6 bits green, 5 bits blue), with the high-byte containing red and the
@@ -473,23 +472,125 @@ cdef class Image(Object):
         :type buf: data
 
         """
-        cdef const_void *p_data
-        cdef Py_ssize_t size, expected_size
+        cdef:
+            Py_ssize_t expected_size
+            Py_buffer view
 
         if buf is None:
             evas_object_image_data_set(self.obj, NULL)
             return
 
-        # TODO: update to new buffer api
-        PyObject_AsReadBuffer(buf, &p_data, &size)
-        if p_data != NULL:
-            expected_size = _data_size_get(self.obj)
-            if size < expected_size:
-                raise ValueError(("buffer size (%d) is smalled than expected "
-                                  "(%d)!") % (size, expected_size))
-        evas_object_image_data_set(self.obj,<void *> p_data)
+        if not PyObject_CheckBuffer(buf):
+            raise TypeError("The provided object does not support buffer 
interface.")
+
+        PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE)
+
+        expected_size = _data_size_get(self.obj)
+        if view.itemsize < expected_size:
+            raise ValueError(
+                "buffer size (%d) is smaller than expected (%d)!" % (
+                    view.itemsize, expected_size
+                    )
+                )
+
+        evas_object_image_data_set(self.obj, <void *>view.buf)
+
+        PyBuffer_Release(&view)
+
+    def image_data_memoryview_get(self, bint for_writing=False, bint 
simple=True):
+        """image_data_memoryview_get(bool for_writing) -> MemoryView
+
+        Get a MemoryView object to the raw image data of the given image 
object.
+
+        :param bool for_writing: Whether the data being retrieved will be
+                modified or not.
+        :param bool simple: Whether the MemoryView is 1D or 2D
+        :return MemoryView: The raw image data.
+
+        This method returns a MemoryView object to an image object's internal 
pixel
+        buffer, for reading only or read/write. If you request it for
+        writing, the image will be marked dirty so that it gets redrawn at
+        the next update.
+
+        Each time you call this method on an image object, its data
+        buffer will have an internal reference counter
+        incremented. Decrement it back by using
+        :py:func:`image_data_set`.
+
+        This is best suited for when you want to modify an existing image,
+        without changing its dimensions.
+
+        .. note::
+            The contents' format returned by it depend on the color
+            space of the given image object.
+
+        .. note::
+            You may want to use :py:func:`image_data_update_add` to
+            inform data changes, if you did any.
+
+        """
+        cdef int stride, h, bpp, cspace, have_alpha, img_size
+
+        stride = evas_object_image_stride_get(self.obj)
+        evas_object_image_size_get(self.obj, NULL, &h)
+        cspace = evas_object_image_colorspace_get(self.obj)
+        have_alpha = evas_object_image_alpha_get(self.obj)
+
+        bpp = 0
+        if cspace == EVAS_COLORSPACE_ARGB8888:
+            bpp = 4
+            format = "L"
+        elif cspace == EVAS_COLORSPACE_RGB565_A5P:
+            if have_alpha == 0:
+                bpp = 2
+                format = "H"
+            else:
+                pass #bpp = 3
+                # XXX: There's no type that has three bytes.
+                #      Is the format string actually used?
+        if bpp == 0:
+            raise ValueError("Unsupported colorspace")
+
+        img_size = stride * h * bpp
+
+        cdef Py_buffer *img_buf = <Py_buffer *>PyMem_Malloc(sizeof(Py_buffer))
+        if img_buf == NULL:
+            raise MemoryError
+
+        cdef:
+            Py_ssize_t simple_shape[1]
+            Py_ssize_t shape[2]
+            Py_ssize_t strides[2]
+            Py_ssize_t suboffsets[2]
+
+        if simple:
+            simple_shape[0] = img_size
+        else:
+            shape[0] = stride / bpp
+            shape[1] = h
+            strides[0] = stride
+            strides[1] = h * bpp
+            suboffsets[0] = -1
+            suboffsets[1] = -1
+
+        img_buf.buf = evas_object_image_data_get(self.obj, for_writing)
+        img_buf.len = img_size
+        img_buf.readonly = not for_writing
+        img_buf.format = format
+        if simple:
+            img_buf.ndim = 1
+            img_buf.shape = simple_shape
+            img_buf.strides = NULL
+            img_buf.suboffsets = NULL
+        else:
+            img_buf.ndim = 2
+            img_buf.shape = shape
+            img_buf.strides = strides
+            img_buf.suboffsets = suboffsets
+        img_buf.itemsize = bpp
+
+        return <object>PyMemoryView_FromBuffer(img_buf)
 
-    # TODO: def image_data_get(self):
 
     # TODO:
     # def image_data_convert(self, to_cspace):
@@ -814,13 +915,16 @@ cdef class Image(Object):
 
         - **EVAS_COLORSPACE_ARGB8888** ARGB 32 bits per pixel, high-byte is
             Alpha, accessed 1 32bit word at a time.
-        - **EVAS_COLORSPACE_YCBCR422P601_PL** YCbCr 4:2:2 Planar, ITU.BT-601
+
+        .. **EVAS_COLORSPACE_YCBCR422P601_PL** YCbCr 4:2:2 Planar, ITU.BT-601
             specifications. The data poitned to is just an array of row
             pointer, pointing to the Y rows, then the Cb, then Cr rows.
-        - **EVAS_COLORSPACE_YCBCR422P709_PL** YCbCr 4:2:2 Planar, ITU.BT-709
+
+        .. **EVAS_COLORSPACE_YCBCR422P709_PL** YCbCr 4:2:2 Planar, ITU.BT-709
             specifications. The data poitned to is just an array of row
             pointer, pointing to the Y rows, then the Cb, then Cr rows.
-        - **EVAS_COLORSPACE_RGB565_A5P** 16bit rgb565 + Alpha plane at end -
+
+        .. **EVAS_COLORSPACE_RGB565_A5P** 16bit rgb565 + Alpha plane at end -
             5 bits of the 8 being used per alpha byte.
 
         :type: Evas_Colorspace
@@ -952,7 +1056,7 @@ cdef class Image(Object):
     def alpha_mask_set(self, bint ismask):
         evas_object_image_alpha_mask_set(self.obj, ismask)
 
-    property image_source:
+    property source:
         """The source object on an image object to used as a **proxy**.
 
         If an image object is set to behave as a **proxy**, it will mirror
@@ -1193,50 +1297,20 @@ cdef class Image(Object):
         evas_object_image_animated_frame_set(self.obj, frame_num)
 
 
-
-    def __getsegcount__(self, Py_ssize_t *p_len):
-        if p_len == NULL:
-            return 1
-
-        p_len[0] = _data_size_get(self.obj)
-        return 1
-
-    def __getreadbuffer__(self, int segment, void **ptr):
-        ptr[0] = evas_object_image_data_get(self.obj, 0)
-        if ptr[0] == NULL:
-            raise SystemError("image has no allocated buffer.")
-        # XXX: keep Evas pixels_checked_out counter to 0 and allow
-        # XXX: image to reload and unload its data.
-        # XXX: may cause problems if buffer is used after these
-        # XXX: functions are called, but buffers aren't expected to
-        # XXX: live much.
-        evas_object_image_data_set(self.obj, ptr[0])
-        return _data_size_get(self.obj)
-
-    def __getwritebuffer__(self, int segment, void **ptr):
-        ptr[0] = evas_object_image_data_get(self.obj, 1)
-        if ptr[0] == NULL:
-            raise SystemError("image has no allocated buffer.")
-        # XXX: keep Evas pixels_checked_out counter to 0 and allow
-        # XXX: image to reload and unload its data.
-        # XXX: may cause problems if buffer is used after these
-        # XXX: functions are called, but buffers aren't expected to
-        # XXX: live much.
-        evas_object_image_data_set(self.obj, ptr[0])
-        return _data_size_get(self.obj)
-
-    def __getcharbuffer__(self, int segment, char **ptr):
-        ptr[0] = <char *>evas_object_image_data_get(self.obj, 0)
-        if ptr[0] == NULL:
+    def __getbuffer__(self, Py_buffer *view, int flags):
+        view.buf = evas_object_image_data_get(self.obj, not view.readonly)
+        if view.buf == NULL:
             raise SystemError("image has no allocated buffer.")
-        # XXX: keep Evas pixels_checked_out counter to 0 and allow
-        # XXX: image to reload and unload its data.
-        # XXX: may cause problems if buffer is used after these
-        # XXX: functions are called, but buffers aren't expected to
-        # XXX: live much.
-        evas_object_image_data_set(self.obj, ptr[0])
-        return _data_size_get(self.obj)
+        view.len = _data_size_get(self.obj)
+        view.format = "L"
+        view.ndim = 1
+        view.itemsize = 4
+        # TODO: Check flags, provide multidim if possible, handle other
+        #       colorspaces
 
+    def __releasebuffer__(self, Py_buffer *view):
+        evas_object_image_data_set(self.obj, view.buf)
+        # TODO: Free possibly allocated memory here (shape, strides, 
suboffsets)
 
 
     def on_image_preloaded_add(self, func, *a, **k):
@@ -1273,8 +1347,8 @@ cdef class FilledImage(Image):
 
     Image that automatically resize it's contents to fit object size.
 
-    This :py::`Image` subclass already calls :py::`Image.fill_set()` on resize 
so
-    it will match and so be scaled to fill the whole area.
+    This :py:class:`Image` subclass already calls :py:func:`Image.fill_set`
+    on resize so it will match and so be scaled to fill the whole area.
 
     :param canvas: The evas canvas for this object
     :type canvas: :py:class:`Canvas`

-- 


Reply via email to