RCTImageBlurUtils.m 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import <React/RCTImageBlurUtils.h>
  8. UIImage *RCTBlurredImageWithRadius(UIImage *inputImage, CGFloat radius)
  9. {
  10. CGImageRef imageRef = inputImage.CGImage;
  11. CGFloat imageScale = inputImage.scale;
  12. UIImageOrientation imageOrientation = inputImage.imageOrientation;
  13. // Image must be nonzero size
  14. if (CGImageGetWidth(imageRef) * CGImageGetHeight(imageRef) == 0) {
  15. return inputImage;
  16. }
  17. //convert to ARGB if it isn't
  18. if (CGImageGetBitsPerPixel(imageRef) != 32 ||
  19. CGImageGetBitsPerComponent(imageRef) != 8 ||
  20. !((CGImageGetBitmapInfo(imageRef) & kCGBitmapAlphaInfoMask))) {
  21. UIGraphicsBeginImageContextWithOptions(inputImage.size, NO, inputImage.scale);
  22. [inputImage drawAtPoint:CGPointZero];
  23. imageRef = UIGraphicsGetImageFromCurrentImageContext().CGImage;
  24. UIGraphicsEndImageContext();
  25. }
  26. vImage_Buffer buffer1, buffer2;
  27. buffer1.width = buffer2.width = CGImageGetWidth(imageRef);
  28. buffer1.height = buffer2.height = CGImageGetHeight(imageRef);
  29. buffer1.rowBytes = buffer2.rowBytes = CGImageGetBytesPerRow(imageRef);
  30. size_t bytes = buffer1.rowBytes * buffer1.height;
  31. buffer1.data = malloc(bytes);
  32. buffer2.data = malloc(bytes);
  33. if (!buffer1.data || !buffer2.data) {
  34. // CWE - 391 : Unchecked error condition
  35. // https://www.cvedetails.com/cwe-details/391/Unchecked-Error-Condition.html
  36. // https://eli.thegreenplace.net/2009/10/30/handling-out-of-memory-conditions-in-c
  37. abort();
  38. }
  39. // A description of how to compute the box kernel width from the Gaussian
  40. // radius (aka standard deviation) appears in the SVG spec:
  41. // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
  42. uint32_t boxSize = floor((radius * imageScale * 3 * sqrt(2 * M_PI) / 4 + 0.5) / 2);
  43. boxSize |= 1; // Ensure boxSize is odd
  44. //create temp buffer
  45. void *tempBuffer = malloc((size_t)vImageBoxConvolve_ARGB8888(&buffer1, &buffer2, NULL, 0, 0, boxSize, boxSize,
  46. NULL, kvImageEdgeExtend + kvImageGetTempBufferSize));
  47. if (!tempBuffer) {
  48. // CWE - 391 : Unchecked error condition
  49. // https://www.cvedetails.com/cwe-details/391/Unchecked-Error-Condition.html
  50. // https://eli.thegreenplace.net/2009/10/30/handling-out-of-memory-conditions-in-c
  51. abort();
  52. }
  53. //copy image data
  54. CFDataRef dataSource = CGDataProviderCopyData(CGImageGetDataProvider(imageRef));
  55. memcpy(buffer1.data, CFDataGetBytePtr(dataSource), bytes);
  56. CFRelease(dataSource);
  57. //perform blur
  58. vImageBoxConvolve_ARGB8888(&buffer1, &buffer2, tempBuffer, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
  59. vImageBoxConvolve_ARGB8888(&buffer2, &buffer1, tempBuffer, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
  60. vImageBoxConvolve_ARGB8888(&buffer1, &buffer2, tempBuffer, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
  61. //free buffers
  62. free(buffer2.data);
  63. free(tempBuffer);
  64. //create image context from buffer
  65. CGContextRef ctx = CGBitmapContextCreate(buffer1.data, buffer1.width, buffer1.height,
  66. 8, buffer1.rowBytes, CGImageGetColorSpace(imageRef),
  67. CGImageGetBitmapInfo(imageRef));
  68. //create image from context
  69. imageRef = CGBitmapContextCreateImage(ctx);
  70. UIImage *outputImage = [UIImage imageWithCGImage:imageRef scale:imageScale orientation:imageOrientation];
  71. CGImageRelease(imageRef);
  72. CGContextRelease(ctx);
  73. free(buffer1.data);
  74. return outputImage;
  75. }