/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "mac/SkUniqueCFRef.h"
#include "SkCGUtils.h"
#include "SkEncodedOrigin.h"
#include "SkImageGeneratorCG.h"
#include "SkPixmapPriv.h"
#include "SkTemplates.h"
#ifdef SK_BUILD_FOR_MAC
#include <ApplicationServices/ApplicationServices.h>
#endif
#ifdef SK_BUILD_FOR_IOS
#include <CoreGraphics/CoreGraphics.h>
#include <ImageIO/ImageIO.h>
#include <MobileCoreServices/MobileCoreServices.h>
#endif
namespace {
class ImageGeneratorCG : public SkImageGenerator {
public:
ImageGeneratorCG(const SkImageInfo&, SkUniqueCFRef<CGImageSourceRef> imageSrc,
sk_sp<SkData> data, SkEncodedOrigin);
protected:
sk_sp<SkData> onRefEncodedData() override;
bool onGetPixels(const SkImageInfo&, void* pixels, size_t rowBytes, const Options&) override;
private:
const SkUniqueCFRef<CGImageSourceRef> fImageSrc;
const sk_sp<SkData> fData;
const SkEncodedOrigin fOrigin;
typedef SkImageGenerator INHERITED;
};
static SkUniqueCFRef<CGImageSourceRef> data_to_CGImageSrc(SkData* data) {
SkUniqueCFRef<CGDataProviderRef> cgData(
CGDataProviderCreateWithData(data, data->data(), data->size(), nullptr));
if (!cgData) {
return nullptr;
}
return SkUniqueCFRef<CGImageSourceRef>(
CGImageSourceCreateWithDataProvider(cgData.get(), nullptr));
}
} // namespace
std::unique_ptr<SkImageGenerator> SkImageGeneratorCG::MakeFromEncodedCG(sk_sp<SkData> data) {
SkUniqueCFRef<CGImageSourceRef> imageSrc = data_to_CGImageSrc(data.get());
if (!imageSrc) {
return nullptr;
}
SkUniqueCFRef<CFDictionaryRef> properties(
CGImageSourceCopyPropertiesAtIndex(imageSrc.get(), 0, nullptr));
if (!properties) {
return nullptr;
}
CFNumberRef widthRef = static_cast<CFNumberRef>(
CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelWidth));
CFNumberRef heightRef = static_cast<CFNumberRef>(
CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelHeight));
if (nullptr == widthRef || nullptr == heightRef) {
return nullptr;
}
int width, height;
if (!CFNumberGetValue(widthRef , kCFNumberIntType, &width ) ||
!CFNumberGetValue(heightRef, kCFNumberIntType, &height))
{
return nullptr;
}
bool hasAlpha = bool(CFDictionaryGetValue(properties.get(), kCGImagePropertyHasAlpha));
SkAlphaType alphaType = hasAlpha ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
SkImageInfo info = SkImageInfo::MakeS32(width, height, alphaType);
SkEncodedOrigin origin = kDefault_SkEncodedOrigin;
CFNumberRef orientationRef = static_cast<CFNumberRef>(
CFDictionaryGetValue(properties.get(), kCGImagePropertyOrientation));
int originInt;
if (orientationRef && CFNumberGetValue(orientationRef, kCFNumberIntType, &originInt)) {
origin = (SkEncodedOrigin) originInt;
}
if (SkPixmapPriv::ShouldSwapWidthHeight(origin)) {
info = SkPixmapPriv::SwapWidthHeight(info);
}
// FIXME: We have the opportunity to extract color space information here,
// though I think it makes sense to wait until we understand how
// we want to communicate it to the generator.
return std::unique_ptr<SkImageGenerator>(new ImageGeneratorCG(info, std::move(imageSrc),
std::move(data), origin));
}
ImageGeneratorCG::ImageGeneratorCG(const SkImageInfo& info, SkUniqueCFRef<CGImageSourceRef> src,
sk_sp<SkData> data, SkEncodedOrigin origin)
: INHERITED(info)
, fImageSrc(std::move(src))
, fData(std::move(data))
, fOrigin(origin)
{}
sk_sp<SkData> ImageGeneratorCG::onRefEncodedData() {
return fData;
}
bool ImageGeneratorCG::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
const Options&)
{
if (kN32_SkColorType != info.colorType()) {
// FIXME: Support other colorTypes.
return false;
}
switch (info.alphaType()) {
case kOpaque_SkAlphaType:
if (kOpaque_SkAlphaType != this->getInfo().alphaType()) {
return false;
}
break;
case kPremul_SkAlphaType:
break;
default:
return false;
}
SkUniqueCFRef<CGImageRef> image(CGImageSourceCreateImageAtIndex(fImageSrc.get(), 0, nullptr));
if (!image) {
return false;
}
SkPixmap dst(info, pixels, rowBytes);
auto decode = [&image](const SkPixmap& pm) {
// FIXME: Using SkCopyPixelsFromCGImage (as opposed to swizzling
// ourselves) greatly restricts the color and alpha types that we
// support. If we swizzle ourselves, we can add support for:
// kUnpremul_SkAlphaType
// 16-bit per component RGBA
// kGray_8_SkColorType
// Additionally, it would be interesting to compare the performance
// of SkSwizzler with CG's built in swizzler.
return SkCopyPixelsFromCGImage(pm, image.get());
};
return SkPixmapPriv::Orient(dst, fOrigin, decode);
}