ImageBufferUtilitiesCG.cpp [plain text]
#include "config.h"
#include "ImageBufferUtilitiesCG.h"
#if USE(CG)
#include "GraphicsContextCG.h"
#include "ImageData.h"
#include "MIMETypeRegistry.h"
#include <ImageIO/ImageIO.h>
#include <wtf/CheckedArithmetic.h>
#include <wtf/text/Base64.h>
#if PLATFORM(COCOA)
#include "UTIUtilities.h"
#endif
namespace WebCore {
uint8_t verifyImageBufferIsBigEnough(const void* buffer, size_t bufferSize)
{
RELEASE_ASSERT(bufferSize);
uintptr_t lastByte;
bool isSafe = WTF::safeAdd((uintptr_t)buffer, bufferSize - 1, lastByte);
RELEASE_ASSERT(isSafe);
return *(uint8_t*)lastByte;
}
CFStringRef jpegUTI()
{
#if PLATFORM(IOS_FAMILY) || PLATFORM(WIN)
static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg");
#endif
return kUTTypeJPEG;
}
RetainPtr<CFStringRef> utiFromImageBufferMIMEType(const String& mimeType)
{
#if PLATFORM(MAC)
return UTIFromMIMEType(mimeType).createCFString();
#else
ASSERT(isMainThread());
static const CFStringRef kUTTypePNG = CFSTR("public.png");
static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif");
if (equalLettersIgnoringASCIICase(mimeType, "image/png"))
return kUTTypePNG;
if (equalLettersIgnoringASCIICase(mimeType, "image/jpeg"))
return jpegUTI();
if (equalLettersIgnoringASCIICase(mimeType, "image/gif"))
return kUTTypeGIF;
ASSERT_NOT_REACHED();
return kUTTypePNG;
#endif
}
bool encodeImage(CGImageRef image, CFStringRef uti, Optional<double> quality, CFMutableDataRef data)
{
if (!image || !uti || !data)
return false;
auto destination = adoptCF(CGImageDestinationCreateWithData(data, uti, 1, 0));
if (!destination)
return false;
RetainPtr<CFDictionaryRef> imageProperties;
if (CFEqual(uti, jpegUTI()) && quality && *quality >= 0.0 && *quality <= 1.0) {
auto compressionQuality = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &*quality));
const void* key = kCGImageDestinationLossyCompressionQuality;
const void* value = compressionQuality.get();
imageProperties = adoptCF(CFDictionaryCreate(0, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
}
CGImageDestinationAddImage(destination.get(), image, imageProperties.get());
return CGImageDestinationFinalize(destination.get());
}
static RetainPtr<CFDataRef> cfData(const ImageData& source, const String& mimeType, Optional<double> quality)
{
ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
auto uti = utiFromImageBufferMIMEType(mimeType);
ASSERT(uti);
CGImageAlphaInfo dataAlphaInfo = kCGImageAlphaLast;
unsigned char* data = source.data()->data();
Vector<uint8_t> premultipliedData;
if (CFEqual(uti.get(), jpegUTI())) {
size_t size = 4 * source.width() * source.height();
if (!premultipliedData.tryReserveCapacity(size))
return nullptr;
premultipliedData.grow(size);
unsigned char* buffer = premultipliedData.data();
for (size_t i = 0; i < size; i += 4) {
unsigned alpha = data[i + 3];
if (alpha != 255) {
buffer[i + 0] = data[i + 0] * alpha / 255;
buffer[i + 1] = data[i + 1] * alpha / 255;
buffer[i + 2] = data[i + 2] * alpha / 255;
} else {
buffer[i + 0] = data[i + 0];
buffer[i + 1] = data[i + 1];
buffer[i + 2] = data[i + 2];
}
}
dataAlphaInfo = kCGImageAlphaNoneSkipLast; data = premultipliedData.data();
}
verifyImageBufferIsBigEnough(data, 4 * source.width() * source.height());
auto dataProvider = adoptCF(CGDataProviderCreateWithData(0, data, 4 * source.width() * source.height(), 0));
if (!dataProvider)
return nullptr;
auto image = adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(), sRGBColorSpaceRef(), kCGBitmapByteOrderDefault | dataAlphaInfo, dataProvider.get(), 0, false, kCGRenderingIntentDefault));
auto cfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
if (!encodeImage(image.get(), uti.get(), quality, cfData.get()))
return nullptr;
return WTFMove(cfData);
}
String dataURL(CFDataRef data, const String& mimeType)
{
Vector<char> base64Data;
base64Encode(CFDataGetBytePtr(data), CFDataGetLength(data), base64Data);
return "data:" + mimeType + ";base64," + base64Data;
}
String dataURL(const ImageData& source, const String& mimeType, Optional<double> quality)
{
if (auto data = cfData(source, mimeType, quality))
return dataURL(data.get(), mimeType);
return "data:,"_s;
}
Vector<uint8_t> dataVector(CFDataRef cfData)
{
Vector<uint8_t> data;
data.append(CFDataGetBytePtr(cfData), CFDataGetLength(cfData));
return data;
}
Vector<uint8_t> data(const ImageData& source, const String& mimeType, Optional<double> quality)
{
if (auto data = cfData(source, mimeType, quality))
return dataVector(data.get());
return { };
}
}
#endif // USE(CG)