diff --git a/pyopencl/c_wrapper/wrap_cl_core.h b/pyopencl/c_wrapper/wrap_cl_core.h
index 1a211fe5b376d8210a680738b3e2ba74ea300077..52bb18759b64fe84e30225f710758c9dbdbb2563 100644
--- a/pyopencl/c_wrapper/wrap_cl_core.h
+++ b/pyopencl/c_wrapper/wrap_cl_core.h
@@ -74,7 +74,6 @@ error *memory_map__release(clobj_t _map, clobj_t _queue,
                            const clobj_t *_wait_for, uint32_t num_wait_for,
                            clobj_t *evt);
 void *memory_map__data(clobj_t _map);
-size_t memory_map__size(clobj_t _map);
 // Program
 error *create_program_with_source(clobj_t *program, clobj_t context,
                                   const char *src);
@@ -174,6 +173,13 @@ error *enqueue_write_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
                            size_t slice_pitch, const clobj_t *_wait_for,
                            uint32_t num_wait_for, int is_blocking,
                            void *pyobj);
+error *enqueue_map_image(clobj_t *_evt, clobj_t *map, clobj_t _queue,
+                         clobj_t _mem, cl_map_flags flags,
+                         const size_t *_origin, size_t origin_l,
+                         const size_t *_region, size_t region_l,
+                         size_t *row_pitch, size_t *slice_pitch,
+                         const clobj_t *_wait_for, uint32_t num_wait_for,
+                         int block);
 // CL Object
 intptr_t clobj__int_ptr(clobj_t obj);
 error *clobj__get_info(clobj_t obj, cl_uint param, generic_info *out);
diff --git a/pyopencl/cffi_cl.py b/pyopencl/cffi_cl.py
index 250de9aeca64a55e3946a7a721ab5bf807936a53..486ab73513df4bacd216610d990039b4af830dc7 100644
--- a/pyopencl/cffi_cl.py
+++ b/pyopencl/cffi_cl.py
@@ -468,6 +468,14 @@ class MemoryMap(_Common):
             self.ptr, queue.ptr if queue is not None else _ffi.NULL,
             c_wait_for, num_wait_for, _event))
         return _create_instance(Event, _event[0])
