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, Optional<double> quality, CoordinateSystem) const { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); cairo_surface_t* image = cairo_get_target(context().platformContext()->cr()); Vector<char> encodedImage; if (!image) return "data:,"; if (mimeType == "image/png") { if (!encodeImagePNG(image, &encodedImage)) return "data:,"; } if (mimeType == "image/jpeg") { int width = cairo_image_surface_get_width(image); int height = cairo_image_surface_get_height(image); IntSize size(width, height); IntRect dataRect(IntPoint(), size); RefPtr<Uint8ClampedArray> myData = getPremultipliedImageData(dataRect); if (!encodeImageJPEG(myData->data(), size, &encodedImage, quality)) return "data:,"; } Vector<char> base64Data; base64Encode(encodedImage, base64Data); return "data:" + mimeType + ";base64," + base64Data; }
String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const { if (m_size.isEmpty()) return "data:,"; enum { EncodeJPEG, EncodePNG, } encodeType = mimeType.lower() == "image/png" ? EncodePNG : EncodeJPEG; // According to http://www.w3.org/TR/html5/the-canvas-element.html, // "For image types that do not support an alpha channel, the image must be" // "composited onto a solid black background using the source-over operator," // "and the resulting image must be the one used to create the data: URL." // JPEG doesn't have alpha channel, so we need premultiplied data. RefPtr<ImageData> imageData = encodeType == EncodePNG ? getUnmultipliedImageData(IntRect(IntPoint(0, 0), m_size)) : getPremultipliedImageData(IntRect(IntPoint(0, 0), m_size)); ASSERT(imageData && imageData->width() == m_size.width() && imageData->height() == m_size.height()); Vector<char> output; const char* header; if (encodeType == EncodePNG) { if (!compressRGBABigEndianToPNG(imageData->data()->data()->data(), m_size, output)) return "data:,"; header = "data:image/png;base64,"; } else { if (!compressRGBABigEndianToJPEG(imageData->data()->data()->data(), m_size, output)) return "data:,"; header = "data:image/jpeg;base64,"; } Vector<char> base64; base64Encode(output, base64); output.clear(); Vector<char> url; url.append(header, strlen(header)); url.append(base64); return String(url.data(), url.size()); }
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); }