/* * Copyright 2007, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * 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. */ #include "config.h" #include "ImageBuffer.h" #include "Base64.h" #include "BitmapImage.h" #include "ColorSpace.h" #include "GraphicsContext.h" #include "MIMETypeRegistry.h" #include "NotImplemented.h" #include "PlatformBridge.h" #include "PlatformGraphicsContext.h" #include "PlatformGraphicsContextSkia.h" #include "SkBitmapRef.h" #include "SkCanvas.h" #include "SkColorPriv.h" #include "SkData.h" #include "SkDevice.h" #include "SkImageEncoder.h" #include "SkStream.h" #include "SkUnPreMultiply.h" #include "image-encoders/skia/JPEGImageEncoder.h" #include "image-encoders/skia/PNGImageEncoder.h" #include using namespace std; namespace WebCore { SkCanvas* imageBufferCanvas(const ImageBuffer* buffer) { // We know that our PlatformGraphicsContext is a PlatformGraphicsContextSkia // because that is what we create in GraphicsContext::createOffscreenContext if (!buffer || !buffer->context()) return 0; PlatformGraphicsContext* pc = buffer->context()->platformContext(); return static_cast(pc)->canvas(); } ImageBufferData::ImageBufferData(const IntSize&) { } ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success) : m_data(size) , m_size(size) { // GraphicsContext creates a 32bpp SkBitmap, so 4 bytes per pixel. if (!PlatformBridge::canSatisfyMemoryAllocation(size.width() * size.height() * 4)) success = false; else { m_context.set(GraphicsContext::createOffscreenContext(size.width(), size.height())); success = true; } } ImageBuffer::~ImageBuffer() { } GraphicsContext* ImageBuffer::context() const { return m_context.get(); } bool ImageBuffer::drawsUsingCopy() const { return true; } PassRefPtr ImageBuffer::copyImage() const { ASSERT(context()); SkCanvas* canvas = imageBufferCanvas(this); if (!canvas) return 0; SkDevice* device = canvas->getDevice(); const SkBitmap& orig = device->accessBitmap(false); SkBitmap copy; if (PlatformBridge::canSatisfyMemoryAllocation(orig.getSize())) orig.copyTo(©, orig.config()); SkBitmapRef* ref = new SkBitmapRef(copy); RefPtr image = BitmapImage::create(ref, 0); ref->unref(); return image; } void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const { SkDebugf("xxxxxxxxxxxxxxxxxx clip not implemented\n"); } void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, bool useLowQualityScale) { RefPtr imageCopy = copyImage(); context->drawImage(imageCopy.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); } void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) { RefPtr imageCopy = copyImage(); imageCopy->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect); } PassRefPtr ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const { GraphicsContext* gc = this->context(); if (!gc) { return 0; } const SkBitmap& src = imageBufferCanvas(this)->getDevice()->accessBitmap(false); SkAutoLockPixels alp(src); if (!src.getPixels()) { return 0; } RefPtr result = ByteArray::create(rect.width() * rect.height() * 4); unsigned char* data = result->data(); if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_size.width() || rect.maxY() > m_size.height()) memset(data, 0, result->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 srcPixelsPerRow = src.rowBytesAsPixels(); unsigned destBytesPerRow = 4 * rect.width(); const SkPMColor* srcRows = src.getAddr32(originx, originy); unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; for (int y = 0; y < numRows; ++y) { for (int x = 0; x < numColumns; x++) { // ugh, it appears they want unpremultiplied pixels SkColor c = SkUnPreMultiply::PMColorToColor(srcRows[x]); int basex = x * 4; destRows[basex + 0] = SkColorGetR(c); destRows[basex + 1] = SkColorGetG(c); destRows[basex + 2] = SkColorGetB(c); destRows[basex + 3] = SkColorGetA(c); } srcRows += srcPixelsPerRow; destRows += destBytesPerRow; } return result.release(); } void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) { GraphicsContext* gc = this->context(); if (!gc) { return; } const SkBitmap& dst = imageBufferCanvas(this)->getDevice()->accessBitmap(true); SkAutoLockPixels alp(dst); if (!dst.getPixels()) { return; } 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.maxX()); int endx = destPoint.x() + sourceRect.maxX(); 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.maxY()); int endy = destPoint.y() + sourceRect.maxY(); ASSERT(endy <= m_size.height()); int numRows = endy - desty; unsigned srcBytesPerRow = 4 * sourceSize.width(); unsigned dstPixelsPerRow = dst.rowBytesAsPixels(); unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4; SkPMColor* dstRows = dst.getAddr32(destx, desty); for (int y = 0; y < numRows; ++y) { for (int x = 0; x < numColumns; x++) { int basex = x * 4; dstRows[x] = SkPackARGB32(srcRows[basex + 3], srcRows[basex + 0], srcRows[basex + 1], srcRows[basex + 2]); } dstRows += dstPixelsPerRow; srcRows += srcBytesPerRow; } } template static String ImageToDataURL(T& source, const String& mimeType, const double* quality) { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); Vector encodedImage; if (mimeType == "image/jpeg") { int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality; if (quality && *quality >= 0.0 && *quality <= 1.0) compressionQuality = static_cast(*quality * 100 + 0.5); if (!JPEGImageEncoder::encode(source, compressionQuality, &encodedImage)) return "data:,"; } else { if (!PNGImageEncoder::encode(source, &encodedImage)) return "data:,"; ASSERT(mimeType == "image/png"); } Vector base64Data; base64Encode(*reinterpret_cast*>(&encodedImage), base64Data); return makeString("data:", mimeType, ";base64,", base64Data); } String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality) { return ImageToDataURL(source, mimeType, quality); } String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const { // Encode the image into a vector. SkDynamicMemoryWStream pngStream; const SkBitmap& dst = imageBufferCanvas(this)->getDevice()->accessBitmap(true); SkImageEncoder::EncodeStream(&pngStream, dst, SkImageEncoder::kPNG_Type, 100); // Convert it into base64. Vector pngEncodedData; SkData* streamData = pngStream.copyToData(); pngEncodedData.append((char*)streamData->data(), streamData->size()); streamData->unref(); Vector base64EncodedData; base64Encode(pngEncodedData, base64EncodedData); // Append with a \0 so that it's a valid string. base64EncodedData.append('\0'); // And the resulting string. return String::format("data:image/png;base64,%s", base64EncodedData.data()); } void ImageBuffer::platformTransformColorSpace(const Vector& lookupTable) { notImplemented(); } size_t ImageBuffer::dataSize() const { return m_size.width() * m_size.height() * 4; } }