mirror of
https://github.com/darlinghq/darling-cocotron.git
synced 2024-11-26 21:40:44 +00:00
1880 lines
57 KiB
Objective-C
1880 lines
57 KiB
Objective-C
/*------------------------------------------------------------------------
|
|
*
|
|
* Derivative of the OpenVG 1.0.1 Reference Implementation
|
|
* -------------------------------------
|
|
*
|
|
* Copyright (c) 2007 The Khronos Group Inc.
|
|
* Copyright (c) 2008 Christopher J. W. Lloyd
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and /or associated documentation files
|
|
* (the "Materials "), to deal in the Materials without restriction,
|
|
* including without limitation the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Materials,
|
|
* and to permit persons to whom the Materials are furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Materials.
|
|
*
|
|
* THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR
|
|
* THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
|
*
|
|
*-------------------------------------------------------------------*/
|
|
|
|
#import <Foundation/NSData.h>
|
|
#import <Onyx2D/O2ColorSpace.h>
|
|
#import <Onyx2D/O2DataProvider.h>
|
|
#import <Onyx2D/O2Exceptions.h>
|
|
#import <Onyx2D/O2Image.h>
|
|
#import <Onyx2D/O2Surface.h>
|
|
|
|
@implementation O2Image
|
|
|
|
ONYX2D_STATIC BOOL initFunctionsForMonochrome(O2Image *self,
|
|
size_t bitsPerComponent,
|
|
size_t bitsPerPixel,
|
|
O2BitmapInfo bitmapInfo)
|
|
{
|
|
switch (bitsPerComponent) {
|
|
case 8:
|
|
switch (bitsPerPixel) {
|
|
case 8:
|
|
self->_read_a8u = O2ImageRead_G8_to_A8;
|
|
self->_read_argb8u = O2ImageRead_G8_to_argb8u;
|
|
return YES;
|
|
}
|
|
break;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
ONYX2D_STATIC BOOL initFunctionsForRGBColorSpace(O2Image *self,
|
|
size_t bitsPerComponent,
|
|
size_t bitsPerPixel,
|
|
O2BitmapInfo bitmapInfo)
|
|
{
|
|
|
|
switch (bitsPerComponent) {
|
|
|
|
case 32:
|
|
switch (bitsPerPixel) {
|
|
case 32:
|
|
break;
|
|
case 128:
|
|
switch (bitmapInfo & kO2BitmapByteOrderMask) {
|
|
case kO2BitmapByteOrder16Little:
|
|
case kO2BitmapByteOrder32Little:
|
|
self->_read_argb32f = O2ImageRead_argb32fLittle_to_argb32f;
|
|
return YES;
|
|
|
|
case kO2BitmapByteOrder16Big:
|
|
case kO2BitmapByteOrder32Big:
|
|
self->_read_argb32f = O2ImageRead_argb32fBig_to_argb32f;
|
|
return YES;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 8:
|
|
switch (bitsPerPixel) {
|
|
|
|
case 8:
|
|
self->_read_a8u = O2ImageRead_G8_to_A8;
|
|
self->_read_argb8u = O2ImageRead_G8_to_argb8u;
|
|
return YES;
|
|
|
|
case 16:
|
|
self->_read_argb8u = O2ImageRead_GA88_to_argb8u;
|
|
return YES;
|
|
|
|
case 24:
|
|
switch (bitmapInfo & kO2BitmapAlphaInfoMask) {
|
|
case kO2ImageAlphaNone:
|
|
switch (bitmapInfo & kO2BitmapByteOrderMask) {
|
|
case kO2BitmapByteOrder16Little:
|
|
case kO2BitmapByteOrder32Little:
|
|
self->_read_argb8u = O2ImageRead_ABGR8888_to_argb8u;
|
|
return YES;
|
|
|
|
case kO2BitmapByteOrder16Big:
|
|
case kO2BitmapByteOrder32Big:
|
|
self->_read_argb8u = O2ImageRead_RGBA8888_to_argb8u;
|
|
return YES;
|
|
}
|
|
return YES;
|
|
}
|
|
break;
|
|
|
|
case 32:
|
|
switch (bitmapInfo & kO2BitmapAlphaInfoMask) {
|
|
case kO2ImageAlphaNone:
|
|
break;
|
|
|
|
case kO2ImageAlphaLast:
|
|
case kO2ImageAlphaPremultipliedLast:
|
|
switch (bitmapInfo & kO2BitmapByteOrderMask) {
|
|
case kO2BitmapByteOrder16Little:
|
|
case kO2BitmapByteOrder32Little:
|
|
self->_read_argb8u = O2ImageRead_ABGR8888_to_argb8u;
|
|
return YES;
|
|
|
|
case kO2BitmapByteOrder16Big:
|
|
case kO2BitmapByteOrder32Big:
|
|
self->_read_argb8u = O2ImageRead_RGBA8888_to_argb8u;
|
|
return YES;
|
|
}
|
|
break;
|
|
|
|
case kO2ImageAlphaPremultipliedFirst:
|
|
switch (bitmapInfo & kO2BitmapByteOrderMask) {
|
|
case kO2BitmapByteOrder16Little:
|
|
case kO2BitmapByteOrder32Little:
|
|
self->_read_argb8u = O2ImageRead_BGRA8888_to_argb8u;
|
|
return YES;
|
|
}
|
|
break;
|
|
|
|
case kO2ImageAlphaFirst:
|
|
break;
|
|
|
|
case kO2ImageAlphaNoneSkipLast:
|
|
switch (bitmapInfo & kO2BitmapByteOrderMask) {
|
|
case kO2BitmapByteOrder16Little:
|
|
case kO2BitmapByteOrder32Little:
|
|
self->_read_argb8u = O2ImageRead_ABGR8888_to_argb8u;
|
|
return YES;
|
|
|
|
case kO2BitmapByteOrder16Big:
|
|
case kO2BitmapByteOrder32Big:
|
|
self->_read_argb8u = O2ImageRead_RGBA8888_to_argb8u;
|
|
return YES;
|
|
}
|
|
break;
|
|
|
|
case kO2ImageAlphaNoneSkipFirst:
|
|
switch (bitmapInfo & kO2BitmapByteOrderMask) {
|
|
|
|
case kO2BitmapByteOrder16Little:
|
|
case kO2BitmapByteOrder32Little:
|
|
self->_read_argb8u = O2ImageRead_BGRX8888_to_argb8u;
|
|
return YES;
|
|
|
|
case kO2BitmapByteOrder16Big:
|
|
case kO2BitmapByteOrder32Big:
|
|
self->_read_argb8u = O2ImageRead_XRGB8888_to_argb8u;
|
|
return YES;
|
|
}
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
switch (bitsPerPixel) {
|
|
|
|
case 16:
|
|
if (bitmapInfo ==
|
|
(kO2BitmapByteOrder16Little | kO2ImageAlphaNoneSkipFirst)) {
|
|
self->_read_argb8u = O2ImageRead_G3B5X1R5G2_to_argb8u;
|
|
return YES;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
switch (bitsPerPixel) {
|
|
case 4:
|
|
break;
|
|
case 12:
|
|
break;
|
|
case 16:
|
|
switch (bitmapInfo & kO2BitmapByteOrderMask) {
|
|
case kO2BitmapByteOrder16Little:
|
|
case kO2BitmapByteOrder32Little:
|
|
self->_read_argb8u = O2ImageRead_BARG4444_to_argb8u;
|
|
return YES;
|
|
|
|
case kO2BitmapByteOrder16Big:
|
|
case kO2BitmapByteOrder32Big:
|
|
self->_read_argb8u = O2ImageRead_RGBA4444_to_argb8u;
|
|
return YES;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
switch (bitsPerPixel) {
|
|
case 2:
|
|
break;
|
|
case 6:
|
|
break;
|
|
case 8:
|
|
self->_read_argb8u = O2ImageRead_RGBA2222_to_argb8u;
|
|
return YES;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
switch (bitsPerPixel) {
|
|
case 1:
|
|
// self->_read_argb32f=O2ImageReadPixelSpan_01;
|
|
// return YES;
|
|
|
|
case 3:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
ONYX2D_STATIC BOOL initFunctionsForCMYKColorSpace(O2Image *self,
|
|
size_t bitsPerComponent,
|
|
size_t bitsPerPixel,
|
|
O2BitmapInfo bitmapInfo)
|
|
{
|
|
switch (bitsPerComponent) {
|
|
|
|
case 8:
|
|
switch (bitsPerPixel) {
|
|
|
|
case 32:
|
|
switch (bitmapInfo & kO2BitmapByteOrderMask) {
|
|
case kO2BitmapByteOrder16Big:
|
|
case kO2BitmapByteOrder32Big:
|
|
self->_read_argb8u = O2ImageRead_CMYK8888_to_argb8u;
|
|
return YES;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
ONYX2D_STATIC BOOL initFunctionsForIndexedColorSpace(O2Image *self,
|
|
size_t bitsPerComponent,
|
|
size_t bitsPerPixel,
|
|
O2ColorSpaceRef colorSpace,
|
|
O2BitmapInfo bitmapInfo)
|
|
{
|
|
|
|
switch ([[(O2ColorSpace_indexed *) colorSpace baseColorSpace] type]) {
|
|
|
|
case kO2ColorSpaceModelRGB:
|
|
self->_read_argb8u = O2ImageRead_I8_to_argb8u;
|
|
return YES;
|
|
|
|
default:
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
ONYX2D_STATIC BOOL initFunctionsForParameters(O2Image *self,
|
|
size_t bitsPerComponent,
|
|
size_t bitsPerPixel,
|
|
O2ColorSpaceRef colorSpace,
|
|
O2BitmapInfo bitmapInfo)
|
|
{
|
|
|
|
self->_read_a8u = O2Image_read_a8u_src_argb8u;
|
|
self->_read_a32f = O2ImageRead_ANY_to_A8_to_Af;
|
|
self->_read_argb32f = O2ImageRead_ANY_to_argb8u_to_argb32f;
|
|
|
|
if ((bitmapInfo & kO2BitmapByteOrderMask) == kO2BitmapByteOrderDefault) {
|
|
#ifdef __LITTLE_ENDIAN__
|
|
bitmapInfo |= kO2BitmapByteOrder32Little;
|
|
#else
|
|
bitmapInfo |= kO2BitmapByteOrder32Big;
|
|
#endif
|
|
}
|
|
|
|
switch ([colorSpace type]) {
|
|
case kO2ColorSpaceModelMonochrome:
|
|
return initFunctionsForMonochrome(self, bitsPerComponent, bitsPerPixel,
|
|
bitmapInfo);
|
|
case kO2ColorSpaceModelRGB:
|
|
return initFunctionsForRGBColorSpace(self, bitsPerComponent,
|
|
bitsPerPixel, bitmapInfo);
|
|
case kO2ColorSpaceModelCMYK:
|
|
return initFunctionsForCMYKColorSpace(self, bitsPerComponent,
|
|
bitsPerPixel, bitmapInfo);
|
|
case kO2ColorSpaceModelIndexed:
|
|
return initFunctionsForIndexedColorSpace(
|
|
self, bitsPerComponent, bitsPerPixel, colorSpace, bitmapInfo);
|
|
default:
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
- initWithWidth: (size_t) width
|
|
height: (size_t) height
|
|
bitsPerComponent: (size_t) bitsPerComponent
|
|
bitsPerPixel: (size_t) bitsPerPixel
|
|
bytesPerRow: (size_t) bytesPerRow
|
|
colorSpace: (O2ColorSpaceRef) colorSpace
|
|
bitmapInfo: (O2BitmapInfo) bitmapInfo
|
|
decoder: (O2ImageDecoder *) decoder
|
|
provider: (O2DataProvider *) provider
|
|
decode: (const O2Float *) decode
|
|
interpolate: (BOOL) interpolate
|
|
renderingIntent: (O2ColorRenderingIntent) renderingIntent
|
|
{
|
|
_width = width;
|
|
_height = height;
|
|
_bitsPerComponent = bitsPerComponent;
|
|
_bitsPerPixel = bitsPerPixel;
|
|
_bytesPerRow = bytesPerRow;
|
|
_colorSpace = [colorSpace retain];
|
|
_bitmapInfo = bitmapInfo;
|
|
_decoder = [decoder retain];
|
|
_provider = [provider retain];
|
|
// _decode=NULL;
|
|
_interpolate = interpolate;
|
|
_isMask = NO;
|
|
_renderingIntent = renderingIntent;
|
|
_mask = nil;
|
|
|
|
_directBytes = NULL;
|
|
_directLength = 0;
|
|
|
|
_clampExternalPixels = NO; // only do this if premultiplied format
|
|
if (!initFunctionsForParameters(self, bitsPerComponent, bitsPerPixel,
|
|
colorSpace, bitmapInfo)) {
|
|
NSLog(@"O2Image failed to init with bpc=%zu, "
|
|
@"bpp=%zu,colorSpace=%@,bitmapInfo=0x%0X",
|
|
bitsPerComponent, bitsPerPixel, colorSpace, bitmapInfo);
|
|
[self dealloc];
|
|
return nil;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- initWithJPEGDataProvider: (O2DataProvider *) jpegProvider
|
|
decode: (const O2Float *) decode
|
|
interpolate: (BOOL) interpolate
|
|
renderingIntent: (O2ColorRenderingIntent) renderingIntent
|
|
{
|
|
O2UnimplementedMethod();
|
|
return nil;
|
|
}
|
|
|
|
- initWithPNGDataProvider: (O2DataProvider *) jpegProvider
|
|
decode: (const O2Float *) decode
|
|
interpolate: (BOOL) interpolate
|
|
renderingIntent: (O2ColorRenderingIntent) renderingIntent
|
|
{
|
|
O2UnimplementedMethod();
|
|
return nil;
|
|
}
|
|
|
|
- initMaskWithWidth: (size_t) width
|
|
height: (size_t) height
|
|
bitsPerComponent: (size_t) bitsPerComponent
|
|
bitsPerPixel: (size_t) bitsPerPixel
|
|
bytesPerRow: (size_t) bytesPerRow
|
|
provider: (O2DataProvider *) provider
|
|
decode: (const O2Float *) decode
|
|
interpolate: (BOOL) interpolate
|
|
{
|
|
O2ColorSpaceRef gray = O2ColorSpaceCreateDeviceGray();
|
|
|
|
if ((self = [self initWithWidth: width
|
|
height: height
|
|
bitsPerComponent: bitsPerComponent
|
|
bitsPerPixel: bitsPerPixel
|
|
bytesPerRow: bytesPerRow
|
|
colorSpace: gray
|
|
bitmapInfo: kO2ImageAlphaNone
|
|
decoder: NULL
|
|
provider: provider
|
|
decode: decode
|
|
interpolate: interpolate
|
|
renderingIntent: kO2RenderingIntentDefault]) == nil) {
|
|
O2ColorSpaceRelease(gray);
|
|
return nil;
|
|
}
|
|
|
|
O2ColorSpaceRelease(gray);
|
|
_isMask = YES;
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc {
|
|
[_colorSpace release];
|
|
[_decoder release];
|
|
[_provider release];
|
|
if (_decode != NULL)
|
|
NSZoneFree(NULL, _decode);
|
|
[_mask release];
|
|
[_directData release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- copyWithZone: (NSZone *) zone {
|
|
return [self retain];
|
|
}
|
|
|
|
- (O2Image *) copyWithColorSpace: (O2ColorSpaceRef) colorSpace {
|
|
O2UnimplementedMethod();
|
|
return nil;
|
|
}
|
|
|
|
- (void) setMask: (O2Image *) image {
|
|
[image retain];
|
|
[_mask release];
|
|
_mask = image;
|
|
}
|
|
|
|
- copyWithMask: (O2Image *) image {
|
|
O2UnimplementedMethod();
|
|
return nil;
|
|
}
|
|
|
|
- copyWithMaskingColors: (const O2Float *) components {
|
|
O2UnimplementedMethod();
|
|
return nil;
|
|
}
|
|
|
|
ONYX2D_STATIC_INLINE const void *directBytes(O2Image *self) {
|
|
if (self->_directBytes == NULL) {
|
|
if ([self->_provider isDirectAccess]) {
|
|
self->_directData = [[self->_provider data] retain];
|
|
self->_directBytes = [self->_provider bytes];
|
|
self->_directLength = [self->_provider length];
|
|
} else {
|
|
self->_directData =
|
|
(NSData *) O2DataProviderCopyData(self->_provider);
|
|
self->_directBytes = [self->_directData bytes];
|
|
self->_directLength = [self->_directData length];
|
|
}
|
|
}
|
|
return self->_directBytes;
|
|
}
|
|
|
|
ONYX2D_STATIC_INLINE const void *scanlineAtY(O2Image *self, int y) {
|
|
const void *bytes = directBytes(self);
|
|
int offset = self->_bytesPerRow * y;
|
|
int max = offset + self->_bytesPerRow;
|
|
|
|
if (max <= self->_directLength)
|
|
return bytes + offset;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
- (NSData *) directData {
|
|
directBytes(self);
|
|
return _directData;
|
|
}
|
|
|
|
- (const void *) directBytes {
|
|
return directBytes(self);
|
|
}
|
|
|
|
- (void) releaseDirectDataIfPossible {
|
|
if (![_provider isDirectAccess]) {
|
|
[_directData release];
|
|
_directData = nil;
|
|
_directBytes = NULL;
|
|
_directLength = 0;
|
|
}
|
|
}
|
|
|
|
O2ImageRef O2ImageCreate(size_t width, size_t height, size_t bitsPerComponent,
|
|
size_t bitsPerPixel, size_t bytesPerRow,
|
|
O2ColorSpaceRef colorSpace, O2BitmapInfo bitmapInfo,
|
|
O2DataProviderRef dataProvider, const O2Float *decode,
|
|
BOOL shouldInterpolate,
|
|
O2ColorRenderingIntent renderingIntent)
|
|
{
|
|
return [[O2Image alloc] initWithWidth: width
|
|
height: height
|
|
bitsPerComponent: bitsPerComponent
|
|
bitsPerPixel: bitsPerPixel
|
|
bytesPerRow: bytesPerRow
|
|
colorSpace: colorSpace
|
|
bitmapInfo: bitmapInfo
|
|
decoder: NULL
|
|
provider: dataProvider
|
|
decode: decode
|
|
interpolate: shouldInterpolate
|
|
renderingIntent: renderingIntent];
|
|
}
|
|
|
|
O2ImageRef O2ImageMaskCreate(size_t width, size_t height,
|
|
size_t bitsPerComponent, size_t bitsPerPixel,
|
|
size_t bytesPerRow, O2DataProviderRef dataProvider,
|
|
const O2Float *decode, BOOL shouldInterpolate)
|
|
{
|
|
return [[O2Image alloc] initMaskWithWidth: width
|
|
height: height
|
|
bitsPerComponent: bitsPerComponent
|
|
bitsPerPixel: bitsPerPixel
|
|
bytesPerRow: bytesPerRow
|
|
provider: dataProvider
|
|
decode: decode
|
|
interpolate: shouldInterpolate];
|
|
}
|
|
|
|
O2ImageRef O2ImageCreateCopy(O2ImageRef self) {
|
|
return [self copy];
|
|
}
|
|
|
|
O2ImageRef O2ImageCreateCopyWithColorSpace(O2ImageRef self,
|
|
O2ColorSpaceRef colorSpace)
|
|
{
|
|
return [self copyWithColorSpace: colorSpace];
|
|
}
|
|
|
|
O2ImageRef
|
|
O2ImageCreateWithJPEGDataProvider(O2DataProviderRef jpegProvider,
|
|
const O2Float *decode, BOOL interpolate,
|
|
O2ColorRenderingIntent renderingIntent)
|
|
{
|
|
return [[O2Image alloc] initWithJPEGDataProvider: jpegProvider
|
|
decode: decode
|
|
interpolate: interpolate
|
|
renderingIntent: renderingIntent];
|
|
}
|
|
|
|
O2ImageRef
|
|
O2ImageCreateWithPNGDataProvider(O2DataProviderRef pngProvider,
|
|
const O2Float *decode, BOOL interpolate,
|
|
O2ColorRenderingIntent renderingIntent)
|
|
{
|
|
return [[O2Image alloc] initWithPNGDataProvider: pngProvider
|
|
decode: decode
|
|
interpolate: interpolate
|
|
renderingIntent: renderingIntent];
|
|
}
|
|
|
|
O2ImageRef O2ImageCreateWithImageInRect(O2ImageRef self, O2Rect rect) {
|
|
rect = O2RectIntegral(rect);
|
|
size_t x = MAX(0, rect.origin.x);
|
|
size_t y = MAX(0, rect.origin.y);
|
|
size_t col, width = rect.size.width;
|
|
size_t row, height = rect.size.height;
|
|
size_t childBytesPerRow = (width * self->_bitsPerPixel + 7) / 8;
|
|
uint8_t *childPixelBytes = malloc(height * childBytesPerRow);
|
|
size_t childIndex = 0;
|
|
const uint8_t *pixelBytes = directBytes(self);
|
|
|
|
pixelBytes += self->_bytesPerRow * y;
|
|
|
|
for (row = 0; row < height; row++) {
|
|
const uint8_t *rowBytes = pixelBytes + (x * self->_bitsPerPixel) / 8;
|
|
|
|
for (col = 0; col < childBytesPerRow; col++) {
|
|
// Copy all of the needed bytes for the row
|
|
childPixelBytes[childIndex++] = rowBytes[col];
|
|
}
|
|
|
|
pixelBytes += self->_bytesPerRow;
|
|
}
|
|
|
|
NSData *data = [NSData dataWithBytesNoCopy: childPixelBytes
|
|
length: childIndex];
|
|
O2DataProviderRef provider =
|
|
O2DataProviderCreateWithCFData((CFDataRef) data);
|
|
|
|
O2ImageRef result = O2ImageCreate(
|
|
width, height, self->_bitsPerComponent, self->_bitsPerPixel,
|
|
childBytesPerRow, self->_colorSpace, self->_bitmapInfo, provider,
|
|
self->_decode, self->_interpolate, self->_renderingIntent);
|
|
|
|
O2DataProviderRelease(provider);
|
|
|
|
return result;
|
|
}
|
|
|
|
O2ImageRef O2ImageCreateWithMask(O2ImageRef self, O2ImageRef mask) {
|
|
return [self copyWithMask: mask];
|
|
}
|
|
|
|
O2ImageRef O2ImageCreateWithMaskingColors(O2ImageRef self,
|
|
const O2Float *components)
|
|
{
|
|
return [self copyWithMaskingColors: components];
|
|
}
|
|
|
|
O2ImageRef O2ImageRetain(O2ImageRef self) {
|
|
return (self != NULL) ? (O2ImageRef) CFRetain(self) : NULL;
|
|
}
|
|
|
|
void O2ImageRelease(O2ImageRef self) {
|
|
if (self != NULL)
|
|
CFRelease(self);
|
|
}
|
|
|
|
BOOL O2ImageIsMask(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return NO;
|
|
|
|
return self->_isMask;
|
|
}
|
|
|
|
size_t O2ImageGetWidth(O2Image *self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_width;
|
|
}
|
|
|
|
size_t O2ImageGetHeight(O2Image *self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_height;
|
|
}
|
|
|
|
size_t O2ImageGetBitsPerComponent(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_bitsPerComponent;
|
|
}
|
|
|
|
size_t O2ImageGetBitsPerPixel(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_bitsPerPixel;
|
|
}
|
|
|
|
size_t O2ImageGetBytesPerRow(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_bytesPerRow;
|
|
}
|
|
|
|
O2ColorSpaceRef O2ImageGetColorSpace(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_colorSpace;
|
|
}
|
|
|
|
O2ImageAlphaInfo O2ImageGetAlphaInfo(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_bitmapInfo & kO2BitmapAlphaInfoMask;
|
|
}
|
|
|
|
O2DataProviderRef O2ImageGetDataProvider(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_provider;
|
|
}
|
|
|
|
const O2Float *O2ImageGetDecode(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_decode;
|
|
}
|
|
|
|
BOOL O2ImageGetShouldInterpolate(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_interpolate;
|
|
}
|
|
|
|
O2ColorRenderingIntent O2ImageGetRenderingIntent(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_renderingIntent;
|
|
}
|
|
|
|
O2BitmapInfo O2ImageGetBitmapInfo(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_bitmapInfo;
|
|
}
|
|
|
|
O2ImageRef O2ImageGetMask(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return 0;
|
|
|
|
return self->_mask;
|
|
}
|
|
|
|
O2ImageDecoderRef O2ImageGetImageDecoder(O2ImageRef self) {
|
|
if (self == NULL)
|
|
return NULL;
|
|
|
|
return self->_decoder;
|
|
}
|
|
|
|
O2argb32f *O2ImageRead_ANY_to_argb8u_to_argb32f(O2Image *self, int x, int y,
|
|
O2argb32f *span, int length)
|
|
{
|
|
O2argb8u *span8888 = __builtin_alloca(length * sizeof(O2argb8u));
|
|
O2argb8u *direct = self->_read_argb8u(self, x, y, span8888, length);
|
|
|
|
if (direct != NULL)
|
|
span8888 = direct;
|
|
|
|
int i;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb32f result;
|
|
|
|
result.r = O2Float32FromByte(span8888[i].r);
|
|
result.g = O2Float32FromByte(span8888[i].g);
|
|
result.b = O2Float32FromByte(span8888[i].b);
|
|
result.a = O2Float32FromByte(span8888[i].a);
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2Float32 bytesLittleToFloat(const unsigned char *scanline) {
|
|
union {
|
|
unsigned char bytes[4];
|
|
O2Float32 f;
|
|
} u;
|
|
|
|
#ifdef __LITTLE_ENDIAN__
|
|
u.bytes[0] = scanline[0];
|
|
u.bytes[1] = scanline[1];
|
|
u.bytes[2] = scanline[2];
|
|
u.bytes[3] = scanline[3];
|
|
#else
|
|
u.bytes[0] = scanline[3];
|
|
u.bytes[1] = scanline[2];
|
|
u.bytes[2] = scanline[1];
|
|
u.bytes[3] = scanline[0];
|
|
#endif
|
|
|
|
return u.f;
|
|
}
|
|
|
|
O2argb32f *O2ImageRead_argb32fLittle_to_argb32f(O2Image *self, int x, int y,
|
|
O2argb32f *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 16;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb32f result;
|
|
|
|
result.r = bytesLittleToFloat(scanline);
|
|
scanline += 4;
|
|
result.g = bytesLittleToFloat(scanline);
|
|
scanline += 4;
|
|
result.b = bytesLittleToFloat(scanline);
|
|
scanline += 4;
|
|
result.a = bytesLittleToFloat(scanline);
|
|
scanline += 4;
|
|
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2Float32 bytesBigToFloat(const unsigned char *scanline) {
|
|
union {
|
|
unsigned char bytes[4];
|
|
O2Float32 f;
|
|
} u;
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
u.bytes[0] = scanline[0];
|
|
u.bytes[1] = scanline[1];
|
|
u.bytes[2] = scanline[2];
|
|
u.bytes[3] = scanline[3];
|
|
#else
|
|
u.bytes[0] = scanline[3];
|
|
u.bytes[1] = scanline[2];
|
|
u.bytes[2] = scanline[1];
|
|
u.bytes[3] = scanline[0];
|
|
#endif
|
|
|
|
return u.f;
|
|
}
|
|
|
|
O2argb32f *O2ImageRead_argb32fBig_to_argb32f(O2Image *self, int x, int y,
|
|
O2argb32f *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 16;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb32f result;
|
|
|
|
result.r = bytesBigToFloat(scanline);
|
|
scanline += 4;
|
|
result.g = bytesBigToFloat(scanline);
|
|
scanline += 4;
|
|
result.b = bytesBigToFloat(scanline);
|
|
scanline += 4;
|
|
result.a = bytesBigToFloat(scanline);
|
|
scanline += 4;
|
|
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
uint8_t *O2ImageRead_G8_to_A8(O2Image *self, int x, int y, uint8_t *alpha,
|
|
int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x;
|
|
for (i = 0; i < length; i++) {
|
|
*alpha++ = *scanline++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
uint8_t *O2Image_read_a8u_src_argb8u(O2Image *self, int x, int y,
|
|
uint8_t *alpha, int length)
|
|
{
|
|
O2argb8u *span = __builtin_alloca(length * sizeof(O2argb8u));
|
|
int i;
|
|
|
|
O2argb8u *direct = self->_read_argb8u(self, x, y, span, length);
|
|
if (direct != NULL)
|
|
span = direct;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
*alpha++ = span[i].a;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2Float *O2ImageRead_ANY_to_A8_to_Af(O2Image *self, int x, int y,
|
|
O2Float *alpha, int length)
|
|
{
|
|
uint8_t span[length];
|
|
int i;
|
|
|
|
self->_read_a8u(self, x, y, span, length);
|
|
for (i = 0; i < length; i++)
|
|
alpha[i] = O2Float32FromByte(span[i]);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_G8_to_argb8u(O2Image *self, int x, int y, O2argb8u *span,
|
|
int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.r = *scanline++;
|
|
result.g = result.r;
|
|
result.b = result.r;
|
|
result.a = 0xFF;
|
|
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_GA88_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 2;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.r = *scanline++;
|
|
result.g = result.r;
|
|
result.b = result.r;
|
|
result.a = *scanline++;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_RGBA8888_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 4;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.r = scanline[0];
|
|
result.g = scanline[1];
|
|
result.b = scanline[2];
|
|
result.a = scanline[3];
|
|
*span++ = result;
|
|
scanline += 4;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_ABGR8888_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 4;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.a = *scanline++;
|
|
result.b = *scanline++;
|
|
result.g = *scanline++;
|
|
result.r = *scanline++;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_BGRA8888_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 4;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
return (O2argb8u *) scanline;
|
|
#endif
|
|
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.b = *scanline++;
|
|
result.g = *scanline++;
|
|
result.r = *scanline++;
|
|
result.a = *scanline++;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_RGB888_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 3;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.r = *scanline++;
|
|
result.g = *scanline++;
|
|
result.b = *scanline++;
|
|
result.a = 255;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_BGRX8888_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 4;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.b = *scanline++;
|
|
result.g = *scanline++;
|
|
result.r = *scanline++;
|
|
result.a = 255;
|
|
scanline++;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_XRGB8888_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 4;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.a = *scanline++;
|
|
result.r = *scanline++;
|
|
result.g = *scanline++;
|
|
result.b = *scanline++;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// kO2BitmapByteOrder16Little|kO2ImageAlphaNoneSkipFirst
|
|
O2argb8u *O2ImageRead_G3B5X1R5G2_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 2;
|
|
for (i = 0; i < length; i++) {
|
|
unsigned short low = *scanline++;
|
|
unsigned short high = *scanline;
|
|
unsigned short value = low | (high << 8);
|
|
O2argb8u result;
|
|
|
|
result.r = ((value >> 10) & 0x1F) << 3;
|
|
result.g = ((value >> 5) & 0x1F) << 3;
|
|
result.b = ((value & 0x1F) << 3);
|
|
result.a = 255;
|
|
scanline++;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_RGBA4444_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 2;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.r = *scanline & 0xF0;
|
|
result.g = (*scanline & 0x0F) << 4;
|
|
scanline++;
|
|
result.b = *scanline & 0xF0;
|
|
result.a = (*scanline & 0x0F) << 4;
|
|
scanline++;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_BARG4444_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 2;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.b = *scanline & 0xF0;
|
|
result.a = (*scanline & 0x0F) << 4;
|
|
scanline++;
|
|
result.r = *scanline & 0xF0;
|
|
result.g = (*scanline & 0x0F) << 4;
|
|
scanline++;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_RGBA2222_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
|
|
result.r = *scanline & 0xC0;
|
|
result.g = (*scanline & 0x03) << 2;
|
|
result.b = (*scanline & 0x0C) << 4;
|
|
result.a = (*scanline & 0x03) << 6;
|
|
scanline++;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_CMYK8888_to_argb8u(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length)
|
|
{
|
|
// poor results
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x * 4;
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u result;
|
|
unsigned char c = *scanline++;
|
|
unsigned char y = *scanline++;
|
|
unsigned char m = *scanline++;
|
|
unsigned char k = *scanline++;
|
|
unsigned char w = 0xff - k;
|
|
|
|
result.r = c > w ? 0 : w - c;
|
|
result.g = m > w ? 0 : w - m;
|
|
result.b = y > w ? 0 : w - y;
|
|
result.a = 1;
|
|
*span++ = result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
O2argb8u *O2ImageRead_I8_to_argb8u(O2Image *self, int x, int y, O2argb8u *span,
|
|
int length)
|
|
{
|
|
O2ColorSpace_indexed *indexed = (O2ColorSpace_indexed *) self->_colorSpace;
|
|
size_t hival = [indexed hival];
|
|
const unsigned char *palette = [indexed paletteBytes];
|
|
|
|
const uint8_t *scanline = scanlineAtY(self, y);
|
|
int i;
|
|
|
|
if (scanline == NULL)
|
|
return NULL;
|
|
|
|
scanline += x;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
unsigned index = *scanline++;
|
|
O2argb8u argb;
|
|
|
|
RI_INT_CLAMP(index, 0, hival); // it is external data after all
|
|
|
|
argb.r = palette[index * 3 + 0];
|
|
argb.g = palette[index * 3 + 1];
|
|
argb.b = palette[index * 3 + 2];
|
|
argb.a = 255;
|
|
*span++ = argb;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/ /*!
|
|
* \brief
|
|
*Reads
|
|
*a
|
|
*texel
|
|
*(u,v)
|
|
*at the
|
|
*given
|
|
*mipmap
|
|
*level.
|
|
*Tiling
|
|
*modes
|
|
*and
|
|
* color
|
|
*space
|
|
*conversion
|
|
*are
|
|
*applied.
|
|
*Outputs
|
|
*color
|
|
*in
|
|
*premultiplied
|
|
* format.
|
|
* \param
|
|
* \return
|
|
* \note
|
|
*/
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/* O2ImageReadTileSpanExtendEdge__ is used by the image resampling functions to
|
|
read translated spans. When a coordinate is outside the image it uses the
|
|
edge value. This works better than say, zero, with averaging algorithms
|
|
(bilinear,bicubic, etc) as you get good values at the edges.
|
|
|
|
Ideally the averaging algorithms would only use the available pixels on the
|
|
edges */
|
|
|
|
ONYX2D_STATIC_INLINE void
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(O2Image *self, int u, int v,
|
|
O2argb8u *span, int length)
|
|
{
|
|
int i;
|
|
O2argb8u *direct;
|
|
v = RI_INT_CLAMP(v, 0, self->_height - 1);
|
|
|
|
for (i = 0; u < 0 && i < length; u++, i++) {
|
|
direct = self->_read_argb8u(self, 0, v, span + i, 1);
|
|
|
|
if (direct != NULL)
|
|
span[i] = direct[0];
|
|
}
|
|
|
|
int chunk = RI_MIN(length - i, self->_width - u);
|
|
direct = self->_read_argb8u(self, u, v, span + i, chunk);
|
|
if (direct != NULL) {
|
|
int k;
|
|
|
|
for (k = 0; k < chunk; k++)
|
|
span[i + k] = direct[k];
|
|
}
|
|
|
|
i += chunk;
|
|
u += chunk;
|
|
|
|
for (; i < length; i++) {
|
|
direct = self->_read_argb8u(self, self->_width - 1, v, span + i, 1);
|
|
|
|
if (direct != NULL)
|
|
span[i] = direct[0];
|
|
}
|
|
}
|
|
|
|
void O2ImageReadTileSpanExtendEdge__largb32f_PRE(O2Image *self, int u, int v,
|
|
O2argb32f *span, int length)
|
|
{
|
|
int i;
|
|
O2argb32f *direct;
|
|
|
|
v = RI_INT_CLAMP(v, 0, self->_height - 1);
|
|
|
|
for (i = 0; i < length && u < 0; u++, i++) {
|
|
direct = O2Image_read_argb32f(self, 0, v, span + i, 1);
|
|
if (direct != NULL)
|
|
span[i] = direct[0];
|
|
}
|
|
|
|
int chunk = RI_MIN(length - i, self->_width - u);
|
|
direct = O2Image_read_argb32f(self, u, v, span + i, chunk);
|
|
if (direct != NULL) {
|
|
int k;
|
|
|
|
for (k = 0; k < chunk; k++)
|
|
span[i + k] = direct[k];
|
|
}
|
|
i += chunk;
|
|
u += chunk;
|
|
|
|
for (; i < length; i++) {
|
|
direct = O2Image_read_argb32f(self, self->_width - 1, v, span + i, 1);
|
|
if (direct != NULL)
|
|
span[i] = direct[0];
|
|
}
|
|
}
|
|
|
|
ONYX2D_STATIC_INLINE int cubic_8(int v0, int v1, int v2, int v3, int fraction) {
|
|
int p = (v3 - v2) - (v0 - v1);
|
|
int q = (v0 - v1) - p;
|
|
|
|
return RI_INT_CLAMP((p * (fraction * fraction * fraction)) /
|
|
(256 * 256 * 256) +
|
|
(q * fraction * fraction) / (256 * 256) +
|
|
((v2 - v0) * fraction) / 256 + v1,
|
|
0, 255);
|
|
}
|
|
|
|
ONYX2D_STATIC_INLINE O2argb8u bicubic_largb8u_PRE(O2argb8u a, O2argb8u b,
|
|
O2argb8u c, O2argb8u d,
|
|
int fraction)
|
|
{
|
|
O2argb8u result;
|
|
|
|
result.r = cubic_8(a.r, b.r, c.r, d.r, fraction);
|
|
result.b = cubic_8(a.g, b.g, c.g, d.g, fraction);
|
|
result.g = cubic_8(a.b, b.b, c.b, d.b, fraction);
|
|
result.a = cubic_8(a.a, b.a, c.a, d.a, fraction);
|
|
|
|
return result;
|
|
}
|
|
|
|
void O2ImageBicubic_largb8u_PRE(O2Image *self, int x, int y, O2argb8u *span,
|
|
int length, O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++, x++) {
|
|
O2Point uv = O2PointMake(du, dv);
|
|
|
|
uv.x -= 0.5f;
|
|
uv.y -= 0.5f;
|
|
int u = RI_FLOOR_TO_INT(uv.x);
|
|
int ufrac = coverageFromZeroToOne(uv.x - u);
|
|
|
|
int v = RI_FLOOR_TO_INT(uv.y);
|
|
int vfrac = coverageFromZeroToOne(uv.y - v);
|
|
|
|
O2argb8u t0, t1, t2, t3;
|
|
O2argb8u cspan[4];
|
|
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, u - 1, v - 1, cspan, 4);
|
|
t0 = bicubic_largb8u_PRE(cspan[0], cspan[1], cspan[2], cspan[3], ufrac);
|
|
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, u - 1, v, cspan, 4);
|
|
t1 = bicubic_largb8u_PRE(cspan[0], cspan[1], cspan[2], cspan[3], ufrac);
|
|
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, u - 1, v + 1, cspan, 4);
|
|
t2 = bicubic_largb8u_PRE(cspan[0], cspan[1], cspan[2], cspan[3], ufrac);
|
|
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, u - 1, v + 2, cspan, 4);
|
|
t3 = bicubic_largb8u_PRE(cspan[0], cspan[1], cspan[2], cspan[3], ufrac);
|
|
|
|
span[i] = bicubic_largb8u_PRE(t0, t1, t2, t3, vfrac);
|
|
|
|
du += surfaceToImage.a;
|
|
dv += surfaceToImage.b;
|
|
}
|
|
}
|
|
|
|
ONYX2D_STATIC_INLINE O2Float cubic_f(O2Float v0, O2Float v1, O2Float v2,
|
|
O2Float v3, O2Float fraction)
|
|
{
|
|
O2Float p = (v3 - v2) - (v0 - v1);
|
|
O2Float q = (v0 - v1) - p;
|
|
|
|
return RI_CLAMP((p * (fraction * fraction * fraction)) +
|
|
(q * fraction * fraction) + ((v2 - v0) * fraction) +
|
|
v1,
|
|
0, 1);
|
|
}
|
|
|
|
O2argb32f bicubic_largb32f_PRE(O2argb32f a, O2argb32f b, O2argb32f c,
|
|
O2argb32f d, O2Float fraction)
|
|
{
|
|
return O2argb32fInit(cubic_f(a.r, b.r, c.r, d.r, fraction),
|
|
cubic_f(a.g, b.g, c.g, d.g, fraction),
|
|
cubic_f(a.b, b.b, c.b, d.b, fraction),
|
|
cubic_f(a.a, b.a, c.a, d.a, fraction));
|
|
}
|
|
|
|
void O2ImageBicubic_largb32f_PRE(O2Image *self, int x, int y, O2argb32f *span,
|
|
int length, O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++, x++) {
|
|
O2Point uv = O2PointMake(du, dv);
|
|
|
|
uv.x -= 0.5f;
|
|
uv.y -= 0.5f;
|
|
int u = RI_FLOOR_TO_INT(uv.x);
|
|
O2Float ufrac = uv.x - u;
|
|
|
|
int v = RI_FLOOR_TO_INT(uv.y);
|
|
O2Float vfrac = uv.y - v;
|
|
|
|
O2argb32f t0, t1, t2, t3;
|
|
O2argb32f cspan[4];
|
|
|
|
O2ImageReadTileSpanExtendEdge__largb32f_PRE(self, u - 1, v - 1, cspan,
|
|
4);
|
|
t0 = bicubic_largb32f_PRE(cspan[0], cspan[1], cspan[2], cspan[3],
|
|
ufrac);
|
|
|
|
O2ImageReadTileSpanExtendEdge__largb32f_PRE(self, u - 1, v, cspan, 4);
|
|
t1 = bicubic_largb32f_PRE(cspan[0], cspan[1], cspan[2], cspan[3],
|
|
ufrac);
|
|
|
|
O2ImageReadTileSpanExtendEdge__largb32f_PRE(self, u - 1, v + 1, cspan,
|
|
4);
|
|
t2 = bicubic_largb32f_PRE(cspan[0], cspan[1], cspan[2], cspan[3],
|
|
ufrac);
|
|
|
|
O2ImageReadTileSpanExtendEdge__largb32f_PRE(self, u - 1, v + 2, cspan,
|
|
4);
|
|
t3 = bicubic_largb32f_PRE(cspan[0], cspan[1], cspan[2], cspan[3],
|
|
ufrac);
|
|
|
|
span[i] = bicubic_largb32f_PRE(t0, t1, t2, t3, vfrac);
|
|
|
|
du += surfaceToImage.a;
|
|
dv += surfaceToImage.b;
|
|
}
|
|
}
|
|
|
|
void O2ImageBilinear_largb8u_PRE(O2Image *self, int x, int y, O2argb8u *span,
|
|
int length, O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
double a = surfaceToImage.a * COVERAGE_MULTIPLIER_FLOAT;
|
|
double b = surfaceToImage.b * COVERAGE_MULTIPLIER_FLOAT;
|
|
int i;
|
|
|
|
du -= 0.5;
|
|
dv -= 0.5;
|
|
|
|
// Coordinates are pre-scaled by 256 to avoid float multiplication and
|
|
// subtraction inside the loop for generating the coverage amount.
|
|
|
|
du *= COVERAGE_MULTIPLIER_FLOAT;
|
|
dv *= COVERAGE_MULTIPLIER_FLOAT;
|
|
|
|
for (i = 0; i < length; i++, x++) {
|
|
int uvx = du;
|
|
int uvy = dv;
|
|
|
|
int u = uvx >> 8;
|
|
int v = uvy >> 8;
|
|
|
|
uint32_t fu = uvx & 0xFF;
|
|
uint32_t oneMinusFu = inverseCoverage(fu);
|
|
|
|
O2argb8u line0[2];
|
|
O2argb8u line1[2];
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, u, v, line0, 2);
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, u, v + 1, line1, 2);
|
|
|
|
O2argb8u c0 = O2argb8uMultiplyByCoverageAdd(line0[0], oneMinusFu,
|
|
line0[1], fu);
|
|
O2argb8u c1 = O2argb8uMultiplyByCoverageAdd(line1[0], oneMinusFu,
|
|
line1[1], fu);
|
|
|
|
uint32_t fv = uvy & 0xFF;
|
|
uint32_t oneMinusFv = inverseCoverage(fv);
|
|
|
|
span[i] = O2argb8uMultiplyByCoverageAdd(c0, oneMinusFv, c1, fv);
|
|
|
|
du += a;
|
|
dv += b;
|
|
}
|
|
}
|
|
|
|
void O2ImageBilinear_largb32f_PRE(O2Image *self, int x, int y, O2argb32f *span,
|
|
int length, O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++, x++) {
|
|
O2Point uv = O2PointMake(du, dv);
|
|
|
|
uv.x -= 0.5f;
|
|
uv.y -= 0.5f;
|
|
int u = RI_FLOOR_TO_INT(uv.x);
|
|
int v = RI_FLOOR_TO_INT(uv.y);
|
|
O2argb32f c00c01[2];
|
|
O2ImageReadTileSpanExtendEdge__largb32f_PRE(self, u, v, c00c01, 2);
|
|
|
|
O2argb32f c01c11[2];
|
|
O2ImageReadTileSpanExtendEdge__largb32f_PRE(self, u, v + 1, c01c11, 2);
|
|
|
|
O2Float fu = uv.x - (O2Float) u;
|
|
O2Float fv = uv.y - (O2Float) v;
|
|
O2argb32f c0 =
|
|
O2argb32fAdd(O2argb32fMultiplyByFloat(c00c01[0], (1.0f - fu)),
|
|
O2argb32fMultiplyByFloat(c00c01[1], fu));
|
|
O2argb32f c1 =
|
|
O2argb32fAdd(O2argb32fMultiplyByFloat(c01c11[0], (1.0f - fu)),
|
|
O2argb32fMultiplyByFloat(c01c11[1], fu));
|
|
span[i] = O2argb32fAdd(O2argb32fMultiplyByFloat(c0, (1.0f - fv)),
|
|
O2argb32fMultiplyByFloat(c1, fv));
|
|
|
|
du += surfaceToImage.a;
|
|
dv += surfaceToImage.b;
|
|
}
|
|
}
|
|
|
|
void O2ImagePointSampling_largb8u_PRE(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length,
|
|
O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++, x++) {
|
|
O2Point uv = O2PointMake(du, dv);
|
|
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, RI_FLOOR_TO_INT(uv.x),
|
|
RI_FLOOR_TO_INT(uv.y),
|
|
span + i, 1);
|
|
|
|
du += surfaceToImage.a;
|
|
dv += surfaceToImage.b;
|
|
}
|
|
}
|
|
|
|
void O2ImagePointSampling_largb32f_PRE(O2Image *self, int x, int y,
|
|
O2argb32f *span, int length,
|
|
O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++, x++) {
|
|
O2Point uv = O2PointMake(du, dv);
|
|
|
|
O2ImageReadTileSpanExtendEdge__largb32f_PRE(self, RI_FLOOR_TO_INT(uv.x),
|
|
RI_FLOOR_TO_INT(uv.y),
|
|
span + i, 1);
|
|
|
|
du += surfaceToImage.a;
|
|
dv += surfaceToImage.b;
|
|
}
|
|
}
|
|
|
|
// Float translate, or float translate with flip
|
|
void O2ImageBilinearFloatTranslate_largb8u_PRE(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length,
|
|
O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
int i;
|
|
|
|
du -= 0.5;
|
|
dv -= 0.5;
|
|
|
|
O2Float uvx = du;
|
|
O2Float uvy = dv;
|
|
|
|
int u = RI_FLOOR_TO_INT(uvx);
|
|
int v = RI_FLOOR_TO_INT(uvy);
|
|
unsigned fu = coverageFromZeroToOne(uvx - (O2Float) u);
|
|
unsigned oneMinusFu = inverseCoverage(fu);
|
|
unsigned fv = coverageFromZeroToOne(uvy - (O2Float) v);
|
|
unsigned oneMinusFv = inverseCoverage(fv);
|
|
|
|
O2argb8u line0[length + 1];
|
|
O2argb8u line1[length + 1];
|
|
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, u, v + 0, line0,
|
|
length + 1);
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, u, v + 1, line1,
|
|
length + 1);
|
|
|
|
for (i = 0; i < length; i++) {
|
|
O2argb8u c0 = O2argb8uMultiplyByCoverageAdd(line0[i], oneMinusFu,
|
|
line0[i + 1], fu);
|
|
O2argb8u c1 = O2argb8uMultiplyByCoverageAdd(line1[i], oneMinusFu,
|
|
line1[i + 1], fu);
|
|
|
|
span[i] = O2argb8uMultiplyByCoverageAdd(c0, oneMinusFv, c1, fv);
|
|
}
|
|
}
|
|
|
|
// Translate or translate with -1 flip
|
|
void O2ImageIntegerTranslate_largb8u_PRE(O2Image *self, int x, int y,
|
|
O2argb8u *span, int length,
|
|
O2AffineTransform surfaceToImage)
|
|
{
|
|
int du = x + surfaceToImage.tx;
|
|
int dv = ((y + 0.5) * surfaceToImage.d + surfaceToImage.ty) - 0.5;
|
|
|
|
O2ImageReadTileSpanExtendEdge_largb8u_PRE(self, du, dv, span, length);
|
|
}
|
|
|
|
// clamp premultiplied color to alpha to enforce consistency
|
|
ONYX2D_STATIC void clampSpan_largb32f_PRE(O2argb32f *span, int length) {
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
span[i].r = RI_MIN(span[i].r, span[i].a);
|
|
span[i].g = RI_MIN(span[i].g, span[i].a);
|
|
span[i].b = RI_MIN(span[i].b, span[i].a);
|
|
}
|
|
}
|
|
|
|
ONYX2D_STATIC_INLINE void O2RGBPremultiplySpan(O2argb32f *span, int length) {
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
span[i].r *= span[i].a;
|
|
span[i].g *= span[i].a;
|
|
span[i].b *= span[i].a;
|
|
}
|
|
}
|
|
|
|
O2argb8u *O2Image_read_argb8u(O2Image *self, int x, int y, O2argb8u *span,
|
|
int length)
|
|
{
|
|
return self->_read_argb8u(self, x, y, span, length);
|
|
}
|
|
|
|
O2argb32f *O2Image_read_argb32f(O2Image *self, int x, int y, O2argb32f *span,
|
|
int length)
|
|
{
|
|
O2argb32f *direct = self->_read_argb32f(self, x, y, span, length);
|
|
|
|
if (direct != NULL)
|
|
span = direct;
|
|
|
|
if (self->_clampExternalPixels)
|
|
clampSpan_largb32f_PRE(span,
|
|
length); // We don't need to do this for
|
|
// internally generated images (context)
|
|
|
|
return NULL;
|
|
}
|
|
|
|
uint8_t *O2Image_read_a8u(O2Image *self, int x, int y, uint8_t *coverage,
|
|
int length)
|
|
{
|
|
return self->_read_a8u(self, x, y, coverage, length);
|
|
}
|
|
|
|
O2Float *O2ImageReadSpan_Af_MASK(O2Image *self, int x, int y, O2Float *coverage,
|
|
int length)
|
|
{
|
|
return self->_read_a32f(self, x, y, coverage, length);
|
|
}
|
|
|
|
void O2ImageReadTexelTileRepeat_largb8u_PRE(O2Image *self, int u, int v,
|
|
O2argb8u *span, int length)
|
|
{
|
|
int i;
|
|
|
|
v = RI_INT_MOD(v, self->_height);
|
|
|
|
for (i = 0; i < length; i++, u++) {
|
|
u = RI_INT_MOD(u, self->_width);
|
|
|
|
O2argb8u *direct = O2Image_read_argb8u(self, u, v, span + i, 1);
|
|
|
|
if (direct != NULL)
|
|
span[i] = direct[0];
|
|
}
|
|
}
|
|
|
|
void O2ImageReadTexelTileRepeat_largb32f_PRE(O2Image *self, int u, int v,
|
|
O2argb32f *span, int length)
|
|
{
|
|
int i;
|
|
|
|
v = RI_INT_MOD(v, self->_height);
|
|
|
|
for (i = 0; i < length; i++, u++) {
|
|
u = RI_INT_MOD(u, self->_width);
|
|
|
|
O2argb32f *direct = O2Image_read_argb32f(self, u, v, span + i, 1);
|
|
if (direct != NULL)
|
|
span[i] = direct[0];
|
|
}
|
|
}
|
|
|
|
void O2ImagePattern_Bilinear(O2Image *self, O2Float x, O2Float y,
|
|
O2argb32f *span, int length,
|
|
O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++, x++) {
|
|
O2Point uv = O2PointMake(du, dv);
|
|
|
|
uv.x -= 0.5f;
|
|
uv.y -= 0.5f;
|
|
int u = RI_FLOOR_TO_INT(uv.x);
|
|
int v = RI_FLOOR_TO_INT(uv.y);
|
|
O2argb32f c00c01[2];
|
|
O2ImageReadTexelTileRepeat_largb32f_PRE(self, u, v, c00c01, 2);
|
|
|
|
O2argb32f c01c11[2];
|
|
O2ImageReadTexelTileRepeat_largb32f_PRE(self, u, v + 1, c01c11, 2);
|
|
|
|
O2Float fu = uv.x - (O2Float) u;
|
|
O2Float fv = uv.y - (O2Float) v;
|
|
O2argb32f c0 =
|
|
O2argb32fAdd(O2argb32fMultiplyByFloat(c00c01[0], (1.0f - fu)),
|
|
O2argb32fMultiplyByFloat(c00c01[1], fu));
|
|
O2argb32f c1 =
|
|
O2argb32fAdd(O2argb32fMultiplyByFloat(c01c11[0], (1.0f - fu)),
|
|
O2argb32fMultiplyByFloat(c01c11[1], fu));
|
|
span[i] = O2argb32fAdd(O2argb32fMultiplyByFloat(c0, (1.0f - fv)),
|
|
O2argb32fMultiplyByFloat(c1, fv));
|
|
|
|
du += surfaceToImage.a;
|
|
dv += surfaceToImage.b;
|
|
}
|
|
}
|
|
|
|
void O2ImagePattern_PointSampling_largb8u_PRE(O2Image *self, O2Float x,
|
|
O2Float y, O2argb8u *span,
|
|
int length,
|
|
O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++, x++) {
|
|
O2Point uv = O2PointMake(du, dv);
|
|
|
|
O2ImageReadTexelTileRepeat_largb8u_PRE(self, RI_FLOOR_TO_INT(uv.x),
|
|
RI_FLOOR_TO_INT(uv.y), span + i,
|
|
1);
|
|
|
|
du += surfaceToImage.a;
|
|
dv += surfaceToImage.b;
|
|
}
|
|
}
|
|
|
|
void O2ImagePattern_PointSampling_largb32f_PRE(O2Image *self, O2Float x,
|
|
O2Float y, O2argb32f *span,
|
|
int length,
|
|
O2AffineTransform surfaceToImage)
|
|
{
|
|
double du = (x + 0.5) * surfaceToImage.a + (y + 0.5) * surfaceToImage.c +
|
|
surfaceToImage.tx;
|
|
double dv = (x + 0.5) * surfaceToImage.b + (y + 0.5) * surfaceToImage.d +
|
|
surfaceToImage.ty;
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++, x++) {
|
|
O2Point uv = O2PointMake(du, dv);
|
|
|
|
O2ImageReadTexelTileRepeat_largb32f_PRE(self, RI_FLOOR_TO_INT(uv.x),
|
|
RI_FLOOR_TO_INT(uv.y), span + i,
|
|
1);
|
|
|
|
du += surfaceToImage.a;
|
|
dv += surfaceToImage.b;
|
|
}
|
|
}
|
|
|
|
void O2ImageReadPatternSpan_largb8u_PRE(O2Image *self, O2Float x, O2Float y,
|
|
O2argb8u *span, int length,
|
|
O2AffineTransform surfaceToImage,
|
|
O2PatternTiling distortion)
|
|
{
|
|
|
|
switch (distortion) {
|
|
case kO2PatternTilingNoDistortion:
|
|
case kO2PatternTilingConstantSpacingMinimalDistortion:
|
|
case kO2PatternTilingConstantSpacing:
|
|
O2ImagePattern_PointSampling_largb8u_PRE(self, x, y, span, length,
|
|
surfaceToImage);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void O2ImageReadPatternSpan_largb32f_PRE(O2Image *self, O2Float x, O2Float y,
|
|
O2argb32f *span, int length,
|
|
O2AffineTransform surfaceToImage,
|
|
O2PatternTiling distortion)
|
|
{
|
|
|
|
switch (distortion) {
|
|
case kO2PatternTilingNoDistortion:
|
|
O2ImagePattern_PointSampling_largb32f_PRE(self, x, y, span, length,
|
|
surfaceToImage);
|
|
break;
|
|
|
|
case kO2PatternTilingConstantSpacingMinimalDistortion:
|
|
case kO2PatternTilingConstantSpacing:
|
|
default:
|
|
O2ImagePattern_Bilinear(self, x, y, span, length, surfaceToImage);
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (NSString *) description {
|
|
return [NSString
|
|
stringWithFormat:
|
|
@"<%@:%p> "
|
|
@"width=%d,height=%d,bpc=%d,bpp=%d,bpr=%d,bminfo=%x data "
|
|
@"length=%d",
|
|
[self class], self, _width, _height, _bitsPerComponent,
|
|
_bitsPerPixel, _bytesPerRow, _bitmapInfo,
|
|
[_provider length]];
|
|
}
|
|
|
|
@end
|