skia_utils_ios.mm 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "skia/ext/skia_utils_ios.h"
  5. #import <ImageIO/ImageIO.h>
  6. #import <UIKit/UIKit.h>
  7. #include <stddef.h>
  8. #include <stdint.h>
  9. #include "base/ios/ios_util.h"
  10. #include "base/logging.h"
  11. #include "base/mac/scoped_cftyperef.h"
  12. #include "third_party/skia/include/utils/mac/SkCGUtils.h"
  13. namespace {
  14. const uint8_t kICOHeaderMagic[4] = {0x00, 0x00, 0x01, 0x00};
  15. // Returns whether the data encodes an ico image.
  16. bool EncodesIcoImage(NSData* image_data) {
  17. if (image_data.length < std::size(kICOHeaderMagic))
  18. return false;
  19. return memcmp(kICOHeaderMagic, image_data.bytes,
  20. std::size(kICOHeaderMagic)) == 0;
  21. }
  22. } // namespace
  23. namespace skia {
  24. SkBitmap CGImageToSkBitmap(CGImageRef image, CGSize size, bool is_opaque) {
  25. SkBitmap bitmap;
  26. if (!image)
  27. return bitmap;
  28. if (!bitmap.tryAllocN32Pixels(size.width, size.height, is_opaque))
  29. return bitmap;
  30. void* data = bitmap.getPixels();
  31. // Allocate a bitmap context with 4 components per pixel (BGRA). Apple
  32. // recommends these flags for improved CG performance.
  33. #define HAS_ARGB_SHIFTS(a, r, g, b) \
  34. (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
  35. && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
  36. #if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
  37. base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
  38. CGColorSpaceCreateDeviceRGB());
  39. base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
  40. data, size.width, size.height, 8, size.width * 4, color_space,
  41. uint32_t{kCGImageAlphaPremultipliedFirst} | kCGBitmapByteOrder32Host));
  42. #else
  43. #error We require that Skia's and CoreGraphics's recommended \
  44. image memory layout match.
  45. #endif
  46. #undef HAS_ARGB_SHIFTS
  47. DCHECK(context);
  48. if (!context)
  49. return bitmap;
  50. CGRect imageRect = CGRectMake(0.0, 0.0, size.width, size.height);
  51. CGContextSetBlendMode(context, kCGBlendModeCopy);
  52. CGContextDrawImage(context, imageRect, image);
  53. return bitmap;
  54. }
  55. UIImage* SkBitmapToUIImageWithColorSpace(const SkBitmap& skia_bitmap,
  56. CGFloat scale,
  57. CGColorSpaceRef color_space) {
  58. if (skia_bitmap.isNull())
  59. return nil;
  60. // First convert SkBitmap to CGImageRef.
  61. base::ScopedCFTypeRef<CGImageRef> cg_image(
  62. SkCreateCGImageRefWithColorspace(skia_bitmap, color_space));
  63. // Now convert to UIImage.
  64. return [UIImage imageWithCGImage:cg_image.get()
  65. scale:scale
  66. orientation:UIImageOrientationUp];
  67. }
  68. std::vector<SkBitmap> ImageDataToSkBitmaps(NSData* image_data) {
  69. return ImageDataToSkBitmapsWithMaxSize(image_data, CGFLOAT_MAX);
  70. }
  71. std::vector<SkBitmap> ImageDataToSkBitmapsWithMaxSize(NSData* image_data,
  72. CGFloat max_size) {
  73. DCHECK(image_data);
  74. // On iOS 8.1.1 |CGContextDrawImage| crashes when processing images included
  75. // in .ico files that are 88x88 pixels or larger (http://crbug.com/435068).
  76. bool skip_images_88x88_or_larger =
  77. base::ios::IsRunningOnOrLater(8, 1, 1) && EncodesIcoImage(image_data);
  78. base::ScopedCFTypeRef<CFDictionaryRef> empty_dictionary(
  79. CFDictionaryCreate(NULL, NULL, NULL, 0, NULL, NULL));
  80. std::vector<SkBitmap> frames;
  81. base::ScopedCFTypeRef<CGImageSourceRef> source(
  82. CGImageSourceCreateWithData((CFDataRef)image_data, empty_dictionary));
  83. size_t count = CGImageSourceGetCount(source);
  84. for (size_t index = 0; index < count; ++index) {
  85. base::ScopedCFTypeRef<CGImageRef> cg_image(
  86. CGImageSourceCreateImageAtIndex(source, index, empty_dictionary));
  87. CGSize size = CGSizeMake(CGImageGetWidth(cg_image),
  88. CGImageGetHeight(cg_image));
  89. if (size.width > max_size || size.height > max_size)
  90. continue;
  91. if (size.width >= 88 && size.height >= 88 && skip_images_88x88_or_larger)
  92. continue;
  93. const SkBitmap bitmap = CGImageToSkBitmap(cg_image, size, false);
  94. if (!bitmap.empty())
  95. frames.push_back(bitmap);
  96. }
  97. DLOG_IF(WARNING, frames.size() != count) << "Only decoded " << frames.size()
  98. << " frames for " << count << " expected.";
  99. return frames;
  100. }
  101. UIColor* UIColorFromSkColor(SkColor color) {
  102. return [UIColor colorWithRed:SkColorGetR(color) / 255.0f
  103. green:SkColorGetG(color) / 255.0f
  104. blue:SkColorGetB(color) / 255.0f
  105. alpha:SkColorGetA(color) / 255.0f];
  106. }
  107. } // namespace skia