/*
 * 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 "include/ports/SkImageGeneratorCG.h"

#include "include/codec/SkEncodedOrigin.h"
#include "include/private/base/SkTemplates.h"
#include "include/utils/mac/SkCGUtils.h"
#include "src/codec/SkPixmapUtilsPriv.h"
#include "src/utils/mac/SkUniqueCFRef.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<const SkData> data,
                     SkEncodedOrigin);

protected:
    sk_sp<const SkData> onRefEncodedData() override;
    bool onGetPixels(const SkImageInfo&, void* pixels, size_t rowBytes, const Options&) override;

private:
    const SkUniqueCFRef<CGImageSourceRef> fImageSrc;
    const sk_sp<const SkData> fData;
    const SkEncodedOrigin fOrigin;

    using INHERITED = SkImageGenerator;
};

static SkUniqueCFRef<CGImageSourceRef> data_to_CGImageSrc(const SkData* data) {
    // We disable cgData from freeing the memory because data's lifetime will
    // be tracked with the fData field.
    SkUniqueCFRef<CGDataProviderRef> cgData(
            CGDataProviderCreateWithData(nullptr, 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<const 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 (SkEncodedOriginSwapsWidthHeight(origin)) {
        info = SkPixmapUtils::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<const SkData> data,
                                   SkEncodedOrigin origin)
        : INHERITED(info), fImageSrc(std::move(src)), fData(std::move(data)), fOrigin(origin) {}

sk_sp<const 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 SkPixmapUtils::Orient(dst, fOrigin, decode);
}
