Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Since <code>CICircularWrap</code> is not supported on iOS <strong>(EDIT: it is now - check answer below)</strong>, one has to code his own effect for now. Probably the simplest way is to compute the transformation from polar to cartesian coordinate systems and then interpolate from the source image. I've come up with this simple (and frankly quite slow - it can be much optimised) algorithm:</p> <pre><code> #import &lt;QuartzCore/QuartzCore.h&gt; 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 red, 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 rendered. 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 &lt; 0 || bottomRadius &lt; 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&lt;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),&amp;temp); if(!clockWise) phi = 1-phi; phi*=w; CGFloat r = ((sqrtf(x*x+y*y))-topRadius)*h/(bottomRadius-topRadius); if(phi&gt;=0 &amp;&amp; phi&lt;w &amp;&amp; r&gt;=0 &amp;&amp; r&lt;h) { if(!interpolate || phi &gt;= w-1 || r&gt;=h-1) { //pick the closest pixel *p = data1[(int)r*w+(int)phi]; } else { double dphi = modf(phi, &amp;temp); double dr = modf(r, &amp;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 &lt; 4; component++) { double avg = ((*c00 &amp; 0xFF)*(1-dphi)+(*c01 &amp; 0xFF)*dphi)*(1-dr)+((*c10 &amp; 0xFF)*(1-dphi)+(*c11 &amp; 0xFF)*dphi)*dr; *p += (((int)(avg))&lt;&lt;(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; } </code></pre> <p>Use the <code>circularWrap</code> function with parameters:</p> <ul> <li><code>CGImageRef inImage</code> the source image</li> <li><code>CGFloat bottomRadius</code> the bottom side of the source image will transform into a circle with this radius</li> <li><code>CGFloat topRadius</code> the same for the top side of the source image, this can be larger or smaler than the bottom radius. (results in wraping around top/bottom of the image)</li> <li><code>CGFloat startAngle</code> the angle in which the left and right sides of the source image will transform. <code>BOOL clockWise</code> direction of rendering</li> <li><code>BOOL interpolate</code> a simple anti-aliasing algorithm. Only the inside of the image is interpolated</li> </ul> <p>some samples (top left is the source image):<br> <img src="https://i.stack.imgur.com/T9zYg.jpg" alt="some samples (top left is the source image)"> with code:</p> <pre><code> 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)]; </code></pre> <p>enjoy! :)</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload