diff options
Diffstat (limited to 'WebCore/platform/graphics/cg/ImageBufferCG.cpp')
-rw-r--r-- | WebCore/platform/graphics/cg/ImageBufferCG.cpp | 244 |
1 files changed, 209 insertions, 35 deletions
diff --git a/WebCore/platform/graphics/cg/ImageBufferCG.cpp b/WebCore/platform/graphics/cg/ImageBufferCG.cpp index 2d1ac01..502313b 100644 --- a/WebCore/platform/graphics/cg/ImageBufferCG.cpp +++ b/WebCore/platform/graphics/cg/ImageBufferCG.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,65 +21,69 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "ImageBuffer.h" +#include "Base64.h" +#include "BitmapImage.h" +#include "CString.h" #include "GraphicsContext.h" - +#include "ImageData.h" +#include "MIMETypeRegistry.h" +#include "PlatformString.h" #include <ApplicationServices/ApplicationServices.h> #include <wtf/Assertions.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/RetainPtr.h> using namespace std; namespace WebCore { -auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize& size, bool grayScale) +ImageBufferData::ImageBufferData(const IntSize&) + : m_data(0) { +} + +ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) + : m_data(size) + , m_size(size) +{ + success = false; // Make early return mean failure. + unsigned bytesPerRow; if (size.width() < 0 || size.height() < 0) - return auto_ptr<ImageBuffer>(); - unsigned int bytesPerRow = size.width(); + return; + bytesPerRow = size.width(); if (!grayScale) { // Protect against overflow if (bytesPerRow > 0x3FFFFFFF) - return auto_ptr<ImageBuffer>(); + return; bytesPerRow *= 4; } - void* imageBuffer = fastCalloc(size.height(), bytesPerRow); - if (!imageBuffer) - return auto_ptr<ImageBuffer>(); - + m_data.m_data = tryFastCalloc(size.height(), bytesPerRow); + ASSERT((reinterpret_cast<size_t>(m_data.m_data) & 2) == 0); + CGColorSpaceRef colorSpace = grayScale ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB(); - CGContextRef cgContext = CGBitmapContextCreate(imageBuffer, size.width(), size.height(), 8, bytesPerRow, + CGContextRef cgContext = CGBitmapContextCreate(m_data.m_data, size.width(), size.height(), 8, bytesPerRow, colorSpace, grayScale ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colorSpace); - if (!cgContext) { - fastFree(imageBuffer); - return auto_ptr<ImageBuffer>(); - } + if (!cgContext) + return; - auto_ptr<GraphicsContext> context(new GraphicsContext(cgContext)); + m_context.set(new GraphicsContext(cgContext)); + m_context->scale(FloatSize(1, -1)); + m_context->translate(0, -size.height()); CGContextRelease(cgContext); - - return auto_ptr<ImageBuffer>(new ImageBuffer(imageBuffer, size, context)); -} - - -ImageBuffer::ImageBuffer(void* imageData, const IntSize& size, auto_ptr<GraphicsContext> context) - : m_data(imageData) - , m_size(size) - , m_context(context.release()) - , m_cgImage(0) -{ + success = true; } ImageBuffer::~ImageBuffer() { - fastFree(m_data); - CGImageRelease(m_cgImage); + fastFree(m_data.m_data); } GraphicsContext* ImageBuffer::context() const @@ -86,16 +91,185 @@ GraphicsContext* ImageBuffer::context() const return m_context.get(); } -CGImageRef ImageBuffer::cgImage() const +Image* ImageBuffer::image() const { - // It's assumed that if cgImage() is called, the actual rendering to the - // contained GraphicsContext must be done, as we create the CGImageRef here. - if (!m_cgImage) { + if (!m_image) { + // It's assumed that if image() is called, the actual rendering to the + // GraphicsContext must be done. ASSERT(context()); - m_cgImage = CGBitmapContextCreateImage(context()->platformContext()); + CGImageRef cgImage = CGBitmapContextCreateImage(context()->platformContext()); + // BitmapImage will release the passed in CGImage on destruction + m_image = BitmapImage::create(cgImage); + } + return m_image.get(); +} + +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const +{ + PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); + unsigned char* data = result->data()->data().data(); + + if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height()) + memset(data, 0, result->data()->length()); + + int originx = rect.x(); + int destx = 0; + if (originx < 0) { + destx = -originx; + originx = 0; + } + int endx = rect.x() + rect.width(); + if (endx > m_size.width()) + endx = m_size.width(); + int numColumns = endx - originx; + + int originy = rect.y(); + int desty = 0; + if (originy < 0) { + desty = -originy; + originy = 0; + } + int endy = rect.y() + rect.height(); + if (endy > m_size.height()) + endy = m_size.height(); + int numRows = endy - originy; + + unsigned srcBytesPerRow = 4 * m_size.width(); + unsigned destBytesPerRow = 4 * rect.width(); + + // ::create ensures that all ImageBuffers have valid data, so we don't need to check it here. + unsigned char* srcRows = reinterpret_cast<unsigned char*>(m_data.m_data) + originy * srcBytesPerRow + originx * 4; + unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + if (unsigned char alpha = srcRows[basex + 3]) { + destRows[basex] = (srcRows[basex] * 255) / alpha; + destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha; + destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha; + destRows[basex + 3] = alpha; + } else + reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; + } + srcRows += srcBytesPerRow; + destRows += destBytesPerRow; + } + return result; +} + +void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) +{ + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originx = sourceRect.x(); + int destx = destPoint.x() + sourceRect.x(); + ASSERT(destx >= 0); + ASSERT(destx < m_size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= m_size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < m_size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= m_size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * source->width(); + unsigned destBytesPerRow = 4 * m_size.width(); + + unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; + unsigned char* destRows = reinterpret_cast<unsigned char*>(m_data.m_data) + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + unsigned char alpha = srcRows[basex + 3]; + if (alpha != 255) { + destRows[basex] = (srcRows[basex] * alpha + 254) / 255; + destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255; + destRows[basex + 2] = (srcRows[basex + 2] * alpha + 254) / 255; + destRows[basex + 3] = alpha; + } else + reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; + } + destRows += destBytesPerRow; + srcRows += srcBytesPerRow; } +} + +static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType) +{ +#if PLATFORM(MAC) + RetainPtr<CFStringRef> mimeTypeCFString(AdoptCF, mimeType.createCFString()); + return RetainPtr<CFStringRef>(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeTypeCFString.get(), 0)); +#else + // FIXME: Add Windows support for all the supported UTIs when a way to convert from MIMEType to UTI reliably is found. + // For now, only support PNG, JPEG, and GIF. See <rdar://problem/6095286>. + static const CFStringRef kUTTypePNG = CFSTR("public.png"); + static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg"); + static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif"); - return m_cgImage; + if (equalIgnoringCase(mimeType, "image/png")) + return kUTTypePNG; + if (equalIgnoringCase(mimeType, "image/jpeg")) + return kUTTypeJPEG; + if (equalIgnoringCase(mimeType, "image/gif")) + return kUTTypeGIF; + + ASSERT_NOT_REACHED(); + return kUTTypePNG; +#endif } +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()); } + +} // namespace WebCore |