#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 <CoreGraphics/CoreGraphics.h>
#include <ImageIO/ImageIO.h>
#include <ImageIO/CGImageSourcePrivate.h>
#include <wtf/Assertions.h>
#include <wtf/OwnArrayPtr.h>
#include <wtf/RetainPtr.h>
using namespace std;
namespace WebCore {
ImageBufferData::ImageBufferData(const IntSize&)
: m_data(0)
{
}
ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success)
: m_data(size)
, m_size(size)
{
success = false; unsigned bytesPerRow;
if (size.width() < 0 || size.height() < 0)
return;
bytesPerRow = size.width();
if (!grayScale) {
if (bytesPerRow > 0x3FFFFFFF)
return;
bytesPerRow *= 4;
}
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(m_data.m_data, size.width(), size.height(), 8, bytesPerRow,
colorSpace, grayScale ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
if (!cgContext)
return;
m_context.set(new GraphicsContext(cgContext));
m_context->scale(FloatSize(1, -1));
m_context->translate(0, -size.height());
CGContextRelease(cgContext);
success = true;
}
ImageBuffer::~ImageBuffer()
{
fastFree(m_data.m_data);
}
GraphicsContext* ImageBuffer::context() const
{
return m_context.get();
}
Image* ImageBuffer::image() const
{
if (!m_image) {
ASSERT(context());
CGImageRef cgImage = CGBitmapContextCreateImage(context()->platformContext());
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();
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)
{
static const CFStringRef kUTTypePNG = CFSTR("public.png");
static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg");
static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif");
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;
}
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());
}
}