+    def _init_array(self, shape, typestr, strides):
+        self.__array_interface__ = {
+            'shape': shape,
+            'typestr': typestr,
+            'strides': strides,
+            'data': (int(_lib.clobj__int_ptr(self.ptr)), False),
+            'version': 3
+        }
 
 def _c_buffer_from_obj(obj, writable=False):
     """Convert a Python object to a tuple (cdata('void *'), num_bytes, dummy)
@@ -880,13 +888,7 @@ def enqueue_map_buffer(queue, buf, flags, offset, shape, dtype,
                                           num_wait_for, bool(is_blocking)))
     event = _create_instance(Event, _event[0])
     map = _create_instance(MemoryMap, _map[0])
-    map.__array_interface__ = {
-        'shape': shape,
-        'typestr': dtype.str,
-        'strides': strides,
-        'data': (int(_lib.clobj__int_ptr(_map[0])), False),
-        'version': 3
-        }
+    map._init_array(shape, dtype.str, strides)
     return np.asarray(map), event
 
 def _enqueue_fill_buffer(queue, mem, pattern, offset, size, wait_for=None):
@@ -937,14 +939,32 @@ def _enqueue_write_image(queue, mem, origin, region, hostbuf, row_pitch=0,
         bool(is_blocking), _ffi.new_handle(c_buf)))
     return _create_instance(NannyEvent, _event[0])
 
+def enqueue_map_image(queue, img, flags, origin, region, shape, dtype,
+                      order="C", strides=None, wait_for=None, is_blocking=True):
+    origin_l = len(origin)
+    region_l = len(region)
+    if origin_l > 3 or region_l > 3:
+        raise RuntimeError("origin or region has too many components",
+                           "enqueue_read_image")
+    _event = _ffi.new('clobj_t*')
+    _map = _ffi.new('clobj_t*')
+    _row_pitch = _ffi.new('size_t*')
+    _slice_pitch = _ffi.new('size_t*')
+    c_wait_for, num_wait_for = _clobj_list(wait_for)
+    _handle_error(_lib.enqueue_map_image(_event, _map, queue.ptr, img.ptr,
+                                         flags, origin, origin_l, region,
+                                         region_l, _row_pitch, _slice_pitch,
+                                         c_wait_for, num_wait_for, is_blocking))
+    event = _create_instance(Event, _event[0])
+    map = _create_instance(MemoryMap, _map[0])
+    map._init_array(shape, dtype.str, strides)
+    return np.asarray(map), event, _row_pitch[0], _slice_pitch[0]
+
 # TODO: copy_image fill_image
 #    copy_buffer_to_image copy_image_to_buffer
 
 # }}}
 
-# TODO
-#   enqueue_map_image
-
 # {{{ gl interop
 
 def have_gl():
diff --git a/src/c_wrapper/utils.h b/src/c_wrapper/utils.h
index 57bdad7909080a254f95ac94cd7ad78d6becbb77..052453789a01df7f1a9e4ed30359633537138030 100644
--- a/src/c_wrapper/utils.h
+++ b/src/c_wrapper/utils.h
@@ -69,6 +69,26 @@ public:
 
 namespace pyopencl {
 
+template<typename T, size_t n>
+class ConstBuffer {
+private:
+    T m_intern_buf[n];
+    const T *m_buf;
+public:
+    ConstBuffer(const T *buf, size_t l)
+        : m_buf(buf)
+    {
+        if (l < n) {
+            memcpy(m_intern_buf, buf, sizeof(T) * std::min(l, n));
+            m_buf = m_intern_buf;
+        }
+    }
+    operator const T*()
+    {
+        return m_buf;
+    }
+};
+
 template<typename T>
 static inline cl_bool
 cast_bool(const T &v)
diff --git a/src/c_wrapper/wrap_cl.cpp b/src/c_wrapper/wrap_cl.cpp
index bd6e969fc155922af5f4b206c7ceb05fe3e0a145..23b82e78ad99d24df7a4090e3e87b00bcb12b9da 100644
--- a/src/c_wrapper/wrap_cl.cpp
+++ b/src/c_wrapper/wrap_cl.cpp
@@ -1514,11 +1514,9 @@ private:
     command_queue m_queue;
     memory_object m_mem;
     void *m_ptr;
-    size_t m_size;
 public:
-    memory_map(const command_queue *queue, const memory_object *mem,
-               void *ptr, size_t size)
-        : m_valid(true), m_queue(*queue), m_mem(*mem), m_ptr(ptr), m_size(size)
+    memory_map(const command_queue *queue, const memory_object *mem, void *ptr)
+        : m_valid(true), m_queue(*queue), m_mem(*mem), m_ptr(ptr)
     {}
     ~memory_map()
     {
@@ -1558,11 +1556,6 @@ public:
     {
         return m_valid ? m_ptr : NULL;
     }
-    size_t
-    size() const
-    {
-        return m_valid ? m_size : 0;
-    }
 };
 
 // }}}
@@ -2073,12 +2066,6 @@ memory_map__data(clobj_t _map)
     return static_cast<memory_map*>(_map)->data();
 }
 
-size_t
-memory_map__size(clobj_t _map)
-{
-    return static_cast<memory_map*>(_map)->size();
-}
-
 // Program
 error*
 create_program_with_source(clobj_t *prog, clobj_t _ctx, const char *src)
@@ -2412,6 +2399,24 @@ enqueue_barrier(clobj_t _queue)
         });
 }
 
+static memory_map*
+_convert_memory_map(clobj_t *_evt, cl_event evt, command_queue *queue,
+                    memory_object *buf, void *res)
+{
+    try {
+        *_evt = new event(evt, false);
+        evt = 0;
+        return new memory_map(queue, buf, res);
+    } catch (...) {
+        if (evt) {
+            pyopencl_call_guarded_cleanup(clReleaseEvent, evt);
+        }
+        pyopencl_call_guarded_cleanup(clEnqueueUnmapMemObject, queue->data(),
+                                      buf->data(), res, 0, NULL, NULL);
+        throw;
+    }
+}
+
 // {{{ enqueue_*_buffer*
 
 error*
@@ -2505,19 +2510,7 @@ enqueue_map_buffer(clobj_t *_evt, clobj_t *map, clobj_t _queue, clobj_t _mem,
                         cast_bool(block), flags, offset, size, num_wait_for,
                         wait_for.get(), &evt);
                 });
-            try {
-                *_evt = new event(evt, false);
-                evt = 0;
-                *map = new memory_map(queue, buf, res, size);
-            } catch (...) {
-                if (evt) {
-                    pyopencl_call_guarded_cleanup(clReleaseEvent, evt);
-                }
-                pyopencl_call_guarded_cleanup(clEnqueueUnmapMemObject,
-                                              queue->data(), buf->data(),
-                                              res, 0, NULL, NULL);
-                throw;
-            }
+            *map = _convert_memory_map(_evt, evt, queue, buf, res);
         });
 }
 
@@ -2547,8 +2540,8 @@ enqueue_fill_buffer(clobj_t *_evt, clobj_t _queue, clobj_t _mem, void *pattern,
 
 error*
 enqueue_read_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
-                   const size_t *origin, size_t origin_l,
-                   const size_t *region, size_t region_l, void *buffer,
+                   const size_t *_origin, size_t origin_l,
+                   const size_t *_region, size_t region_l, void *buffer,
                    size_t row_pitch, size_t slice_pitch,
                    const clobj_t *_wait_for, uint32_t num_wait_for,
                    int is_blocking, void *pyobj)
@@ -2556,16 +2549,8 @@ enqueue_read_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
     auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
     auto queue = static_cast<command_queue*>(_queue);
     auto img = static_cast<image*>(_mem);
-    size_t _origin[3] = {0};
-    if (origin_l < 3) {
-        memcpy(_origin, origin, sizeof(size_t) * origin_l);
-        origin = _origin;
-    }
-    size_t _region[3] = {0};
-    if (region_l < 3) {
-        memcpy(_region, region, sizeof(size_t) * region_l);
-        region = _region;
-    }
+    ConstBuffer<size_t, 3> origin(_origin, origin_l);
+    ConstBuffer<size_t, 3> region(_region, region_l);
     return c_handle_error([&] {
             cl_event evt;
             retry_mem_error<void>([&] {
@@ -2581,8 +2566,8 @@ enqueue_read_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
 
 error*
 enqueue_write_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
-                    const size_t *origin, size_t origin_l,
-                    const size_t *region, size_t region_l,
+                    const size_t *_origin, size_t origin_l,
+                    const size_t *_region, size_t region_l,
                     const void *buffer, size_t row_pitch, size_t slice_pitch,
                     const clobj_t *_wait_for, uint32_t num_wait_for,
                     int is_blocking, void *pyobj)
@@ -2590,16 +2575,8 @@ enqueue_write_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
     auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
     auto queue = static_cast<command_queue*>(_queue);
     auto img = static_cast<image*>(_mem);
-    size_t _origin[3] = {0};
-    if (origin_l < 3) {
-        memcpy(_origin, origin, sizeof(size_t) * origin_l);
-        origin = _origin;
-    }
-    size_t _region[3] = {0};
-    if (region_l < 3) {
-        memcpy(_region, region, sizeof(size_t) * region_l);
-        region = _region;
-    }
+    ConstBuffer<size_t, 3> origin(_origin, origin_l);
+    ConstBuffer<size_t, 3> region(_region, region_l);
     return c_handle_error([&] {
             cl_event evt;
             retry_mem_error<void>([&] {
@@ -2613,6 +2590,30 @@ enqueue_write_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
         });
 }
 
+error*
+enqueue_map_image(clobj_t *_evt, clobj_t *map, clobj_t _queue, clobj_t _mem,
+                  cl_map_flags flags, const size_t *_origin, size_t origin_l,
+                  const size_t *_region, size_t region_l, size_t *row_pitch,
+                  size_t *slice_pitch, const clobj_t *_wait_for,
+                  uint32_t num_wait_for, int block)
+{
+    auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    auto queue = static_cast<command_queue*>(_queue);
+    auto img = static_cast<image*>(_mem);
+    ConstBuffer<size_t, 3> origin(_origin, origin_l);
+    ConstBuffer<size_t, 3> region(_region, region_l);
+    return c_handle_error([&] {
+            cl_event evt;
+            void *res = retry_mem_error<void*>([&] {
+                    return pyopencl_call_guarded(
+                        clEnqueueMapImage, queue->data(), img->data(),
+                        cast_bool(block), flags, origin, region, row_pitch,
+                        slice_pitch, num_wait_for, wait_for.get(), &evt);
+                });
+            *map = _convert_memory_map(_evt, evt, queue, img, res);
+        });
+}
+
 // }}}
 
 intptr_t