- Create the QRCode
| CIFilter<CIBlendWithMask> *f = CIFilter.QRCodeGenerator; |
| |
| f.message = [@"Message" dataUsingEncoding:NSASCIIStringEncoding]; |
| |
| f.correctionLevel = @"Q"; |
| |
| CIImage *qrcode = f.outputImage; |
- Overlay the icon
| CIImage *icon = [CIImage imageWithURL:url]; |
| |
| CGAffineTransform *t = CGAffineTransformMakeTranslation( |
| (qrcode.extent.width-icon.extent.width)/2.0, |
| (qrcode.extent.height-icon.extent.height)/2.0); |
| |
| icon = [icon imageByApplyingTransform:t]; |
| qrcode = [icon imageByCompositingOver:qrcode]; |
- Round off the corners
| static dispatch_once_t onceToken; |
| static CIWarpKernel *k; |
| dispatch_once(&onceToken, ^ { |
| k = [CIWarpKernel kernelWithFunctionName:name |
| fromMetalLibraryData:metalLibData() |
| error:nil]; |
| }); |
| |
| CGRect iExtent = image.extent; |
| qrcode = [k applyWithExtent:qrcode.extent |
| roiCallback:^CGRect(int i, CGRect r) { |
| return CGRectInset(r, -radius, -radius); } |
| inputImage:qrcode |
| arguments:@[[CIVector vectorWithCGRect:qrcode.extent], @(radius)]]; |
…and this code for the kernel should go in a separate .ci.metal source file:
| float2 bend_corners (float4 extent, float s, destination dest) |
| { |
| float2 p, dc = dest.coord(); |
| float ratio = 1.0; |
| |
| // Round lower left corner |
| p = float2(extent.x+s,extent.y+s); |
| if (dc.x < p.x && dc.y < p.y) { |
| float2 d = abs(dc - p); |
| ratio = min(d.x,d.y)/max(d.x,d.y); |
| ratio = sqrt(1.0 + ratio*ratio); |
| return (dc - p)*ratio + p; |
| } |
| |
| // Round lower right corner |
| p = float2(extent.x+extent.z-s, extent.y+s); |
| |
| if (dc.x > p.x && dc.y < p.y) { |
| float2 d = abs(dc - p); |
| ratio = min(d.x,d.y)/max(d.x,d.y); |
| ratio = sqrt(1.0 + ratio*ratio); |
| return (dc - p)*ratio + p; |
| } |
| |
| // Round upper left corner |
| p = float2(extent.x+s,extent.y+extent.w-s); |
| if (dc.x < p.x && dc.y > p.y) { |
| float2 d = abs(dc - p); |
| ratio = min(d.x,d.y)/max(d.x,d.y); |
| ratio = sqrt(1.0 + ratio*ratio); |
| return (dc - p)*ratio + p; |
| } |
| |
| // Round upper right corner |
| p = float2(extent.x+extent.z-s, extent.y+extent.w-s); |
| if (dc.x > p.x && dc.y > p.y) { |
| float2 d = abs(dc - p); |
| ratio = min(d.x,d.y)/max(d.x,d.y); |
| ratio = sqrt(1.0 + ratio*ratio); |
| return (dc - p)*ratio + p; |
| } |
| |
| return dc; |
| } |