skia_utils_mac.mm 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Copyright (c) 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_mac.h"
  5. #import <AppKit/AppKit.h>
  6. #include <stdint.h>
  7. #include <memory>
  8. #include "base/check.h"
  9. #include "base/mac/mac_util.h"
  10. #include "base/mac/scoped_cftyperef.h"
  11. #include "base/mac/scoped_nsobject.h"
  12. #include "skia/ext/platform_canvas.h"
  13. #include "third_party/skia/include/utils/mac/SkCGUtils.h"
  14. namespace {
  15. // Draws an NSImage or an NSImageRep with a given size into a SkBitmap.
  16. SkBitmap NSImageOrNSImageRepToSkBitmapWithColorSpace(
  17. NSImage* image,
  18. NSImageRep* image_rep,
  19. NSSize size,
  20. bool is_opaque,
  21. CGColorSpaceRef color_space) {
  22. // Only image or image_rep should be provided, not both.
  23. DCHECK((image != nullptr) ^ (image_rep != nullptr));
  24. SkBitmap bitmap;
  25. if (!bitmap.tryAllocN32Pixels(size.width, size.height, is_opaque))
  26. return bitmap; // Return |bitmap| which should respond true to isNull().
  27. void* data = bitmap.getPixels();
  28. // Allocate a bitmap context with 4 components per pixel (BGRA). Apple
  29. // recommends these flags for improved CG performance.
  30. #define HAS_ARGB_SHIFTS(a, r, g, b) \
  31. (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
  32. && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
  33. #if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
  34. base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
  35. data, size.width, size.height, 8, size.width * 4, color_space,
  36. uint32_t{kCGImageAlphaPremultipliedFirst} | kCGBitmapByteOrder32Host));
  37. #else
  38. #error We require that Skia's and CoreGraphics's recommended \
  39. image memory layout match.
  40. #endif
  41. #undef HAS_ARGB_SHIFTS
  42. // Something went really wrong. Best guess is that the bitmap data is invalid.
  43. DCHECK(context);
  44. [NSGraphicsContext saveGraphicsState];
  45. NSGraphicsContext* context_cocoa =
  46. [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
  47. [NSGraphicsContext setCurrentContext:context_cocoa];
  48. NSRect drawRect = NSMakeRect(0, 0, size.width, size.height);
  49. if (image) {
  50. [image drawInRect:drawRect
  51. fromRect:NSZeroRect
  52. operation:NSCompositingOperationCopy
  53. fraction:1.0];
  54. } else {
  55. [image_rep drawInRect:drawRect
  56. fromRect:NSZeroRect
  57. operation:NSCompositingOperationCopy
  58. fraction:1.0
  59. respectFlipped:NO
  60. hints:nil];
  61. }
  62. [NSGraphicsContext restoreGraphicsState];
  63. return bitmap;
  64. }
  65. } // namespace
  66. namespace skia {
  67. CGAffineTransform SkMatrixToCGAffineTransform(const SkMatrix& matrix) {
  68. // CGAffineTransforms don't support perspective transforms, so make sure
  69. // we don't get those.
  70. DCHECK(matrix[SkMatrix::kMPersp0] == 0.0f);
  71. DCHECK(matrix[SkMatrix::kMPersp1] == 0.0f);
  72. DCHECK(matrix[SkMatrix::kMPersp2] == 1.0f);
  73. return CGAffineTransformMake(matrix[SkMatrix::kMScaleX],
  74. matrix[SkMatrix::kMSkewY],
  75. matrix[SkMatrix::kMSkewX],
  76. matrix[SkMatrix::kMScaleY],
  77. matrix[SkMatrix::kMTransX],
  78. matrix[SkMatrix::kMTransY]);
  79. }
  80. SkRect CGRectToSkRect(const CGRect& rect) {
  81. SkRect sk_rect = {
  82. rect.origin.x, rect.origin.y, CGRectGetMaxX(rect), CGRectGetMaxY(rect)
  83. };
  84. return sk_rect;
  85. }
  86. CGRect SkIRectToCGRect(const SkIRect& rect) {
  87. CGRect cg_rect = {
  88. { rect.fLeft, rect.fTop },
  89. { rect.fRight - rect.fLeft, rect.fBottom - rect.fTop }
  90. };
  91. return cg_rect;
  92. }
  93. CGRect SkRectToCGRect(const SkRect& rect) {
  94. CGRect cg_rect = {
  95. { rect.fLeft, rect.fTop },
  96. { rect.fRight - rect.fLeft, rect.fBottom - rect.fTop }
  97. };
  98. return cg_rect;
  99. }
  100. SkColor NSSystemColorToSkColor(NSColor* color) {
  101. // System colors use the an NSNamedColorSpace called "System", so first step
  102. // is to convert the color into something that can be worked with.
  103. NSColor* device_color =
  104. [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
  105. if (device_color)
  106. return NSDeviceColorToSkColor(device_color);
  107. // Sometimes the conversion is not possible, but we can get an approximation
  108. // by going through a CGColorRef. Note that simply using NSColor methods for
  109. // accessing components for system colors results in exceptions like
  110. // "-numberOfComponents not valid for the NSColor NSNamedColorSpace System
  111. // windowBackgroundColor; need to first convert colorspace." Hence the
  112. // conversion first to CGColor.
  113. CGColorRef cg_color = [color CGColor];
  114. const size_t component_count = CGColorGetNumberOfComponents(cg_color);
  115. if (component_count == 4)
  116. return CGColorRefToSkColor(cg_color);
  117. CHECK(component_count == 1 || component_count == 2);
  118. // 1-2 components means a grayscale channel and maybe an alpha channel, which
  119. // CGColorRefToSkColor will not like. But RGB is additive, so the conversion
  120. // is easy (RGB to grayscale is less easy).
  121. const CGFloat* components = CGColorGetComponents(cg_color);
  122. CGFloat alpha = component_count == 2 ? components[1] : 1.0f;
  123. return SkColor4f{components[0], components[0], components[0], alpha}
  124. .toSkColor();
  125. }
  126. SkColor CGColorRefToSkColor(CGColorRef color) {
  127. base::ScopedCFTypeRef<CGColorRef> cg_color(
  128. CGColorCreateCopyByMatchingToColorSpace(base::mac::GetSRGBColorSpace(),
  129. kCGRenderingIntentDefault, color,
  130. nullptr));
  131. DCHECK(CGColorGetNumberOfComponents(color) == 4);
  132. const CGFloat* components = CGColorGetComponents(cg_color);
  133. return SkColor4f{components[0], components[1], components[2], components[3]}
  134. .toSkColor();
  135. }
  136. base::ScopedCFTypeRef<CGColorRef> CGColorCreateFromSkColor(SkColor color) {
  137. CGFloat components[] = {
  138. SkColorGetR(color) / 255.0f, SkColorGetG(color) / 255.0f,
  139. SkColorGetB(color) / 255.0f, SkColorGetA(color) / 255.0f};
  140. return base::ScopedCFTypeRef<CGColorRef>(
  141. CGColorCreate(base::mac::GetSRGBColorSpace(), components));
  142. }
  143. // Converts NSColor to ARGB
  144. SkColor NSDeviceColorToSkColor(NSColor* color) {
  145. DCHECK([color colorSpace] == [NSColorSpace genericRGBColorSpace] ||
  146. [color colorSpace] == [NSColorSpace deviceRGBColorSpace]);
  147. CGFloat red, green, blue, alpha;
  148. color = [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
  149. [color getRed:&red green:&green blue:&blue alpha:&alpha];
  150. return SkColor4f{red, green, blue, alpha}.toSkColor();
  151. }
  152. // Converts ARGB to NSColor.
  153. NSColor* SkColorToCalibratedNSColor(SkColor color) {
  154. return [NSColor colorWithCalibratedRed:SkColorGetR(color) / 255.0f
  155. green:SkColorGetG(color) / 255.0f
  156. blue:SkColorGetB(color) / 255.0f
  157. alpha:SkColorGetA(color) / 255.0f];
  158. }
  159. NSColor* SkColorToDeviceNSColor(SkColor color) {
  160. return [NSColor colorWithDeviceRed:SkColorGetR(color) / 255.0f
  161. green:SkColorGetG(color) / 255.0f
  162. blue:SkColorGetB(color) / 255.0f
  163. alpha:SkColorGetA(color) / 255.0f];
  164. }
  165. NSColor* SkColorToSRGBNSColor(SkColor color) {
  166. const CGFloat components[] = {
  167. SkColorGetR(color) / 255.0f, SkColorGetG(color) / 255.0f,
  168. SkColorGetB(color) / 255.0f, SkColorGetA(color) / 255.0f};
  169. return [NSColor colorWithColorSpace:[NSColorSpace sRGBColorSpace]
  170. components:components
  171. count:4];
  172. }
  173. SkBitmap CGImageToSkBitmap(CGImageRef image) {
  174. SkBitmap bitmap;
  175. if (image && SkCreateBitmapFromCGImage(&bitmap, image))
  176. return bitmap;
  177. return SkBitmap();
  178. }
  179. SkBitmap NSImageToSkBitmapWithColorSpace(
  180. NSImage* image, bool is_opaque, CGColorSpaceRef color_space) {
  181. return NSImageOrNSImageRepToSkBitmapWithColorSpace(
  182. image, nil, [image size], is_opaque, color_space);
  183. }
  184. SkBitmap NSImageRepToSkBitmapWithColorSpace(NSImageRep* image_rep,
  185. NSSize size,
  186. bool is_opaque,
  187. CGColorSpaceRef color_space) {
  188. return NSImageOrNSImageRepToSkBitmapWithColorSpace(
  189. nil, image_rep, size, is_opaque, color_space);
  190. }
  191. NSBitmapImageRep* SkBitmapToNSBitmapImageRepWithColorSpace(
  192. const SkBitmap& skiaBitmap,
  193. CGColorSpaceRef colorSpace) {
  194. // First convert SkBitmap to CGImageRef.
  195. base::ScopedCFTypeRef<CGImageRef> cgimage(
  196. SkCreateCGImageRefWithColorspace(skiaBitmap, colorSpace));
  197. if (!cgimage)
  198. return nil;
  199. // Now convert to NSBitmapImageRep.
  200. base::scoped_nsobject<NSBitmapImageRep> bitmap(
  201. [[NSBitmapImageRep alloc] initWithCGImage:cgimage]);
  202. return [bitmap.release() autorelease];
  203. }
  204. NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& skiaBitmap,
  205. CGColorSpaceRef colorSpace) {
  206. if (skiaBitmap.isNull())
  207. return nil;
  208. base::scoped_nsobject<NSImage> image([[NSImage alloc] init]);
  209. NSBitmapImageRep* imageRep =
  210. SkBitmapToNSBitmapImageRepWithColorSpace(skiaBitmap, colorSpace);
  211. if (!imageRep)
  212. return nil;
  213. [image addRepresentation:imageRep];
  214. [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())];
  215. return [image.release() autorelease];
  216. }
  217. } // namespace skia