static String CGImageToDataURL(CGImageRef image, const String& mimeType, const double* quality) { RetainPtr<CFMutableDataRef> data(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0)); if (!data) return "data:,"; RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType); ASSERT(uti); RetainPtr<CGImageDestinationRef> destination(AdoptCF, CGImageDestinationCreateWithData(data.get(), uti.get(), 1, 0)); if (!destination) return "data:,"; RetainPtr<CFDictionaryRef> imageProperties = 0; if (CFEqual(uti.get(), jpegUTI()) && quality && *quality >= 0.0 && *quality <= 1.0) { // Apply the compression quality to the image destination. RetainPtr<CFNumberRef> compressionQuality(AdoptCF, CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, quality)); const void* key = kCGImageDestinationLossyCompressionQuality; const void* value = compressionQuality.get(); imageProperties.adoptCF(CFDictionaryCreate(0, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); } // Setting kCGImageDestinationBackgroundColor to black in imageProperties would allow saving some math in the // calling functions, but it doesn't seem to work. CGImageDestinationAddImage(destination.get(), image, imageProperties.get()); CGImageDestinationFinalize(destination.get()); Vector<char> out; base64Encode(reinterpret_cast<const char*>(CFDataGetBytePtr(data.get())), CFDataGetLength(data.get()), out); return "data:" + mimeType + ";base64," + out; }
String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); RetainPtr<CGImageRef> image; RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType); ASSERT(uti); RefPtr<ByteArray> arr; if (CFEqual(uti.get(), jpegUTI())) { // JPEGs don't have an alpha channel, so we have to manually composite on top of black. arr = getPremultipliedImageData(IntRect(IntPoint(0, 0), internalSize())); unsigned char *data = arr->data(); for (int i = 0; i < internalSize().width() * internalSize().height(); i++) data[i * 4 + 3] = 255; // The image is already premultiplied, so we just need to make it opaque. RetainPtr<CGDataProviderRef> dataProvider; dataProvider.adoptCF(CGDataProviderCreateWithData(0, data, 4 * internalSize().width() * internalSize().height(), 0)); if (!dataProvider) return "data:,"; image.adoptCF(CGImageCreate(internalSize().width(), internalSize().height(), 8, 32, 4 * internalSize().width(), CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrderDefault | kCGImageAlphaLast, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); } else image.adoptCF(copyNativeImage(CopyBackingStore)); if (!image) return "data:,"; return CGImageToDataURL(image.get(), mimeType, quality); }
String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality) { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); RetainPtr<CGImageRef> image; RetainPtr<CGDataProviderRef> dataProvider; unsigned char* data = source.data()->data()->data(); RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType); ASSERT(uti); Vector<uint8_t> dataVector; if (CFEqual(uti.get(), jpegUTI())) { // JPEGs don't have an alpha channel, so we have to manually composite on top of black. dataVector.resize(4 * source.width() * source.height()); unsigned char *out = dataVector.data(); for (int i = 0; i < source.width() * source.height(); i++) { // Multiply color data by alpha, and set alpha to 255. int alpha = data[4 * i + 3]; if (alpha != 255) { out[4 * i + 0] = data[4 * i + 0] * alpha / 255; out[4 * i + 1] = data[4 * i + 1] * alpha / 255; out[4 * i + 2] = data[4 * i + 2] * alpha / 255; } else { out[4 * i + 0] = data[4 * i + 0]; out[4 * i + 1] = data[4 * i + 1]; out[4 * i + 2] = data[4 * i + 2]; } out[4 * i + 3] = 255; } data = out; } dataProvider.adoptCF(CGDataProviderCreateWithData(0, data, 4 * source.width() * source.height(), 0)); if (!dataProvider) return "data:,"; image.adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(), CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrderDefault | kCGImageAlphaLast, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); if (!image) return "data:,"; return CGImageToDataURL(image.get(), mimeType, quality); }
static String CGImageToDataURL(CGImageRef image, const String& mimeType, const double* quality) { RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType); ASSERT(uti); RetainPtr<CFMutableDataRef> data = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0)); if (!CGImageEncodeToData(image, uti.get(), quality, data.get())) return "data:,"; Vector<char> base64Data; base64Encode(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get()), base64Data); return "data:" + mimeType + ";base64," + base64Data; }
String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality) { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType); ASSERT(uti); CGImageAlphaInfo dataAlphaInfo = kCGImageAlphaLast; unsigned char* data = source.data()->data(); Vector<uint8_t> premultipliedData; if (CFEqual(uti.get(), jpegUTI())) { // JPEGs don't have an alpha channel, so we have to manually composite on top of black. size_t size = 4 * source.width() * source.height(); if (!premultipliedData.tryReserveCapacity(size)) return "data:,"; premultipliedData.resize(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; // Ignore the alpha channel. data = premultipliedData.data(); } RetainPtr<CGDataProviderRef> dataProvider; dataProvider = adoptCF(CGDataProviderCreateWithData(0, data, 4 * source.width() * source.height(), 0)); if (!dataProvider) return "data:,"; RetainPtr<CGImageRef> image; image = adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(), deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | dataAlphaInfo, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); return CGImageToDataURL(image.get(), mimeType, quality); }
String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); RetainPtr<CGImageRef> image; if (!m_accelerateRendering) image.adoptCF(CGBitmapContextCreateImage(context()->platformContext())); #if USE(IOSURFACE_CANVAS_BACKING_STORE) else image.adoptCF(wkIOSurfaceContextCreateImage(context()->platformContext())); #endif if (!image) return "data:,"; RetainPtr<CFMutableDataRef> data(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0)); if (!data) return "data:,"; RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType); ASSERT(uti); RetainPtr<CGImageDestinationRef> destination(AdoptCF, CGImageDestinationCreateWithData(data.get(), uti.get(), 1, 0)); if (!destination) return "data:,"; RetainPtr<CFDictionaryRef> imageProperties = 0; if (CFEqual(uti.get(), jpegUTI()) && quality && *quality >= 0.0 && *quality <= 1.0) { // Apply the compression quality to the image destination. RetainPtr<CFNumberRef> 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.get(), imageProperties.get()); CGImageDestinationFinalize(destination.get()); Vector<char> out; base64Encode(reinterpret_cast<const char*>(CFDataGetBytePtr(data.get())), CFDataGetLength(data.get()), out); return makeString("data:", mimeType, ";base64,", out); }
String ImageBuffer::toDataURL(const String& mimeType, const double* quality, CoordinateSystem) const { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); if (context().isAcceleratedContext()) flushContext(); RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType); ASSERT(uti); RefPtr<Uint8ClampedArray> premultipliedData; RetainPtr<CGImageRef> image; if (CFEqual(uti.get(), jpegUTI())) { // JPEGs don't have an alpha channel, so we have to manually composite on top of black. premultipliedData = getPremultipliedImageData(IntRect(IntPoint(0, 0), logicalSize())); if (!premultipliedData) return "data:,"; RetainPtr<CGDataProviderRef> dataProvider; dataProvider = adoptCF(CGDataProviderCreateWithData(0, premultipliedData->data(), 4 * logicalSize().width() * logicalSize().height(), 0)); if (!dataProvider) return "data:,"; image = adoptCF(CGImageCreate(logicalSize().width(), logicalSize().height(), 8, 32, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); } else if (m_resolutionScale == 1) { image = copyNativeImage(CopyBackingStore); image = createCroppedImageIfNecessary(image.get(), internalSize()); } else { image = copyNativeImage(DontCopyBackingStore); RetainPtr<CGContextRef> context = adoptCF(CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast)); CGContextSetBlendMode(context.get(), kCGBlendModeCopy); CGContextClipToRect(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height())); FloatSize imageSizeInUserSpace = scaleSizeToUserSpace(logicalSize(), m_data.backingStoreSize, internalSize()); CGContextDrawImage(context.get(), CGRectMake(0, 0, imageSizeInUserSpace.width(), imageSizeInUserSpace.height()), image.get()); image = adoptCF(CGBitmapContextCreateImage(context.get())); } return CGImageToDataURL(image.get(), mimeType, quality); }
String ImageBuffer::toDataURL(const String& mimeType) const { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context()->platformContext())); if (!image) return "data:,"; size_t width = CGImageGetWidth(image.get()); size_t height = CGImageGetHeight(image.get()); OwnArrayPtr<uint32_t> imageData(new uint32_t[width * height]); if (!imageData) return "data:,"; RetainPtr<CGImageRef> transformedImage(AdoptCF, CGBitmapContextCreateImage(context()->platformContext())); if (!transformedImage) return "data:,"; RetainPtr<CFMutableDataRef> transformedImageData(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0)); if (!transformedImageData) return "data:,"; RetainPtr<CGImageDestinationRef> imageDestination(AdoptCF, CGImageDestinationCreateWithData(transformedImageData.get(), utiFromMIMEType(mimeType).get(), 1, 0)); if (!imageDestination) return "data:,"; CGImageDestinationAddImage(imageDestination.get(), transformedImage.get(), 0); CGImageDestinationFinalize(imageDestination.get()); Vector<char> in; in.append(CFDataGetBytePtr(transformedImageData.get()), CFDataGetLength(transformedImageData.get())); Vector<char> out; base64Encode(in, out); out.append('\0'); return String::format("data:%s;base64,%s", mimeType.utf8().data(), out.data()); }