1/* 2 * Copyright 2007, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "ImageBuffer.h" 28 29#include "Base64.h" 30#include "BitmapImage.h" 31#include "ColorSpace.h" 32#include "GraphicsContext.h" 33#include "NotImplemented.h" 34#include "PlatformBridge.h" 35#include "PlatformGraphicsContext.h" 36#include "SkBitmapRef.h" 37#include "SkCanvas.h" 38#include "SkColorPriv.h" 39#include "SkData.h" 40#include "SkDevice.h" 41#include "SkImageEncoder.h" 42#include "SkStream.h" 43#include "SkUnPreMultiply.h" 44#include "android_graphics.h" 45 46using namespace std; 47 48namespace WebCore { 49 50ImageBufferData::ImageBufferData(const IntSize&) 51{ 52} 53 54ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success) 55 : m_data(size) 56 , m_size(size) 57{ 58 // GraphicsContext creates a 32bpp SkBitmap, so 4 bytes per pixel. 59 if (!PlatformBridge::canSatisfyMemoryAllocation(size.width() * size.height() * 4)) 60 success = false; 61 else { 62 m_context.set(GraphicsContext::createOffscreenContext(size.width(), size.height())); 63 success = true; 64 } 65} 66 67ImageBuffer::~ImageBuffer() 68{ 69} 70 71GraphicsContext* ImageBuffer::context() const 72{ 73 return m_context.get(); 74} 75 76bool ImageBuffer::drawsUsingCopy() const 77{ 78 return true; 79} 80 81PassRefPtr<Image> ImageBuffer::copyImage() const 82{ 83 ASSERT(context()); 84 85 SkCanvas* canvas = context()->platformContext()->getCanvas(); 86 if (!canvas) 87 return 0; 88 89 SkDevice* device = canvas->getDevice(); 90 const SkBitmap& orig = device->accessBitmap(false); 91 92 SkBitmap copy; 93 if (PlatformBridge::canSatisfyMemoryAllocation(orig.getSize())) 94 orig.copyTo(©, orig.config()); 95 96 SkBitmapRef* ref = new SkBitmapRef(copy); 97 RefPtr<Image> image = BitmapImage::create(ref, 0); 98 ref->unref(); 99 return image; 100} 101 102void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const 103{ 104 SkDebugf("xxxxxxxxxxxxxxxxxx clip not implemented\n"); 105} 106 107void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, bool useLowQualityScale) 108{ 109 RefPtr<Image> imageCopy = copyImage(); 110 context->drawImage(imageCopy.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); 111} 112 113void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) 114{ 115 RefPtr<Image> imageCopy = copyImage(); 116 imageCopy->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect); 117} 118 119PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const 120{ 121 GraphicsContext* gc = this->context(); 122 if (!gc) { 123 return 0; 124 } 125 126 const SkBitmap& src = android_gc2canvas(gc)->getDevice()->accessBitmap(false); 127 SkAutoLockPixels alp(src); 128 if (!src.getPixels()) { 129 return 0; 130 } 131 132 RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); 133 unsigned char* data = result->data(); 134 135 if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_size.width() || rect.maxY() > m_size.height()) 136 memset(data, 0, result->length()); 137 138 int originx = rect.x(); 139 int destx = 0; 140 if (originx < 0) { 141 destx = -originx; 142 originx = 0; 143 } 144 int endx = rect.x() + rect.width(); 145 if (endx > m_size.width()) 146 endx = m_size.width(); 147 int numColumns = endx - originx; 148 149 int originy = rect.y(); 150 int desty = 0; 151 if (originy < 0) { 152 desty = -originy; 153 originy = 0; 154 } 155 int endy = rect.y() + rect.height(); 156 if (endy > m_size.height()) 157 endy = m_size.height(); 158 int numRows = endy - originy; 159 160 unsigned srcPixelsPerRow = src.rowBytesAsPixels(); 161 unsigned destBytesPerRow = 4 * rect.width(); 162 163 const SkPMColor* srcRows = src.getAddr32(originx, originy); 164 unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; 165 for (int y = 0; y < numRows; ++y) { 166 for (int x = 0; x < numColumns; x++) { 167 // ugh, it appears they want unpremultiplied pixels 168 SkColor c = SkUnPreMultiply::PMColorToColor(srcRows[x]); 169 int basex = x * 4; 170 destRows[basex + 0] = SkColorGetR(c); 171 destRows[basex + 1] = SkColorGetG(c); 172 destRows[basex + 2] = SkColorGetB(c); 173 destRows[basex + 3] = SkColorGetA(c); 174 } 175 srcRows += srcPixelsPerRow; 176 destRows += destBytesPerRow; 177 } 178 return result.release(); 179} 180 181void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) 182{ 183 GraphicsContext* gc = this->context(); 184 if (!gc) { 185 return; 186 } 187 188 const SkBitmap& dst = android_gc2canvas(gc)->getDevice()->accessBitmap(true); 189 SkAutoLockPixels alp(dst); 190 if (!dst.getPixels()) { 191 return; 192 } 193 194 ASSERT(sourceRect.width() > 0); 195 ASSERT(sourceRect.height() > 0); 196 197 int originx = sourceRect.x(); 198 int destx = destPoint.x() + sourceRect.x(); 199 ASSERT(destx >= 0); 200 ASSERT(destx < m_size.width()); 201 ASSERT(originx >= 0); 202 ASSERT(originx <= sourceRect.maxX()); 203 204 int endx = destPoint.x() + sourceRect.maxX(); 205 ASSERT(endx <= m_size.width()); 206 207 int numColumns = endx - destx; 208 209 int originy = sourceRect.y(); 210 int desty = destPoint.y() + sourceRect.y(); 211 ASSERT(desty >= 0); 212 ASSERT(desty < m_size.height()); 213 ASSERT(originy >= 0); 214 ASSERT(originy <= sourceRect.maxY()); 215 216 int endy = destPoint.y() + sourceRect.maxY(); 217 ASSERT(endy <= m_size.height()); 218 int numRows = endy - desty; 219 220 unsigned srcBytesPerRow = 4 * sourceSize.width(); 221 unsigned dstPixelsPerRow = dst.rowBytesAsPixels(); 222 223 unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4; 224 SkPMColor* dstRows = dst.getAddr32(destx, desty); 225 for (int y = 0; y < numRows; ++y) { 226 for (int x = 0; x < numColumns; x++) { 227 int basex = x * 4; 228 dstRows[x] = SkPackARGB32(srcRows[basex + 3], 229 srcRows[basex + 0], 230 srcRows[basex + 1], 231 srcRows[basex + 2]); 232 } 233 dstRows += dstPixelsPerRow; 234 srcRows += srcBytesPerRow; 235 } 236} 237 238 239String ImageBuffer::toDataURL(const String&, const double*) const 240{ 241 // Encode the image into a vector. 242 SkDynamicMemoryWStream pngStream; 243 const SkBitmap& dst = android_gc2canvas(context())->getDevice()->accessBitmap(true); 244 SkImageEncoder::EncodeStream(&pngStream, dst, SkImageEncoder::kPNG_Type, 100); 245 246 // Convert it into base64. 247 Vector<char> pngEncodedData; 248 SkData* streamData = pngStream.copyToData(); 249 pngEncodedData.append((char*)streamData->data(), streamData->size()); 250 streamData->unref(); 251 Vector<char> base64EncodedData; 252 base64Encode(pngEncodedData, base64EncodedData); 253 // Append with a \0 so that it's a valid string. 254 base64EncodedData.append('\0'); 255 256 // And the resulting string. 257 return String::format("data:image/png;base64,%s", base64EncodedData.data()); 258} 259 260void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookupTable) 261{ 262 notImplemented(); 263} 264 265size_t ImageBuffer::dataSize() const 266{ 267 return m_size.width() * m_size.height() * 4; 268} 269 270} 271