Image Circular Wrap en iOS

Tengo un problema: quiero crear una function de envoltura circular que envuelva una image como se muestra a continuación:

Envoltura de imagen

Esto está disponible en OSX, pero no está disponible en iOS.

Mi lógica hasta ahora ha sido:

Divida la image en secciones x y para cada sección:

  1. Gire grados alpha
  2. Escala la image en el eje x para crear un efecto 'distorsionado' en forma de diamante de la image
  3. Girar hacia atrás 90 - atan((h / 2) / (w / 2))
  4. Traducir el desplazamiento

Mi problema es que esto parece inexacto y no he podido resolver matemáticamente cómo hacer esto correctamente, cualquier ayuda sería apreciada de forma masiva.

Enlace a documentos OSX para CICircularWrap :

https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CoreImageFilterReference/Reference/reference.html#//apple_ref/doc/filter/ci/CICircularWrap

Como CICircularWrap no es compatible con iOS, uno tiene que codificar su propio efecto por ahora. Probablemente la forma más simple es calcular la transformación de sistemas de coorderadas polares a cartesianos y luego interpolar desde la image fuente. Se me ocurrió este algorithm simple (y francamente bastante lento: puede optimizarse mucho):

  #import <QuartzCore/QuartzCore.h> CGContextRef CreateARGBBitmapContext (size_t pixelsWide, size_t pixelsHigh) { CGContextRef context = NULL; CGColorSpaceRef colorSpace; void * bitmapData; int bitmapByteCount; int bitmapBytesPerRow; // Declare the number of bytes per row. Each pixel in the bitmap in this // example is represented by 4 bytes; 8 bits each of networking, green, blue, and // alpha. bitmapBytesPerRow = (int)(pixelsWide * 4); bitmapByteCount = (int)(bitmapBytesPerRow * pixelsHigh); // Use the generic RGB color space. colorSpace = CGColorSpaceCreateDeviceRGB(); if (colorSpace == NULL) { fprintf(stderr, "Error allocating color space\n"); return NULL; } // Allocate memory for image data. This is the destination in memory // where any drawing to the bitmap context will be rendenetworking. bitmapData = malloc( bitmapByteCount ); if (bitmapData == NULL) { fprintf (stderr, "Memory not allocated!"); CGColorSpaceRelease( colorSpace ); return NULL; } // Create the bitmap context. We want pre-multiplied ARGB, 8-bits // per component. Regardless of what the source image format is // (CMYK, Grayscale, and so on) it will be converted over to the format // specified here by CGBitmapContextCreate. context = CGBitmapContextCreate (bitmapData, pixelsWide, pixelsHigh, 8, // bits per component bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst); if (context == NULL) { free (bitmapData); fprintf (stderr, "Context not created!"); } // Make sure and release colorspace before returning CGColorSpaceRelease( colorSpace ); return context; } CGImageRef circularWrap(CGImageRef inImage,CGFloat bottomRadius, CGFloat topRadius, CGFloat startAngle, BOOL clockWise, BOOL interpolate) { if(topRadius < 0 || bottomRadius < 0) return NULL; // Create the bitmap context int w = (int)CGImageGetWidth(inImage); int h = (int)CGImageGetHeight(inImage); //result image side size (always a square image) int resultSide = 2*MAX(topRadius, bottomRadius); CGContextRef cgctx1 = CreateARGBBitmapContext(w,h); CGContextRef cgctx2 = CreateARGBBitmapContext(resultSide,resultSide); if (cgctx1 == NULL || cgctx2 == NULL) { return NULL; } // Get image width, height. We'll use the entire image. CGRect rect = {{0,0},{w,h}}; // Draw the image to the bitmap context. Once we draw, the memory // allocated for the context for rendering will then contain the // raw image data in the specified color space. CGContextDrawImage(cgctx1, rect, inImage); // Now we can get a pointer to the image data associated with the bitmap // context. int *data1 = CGBitmapContextGetData (cgctx1); int *data2 = CGBitmapContextGetData (cgctx2); int resultImageSize = resultSide*resultSide; double temp; for(int *p = data2, pos = 0;pos<resultImageSize;p++,pos++) { *p = 0; int x = pos%resultSide-resultSide/2; int y = -pos/resultSide+resultSide/2; CGFloat phi = modf(((atan2(x, y)+startAngle)/2.0/M_PI+0.5),&temp); if(!clockWise) phi = 1-phi; phi*=w; CGFloat r = ((sqrtf(x*x+y*y))-topRadius)*h/(bottomRadius-topRadius); if(phi>=0 && phi<w && r>=0 && r<h) { if(!interpolate || phi >= w-1 || r>=h-1) { //pick the closest pixel *p = data1[(int)r*w+(int)phi]; } else { double dphi = modf(phi, &temp); double dr = modf(r, &temp); int8_t* c00 = (int8_t*)(data1+(int)r*w+(int)phi); int8_t* c01 = (int8_t*)(data1+(int)r*w+(int)phi+1); int8_t* c10 = (int8_t*)(data1+(int)r*w+w+(int)phi); int8_t* c11 = (int8_t*)(data1+(int)r*w+w+(int)phi+1); //interpolate components separately for(int component = 0; component < 4; component++) { double avg = ((*c00 & 0xFF)*(1-dphi)+(*c01 & 0xFF)*dphi)*(1-dr)+((*c10 & 0xFF)*(1-dphi)+(*c11 & 0xFF)*dphi)*dr; *p += (((int)(avg))<<(component*8)); c00++; c10++; c01++; c11++; } } } } CGImageRef result = CGBitmapContextCreateImage(cgctx2); // When finished, release the context CGContextRelease(cgctx1); CGContextRelease(cgctx2); // Free image data memory for the context if (data1) free(data1); if (data2) free(data2); return result; } 

Utilice la function circularWrap con parameters:

  • CGImageRef inImage la image de origen
  • CGFloat bottomRadius el lado inferior de la image fuente se transformará en un círculo con este radio
  • CGFloat topRadius igual para el lado superior de la image fuente, esto puede ser más grande o más pequeño que el radio inferior. (resulta en wraping alnetworkingedor de la parte superior / inferior de la image)
  • CGFloat startAngle el ángulo en el que se transformarán los lados izquierdo y derecho de la image fuente. BOOL clockWise dirección de renderizado
  • BOOL interpolate un simple algorithm de suavizado. Solo se interpola el interior de la image.

Algunas muestras (arriba a la izquierda es la image de origen):
algunas muestras (arriba a la izquierda es la imagen de origen) con el codigo:

  image1 = [UIImage imageWithCGImage:circularWrap(sourceImage.CGImage,0,300,0,YES,NO)]; image2 = [UIImage imageWithCGImage:circularWrap(sourceImage.CGImage,100,300,M_PI_2,NO,YES)]; image3 = [UIImage imageWithCGImage:circularWrap(sourceImage.CGImage,300,200,M_PI_4,NO,YES)]; image4 = [UIImage imageWithCGImage:circularWrap(sourceImage.CGImage,250,300,0,NO,NO)]; 

¡disfrutar! 🙂

Apple agregó CICircularWrap a iOS 9

https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CICircularWrap

Envuelve una image alnetworkingedor de un círculo transparente.

Nombre de visualización localizado

Distorsión de envoltura circular

Disponibilidad

Disponible en OS X v10.5 y posterior y en iOS 9 y posterior.