From cc4dfd53af769ffea853e728b3a155531ca09f4f Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 8 Sep 2014 11:24:05 +0200 Subject: [PATCH 1/2] imageio: add possibility to load image from a buffer, and make the loader GIL-free --- kivy/core/image/img_imageio.pyx | 116 ++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 36 deletions(-) diff --git a/kivy/core/image/img_imageio.pyx b/kivy/core/image/img_imageio.pyx index 1c6dfb12f..24f0eed38 100644 --- a/kivy/core/image/img_imageio.pyx +++ b/kivy/core/image/img_imageio.pyx @@ -27,10 +27,10 @@ ctypedef signed long CFIndex cdef unsigned int kCFStringEncodingUTF8 = 0x08000100 -cdef extern from "stdlib.h": +cdef extern from "stdlib.h" nogil: void* calloc(size_t, size_t) -cdef extern from "CoreGraphics/CGDataProvider.h": +cdef extern from "CoreGraphics/CGDataProvider.h" nogil: ctypedef void *CFDataRef unsigned char *CFDataGetBytePtr(CFDataRef) @@ -48,7 +48,7 @@ cdef extern from "CoreGraphics/CGDataProvider.h": CGRect CGRectMake(float, float, float, float) -cdef extern from "CoreFoundation/CFBase.h": +cdef extern from "CoreFoundation/CFBase.h" nogil: ctypedef void *CFAllocatorRef ctypedef void *CFStringRef ctypedef void *CFURLRef @@ -58,7 +58,7 @@ cdef extern from "CoreFoundation/CFBase.h": void CFRelease(CFTypeRef cf) -cdef extern from "CoreFoundation/CFURL.h": +cdef extern from "CoreFoundation/CFURL.h" nogil: ctypedef void *CFURLRef ctypedef int CFURLPathStyle int kCFURLPOSIXPathStyle @@ -70,7 +70,11 @@ cdef extern from "CoreFoundation/CFURL.h": cdef extern from "CoreFoundation/CFDictionary.h": ctypedef void *CFDictionaryRef -cdef extern from "CoreGraphics/CGImage.h": +cdef extern from "CoreFoundation/CoreFoundation.h" nogil: + CFDataRef CFDataCreateWithBytesNoCopy( + CFAllocatorRef, char *, int length, CFAllocatorRef) + +cdef extern from "CoreGraphics/CGImage.h" nogil: ctypedef void *CGImageRef void CGImageRelease(CGImageRef image) size_t CGImageGetWidth(CGImageRef) @@ -83,16 +87,16 @@ cdef extern from "CoreGraphics/CGImage.h": int kCGImageAlphaPremultipliedFirst int kCGBitmapByteOrder32Host -cdef extern from "CoreGraphics/CGColorSpace.h": +cdef extern from "CoreGraphics/CGColorSpace.h" nogil: ctypedef void *CGColorSpaceRef CGColorSpaceRef CGColorSpaceCreateDeviceRGB() void CGColorSpaceRelease(CGColorSpaceRef cs) -cdef extern from "CoreGraphics/CGAffineTransform.h": +cdef extern from "CoreGraphics/CGAffineTransform.h" nogil: ctypedef void *CGAffineTransform CGAffineTransform CGAffineTransformMake(float a, float b, float c, float d, float tx, float ty) -cdef extern from "CoreGraphics/CGContext.h": +cdef extern from "CoreGraphics/CGContext.h" nogil: ctypedef void *CGContextRef void CGContextRelease(CGContextRef c) void CGContextDrawImage(CGContextRef, CGRect, CGImageRef) @@ -101,20 +105,22 @@ cdef extern from "CoreGraphics/CGContext.h": void CGContextSetBlendMode(CGContextRef, int) void CGContextConcatCTM(CGContextRef fc, CGAffineTransform matrix) -cdef extern from "CoreGraphics/CGBitmapContext.h": +cdef extern from "CoreGraphics/CGBitmapContext.h" nogil: CGImageRef CGBitmapContextCreateImage(CGColorSpaceRef) CGContextRef CGBitmapContextCreate( void *data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef colorspace, unsigned int bitmapInfo) -cdef extern from "ImageIO/CGImageSource.h": +cdef extern from "ImageIO/CGImageSource.h" nogil: ctypedef void *CGImageSourceRef CGImageSourceRef CGImageSourceCreateWithURL( CFURLRef, CFDictionaryRef) CGImageRef CGImageSourceCreateImageAtIndex( CGImageSourceRef, size_t, CFDictionaryRef) + CGImageRef CGImageSourceCreateWithData( + CFDataRef data, CFDictionaryRef options) -cdef extern from "ImageIO/CGImageDestination.h": +cdef extern from "ImageIO/CGImageDestination.h" nogil: ctypedef void *CGImageDestinationRef CGImageDestinationRef CGImageDestinationCreateWithURL( CFURLRef, CFStringRef, size_t, CFDictionaryRef) @@ -122,7 +128,7 @@ cdef extern from "ImageIO/CGImageDestination.h": CGImageRef image, CFDictionaryRef properties) int CGImageDestinationFinalize(CGImageDestinationRef idst) -cdef extern from "Accelerate/Accelerate.h": +cdef extern from "Accelerate/Accelerate.h" nogil: ctypedef struct vImage_Buffer: void *data int width @@ -134,32 +140,62 @@ cdef extern from "Accelerate/Accelerate.h": int flags) -def load_image_data(bytes _url): +def load_image_data(bytes _url, bytes _data=None): + cdef size_t width, height + cdef char *r_data = NULL + cdef size_t datalen = 0 + cdef char *c_url = NULL + cdef char *c_data = NULL + if _url: + c_url = _url + datalen = len(_url) + if _data: + c_data = _data + datalen = len(_data) + c_load_image_data(c_url, c_data, datalen, &width, &height, &r_data) + if r_data == NULL: + raise ValueError("No image to load at {}".format(_url)) + py_data = r_data[:width * height * 4] + free(r_data) + return (width, height, 'rgba', py_data) + + +cdef void c_load_image_data(char *_url, char *_data, size_t datalen, size_t *width, size_t + *height, char **r_data) nogil: # load an image from the _url with CoreGraphics, and output an RGBA string. cdef CFURLRef url - url = CFURLCreateFromFileSystemRepresentation(NULL, _url, len(_url), 0) - cdef CGImageSourceRef myImageSourceRef = CGImageSourceCreateWithURL(url, NULL) - if not myImageSourceRef: - CFRelease(url) - raise ValueError('No image to load at %r' % _url) - cdef CGImageRef myImageRef = CGImageSourceCreateImageAtIndex (myImageSourceRef, 0, NULL) - cdef size_t width = CGImageGetWidth(myImageRef) - cdef size_t height = CGImageGetHeight(myImageRef) - cdef CGRect rect = CGRectMake(0, 0, width, height) + cdef CGImageSourceRef myImageSourceRef + cdef CFDataRef dataref + + width[0] = height[0] = 0 + r_data[0] = NULL + + if _data != NULL: + dataref = CFDataCreateWithBytesNoCopy(NULL, _data, datalen, NULL) + myImageSourceRef = CGImageSourceCreateWithData(dataref, NULL) + else: + url = CFURLCreateFromFileSystemRepresentation(NULL, _url, datalen, 0) + myImageSourceRef = CGImageSourceCreateWithURL(url, NULL) + if not myImageSourceRef: + CFRelease(url) + return + cdef CGImageRef myImageRef = CGImageSourceCreateImageAtIndex(myImageSourceRef, 0, NULL) + width[0] = CGImageGetWidth(myImageRef) + height[0] = CGImageGetHeight(myImageRef) + cdef CGRect rect = CGRectMake(0, 0, width[0], height[0]) cdef CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB() cdef vImage_Buffer src cdef vImage_Buffer dest - dest.height = src.height = height - dest.width = src.width = width - dest.rowBytes = src.rowBytes = width * 4 - src.data = calloc(width * 4, height) - dest.data = calloc(width * 4, height) - + dest.height = src.height = height[0] + dest.width = src.width = width[0] + dest.rowBytes = src.rowBytes = width[0] * 4 + src.data = calloc(width[0] * 4, height[0]) + dest.data = r_data[0] = calloc(width[0] * 4, height[0]) # endianness: kCGBitmapByteOrder32Little = (2 << 12) # (2 << 12) | kCGImageAlphaPremultipliedLast) cdef CGContextRef myBitmapContext = CGBitmapContextCreate( - src.data, width, height, 8, width * 4, space, + src.data, width[0], height[0], 8, width[0] * 4, space, kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst) CGContextSetBlendMode(myBitmapContext, kCGBlendModeCopy) @@ -169,9 +205,6 @@ def load_image_data(bytes _url): cdef unsigned char *pmap = [2, 1, 0, 3] vImagePermuteChannels_ARGB8888(&src, &dest, pmap, 0) - # get a python string - r_data = (dest.data)[:width * height * 4] - # release everything CFRelease(url) CGImageRelease(myImageSourceRef) @@ -179,9 +212,7 @@ def load_image_data(bytes _url): CGContextRelease(myBitmapContext) CGColorSpaceRelease(space) free(src.data) - free(dest.data) - - return (width, height, 'rgba', r_data) + #free(dest.data) # this part is freed by the caller. def save_image_rgba(filename, width, height, data, flipped): # compatibility, could be removed i guess @@ -272,6 +303,8 @@ def save_image(filename, width, height, fmt, data, flipped): class ImageLoaderImageIO(ImageLoaderBase): '''Image loader based on ImageIO MacOSX Framework ''' + count = 0 + total = 0 @staticmethod def extensions(): @@ -285,7 +318,18 @@ class ImageLoaderImageIO(ImageLoaderBase): def load(self, filename): # FIXME: if the filename is unicode, the loader is failing. - ret = load_image_data(str(filename)) + from time import time + start = time() + if hasattr(filename, "fileno"): + data = filename.read() + ret = load_image_data(None, data) + else: + ret = load_image_data(str(filename)) + stop = time() + ImageLoaderImageIO.total += (stop - start) + ImageLoaderImageIO.count += 1. + print "Loaded in {:.2f}, average {:2f}".format((stop - start) * 1000, + (ImageLoaderImageIO.total / ImageLoaderImageIO.count) * 1000) if ret is None: Logger.warning('Image: Unable to load image <%s>' % filename) raise Exception('Unable to load image') From 15b962632795a878b6cad06f0d660e74a48a9627 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 8 Sep 2014 11:29:28 +0200 Subject: [PATCH 2/2] imageio: remove debug --- kivy/core/image/img_imageio.pyx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/kivy/core/image/img_imageio.pyx b/kivy/core/image/img_imageio.pyx index 24f0eed38..9e8080a1f 100644 --- a/kivy/core/image/img_imageio.pyx +++ b/kivy/core/image/img_imageio.pyx @@ -303,8 +303,6 @@ def save_image(filename, width, height, fmt, data, flipped): class ImageLoaderImageIO(ImageLoaderBase): '''Image loader based on ImageIO MacOSX Framework ''' - count = 0 - total = 0 @staticmethod def extensions(): @@ -318,18 +316,11 @@ class ImageLoaderImageIO(ImageLoaderBase): def load(self, filename): # FIXME: if the filename is unicode, the loader is failing. - from time import time - start = time() if hasattr(filename, "fileno"): data = filename.read() ret = load_image_data(None, data) else: ret = load_image_data(str(filename)) - stop = time() - ImageLoaderImageIO.total += (stop - start) - ImageLoaderImageIO.count += 1. - print "Loaded in {:.2f}, average {:2f}".format((stop - start) * 1000, - (ImageLoaderImageIO.total / ImageLoaderImageIO.count) * 1000) if ret is None: Logger.warning('Image: Unable to load image <%s>' % filename) raise Exception('Unable to load image')