| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/gfx/image/image_platform.h" |
| |
| #import <AppKit/AppKit.h> |
| #include <stddef.h> |
| |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/logging.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/image/image_internal.h" |
| #include "ui/gfx/image/image_png_rep.h" |
| |
| namespace { |
| |
| // Returns a 16x16 red NSImage to visually show when a NSImage cannot be |
| // created from PNG data. |
| NSImage* GetErrorNSImage() { |
| return [NSImage imageWithSize:NSMakeSize(16, 16) |
| flipped:NO |
| drawingHandler:^(NSRect rect) { |
| [NSColor.redColor set]; |
| NSRectFill(rect); |
| return YES; |
| }]; |
| } |
| |
| } // namespace |
| |
| namespace gfx { |
| |
| namespace internal { |
| |
| class ImageRepCocoa final : public ImageRep { |
| public: |
| explicit ImageRepCocoa(NSImage* image) |
| : ImageRep(Image::kImageRepCocoa), image_(image) { |
| CHECK(image_); |
| } |
| |
| ImageRepCocoa(const ImageRepCocoa&) = delete; |
| ImageRepCocoa& operator=(const ImageRepCocoa&) = delete; |
| |
| ~ImageRepCocoa() override = default; |
| |
| int Width() const override { return Size().width(); } |
| |
| int Height() const override { return Size().height(); } |
| |
| gfx::Size Size() const override { |
| int width = static_cast<int>(image_.size.width); |
| int height = static_cast<int>(image_.size.height); |
| return gfx::Size(width, height); |
| } |
| |
| NSImage* image() const { return image_; } |
| |
| private: |
| NSImage* __strong image_; |
| }; |
| |
| const ImageRepCocoa* ImageRep::AsImageRepCocoa() const { |
| CHECK_EQ(type_, Image::kImageRepCocoa); |
| return reinterpret_cast<const ImageRepCocoa*>(this); |
| } |
| ImageRepCocoa* ImageRep::AsImageRepCocoa() { |
| return const_cast<ImageRepCocoa*>( |
| static_cast<const ImageRep*>(this)->AsImageRepCocoa()); |
| } |
| |
| scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage( |
| NSImage* nsimage) { |
| DCHECK(nsimage); |
| |
| // This function is to get the 1x bytes, so explicitly specify an identity |
| // transform when extracting the pixels from this NSImage to get that 1x. |
| NSDictionary<NSImageHintKey, id>* hints = |
| @{NSImageHintCTM : [NSAffineTransform transform]}; |
| CGImageRef cg_image = [nsimage CGImageForProposedRect:nullptr |
| context:nil |
| hints:hints]; |
| if (!cg_image) { |
| // TODO(crbug.com/1271762): Look at DumpWithoutCrashing() reports to figure |
| // out what's going on here. |
| return scoped_refptr<base::RefCountedMemory>(); |
| } |
| NSBitmapImageRep* ns_bitmap = |
| [[NSBitmapImageRep alloc] initWithCGImage:cg_image]; |
| NSData* ns_data = [ns_bitmap representationUsingType:NSBitmapImageFileTypePNG |
| properties:@{}]; |
| const unsigned char* bytes = static_cast<const unsigned char*>(ns_data.bytes); |
| scoped_refptr<base::RefCountedBytes> refcounted_bytes( |
| new base::RefCountedBytes()); |
| refcounted_bytes->data().assign(bytes, bytes + ns_data.length); |
| return refcounted_bytes; |
| } |
| |
| NSImage* NSImageFromPNG(const std::vector<gfx::ImagePNGRep>& image_png_reps) { |
| if (image_png_reps.empty()) { |
| LOG(ERROR) << "Unable to decode PNG."; |
| return GetErrorNSImage(); |
| } |
| |
| NSImage* image; |
| for (const auto& image_png_rep : image_png_reps) { |
| scoped_refptr<base::RefCountedMemory> png = image_png_rep.raw_data; |
| CHECK(png.get()); |
| NSData* ns_data = [[NSData alloc] initWithBytes:png->front() |
| length:png->size()]; |
| NSBitmapImageRep* ns_image_rep = |
| [[NSBitmapImageRep alloc] initWithData:ns_data]; |
| if (!ns_image_rep) { |
| LOG(ERROR) << "Unable to decode PNG at " << image_png_rep.scale << "."; |
| return GetErrorNSImage(); |
| } |
| if (!image) { |
| float scale = image_png_rep.scale; |
| NSSize image_size = NSMakeSize(ns_image_rep.pixelsWide / scale, |
| ns_image_rep.pixelsHigh / scale); |
| image = [[NSImage alloc] initWithSize:image_size]; |
| } |
| [image addRepresentation:ns_image_rep]; |
| } |
| |
| return image; |
| } |
| |
| NSImage* NSImageOfImageRepCocoa(const ImageRepCocoa* image_rep) { |
| return image_rep->image(); |
| } |
| |
| std::unique_ptr<ImageRep> MakeImageRepCocoa(NSImage* image) { |
| return std::make_unique<internal::ImageRepCocoa>(image); |
| } |
| |
| } // namespace internal |
| |
| Image::Image(NSImage* image) { |
| if (image) { |
| storage_ = new internal::ImageStorage(Image::kImageRepCocoa); |
| AddRepresentation(std::make_unique<internal::ImageRepCocoa>(image)); |
| } |
| } |
| |
| } // namespace gfx |