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 ImageBuffer::toDataURL(const String& mimeType, const double* quality) const { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); RetainPtr<CGImageRef> 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); }
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, 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 ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality) { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); RetainPtr<CGImageRef> image; RetainPtr<CGDataProviderRef> dataProvider; dataProvider.adoptCF(CGDataProviderCreateWithData(0, source.data()->data()->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); }