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 "PlatformGraphicsContextSkia.h" 37#include "SkBitmapRef.h" 38#include "SkCanvas.h" 39#include "SkColorPriv.h" 40#include "SkData.h" 41#include "SkDevice.h" 42#include "SkImageEncoder.h" 43#include "SkStream.h" 44#include "SkUnPreMultiply.h" 45 46using namespace std; 47 48namespace WebCore { 49 50SkCanvas* imageBufferCanvas(const ImageBuffer* buffer) 51{ 52 // We know that our PlatformGraphicsContext is a PlatformGraphicsContextSkia 53 // because that is what we create in GraphicsContext::createOffscreenContext 54 if (!buffer || !buffer->context()) 55 return 0; 56 PlatformGraphicsContext* pc = buffer->context()->platformContext(); 57 return static_cast<PlatformGraphicsContextSkia*>(pc)->canvas(); 58} 59 60ImageBufferData::ImageBufferData(const IntSize&) 61{ 62} 63 64ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success) 65 : m_data(size) 66 , m_size(size) 67{ 68 // GraphicsContext creates a 32bpp SkBitmap, so 4 bytes per pixel. 69 if (!PlatformBridge::canSatisfyMemoryAllocation(size.width() * size.height() * 4)) 70 success = false; 71 else { 72 m_context.set(GraphicsContext::createOffscreenContext(size.width(), size.height())); 73 success = true; 74 } 75} 76 77ImageBuffer::~ImageBuffer() 78{ 79} 80 81GraphicsContext* ImageBuffer::context() const 82{ 83 return m_context.get(); 84} 85 86bool ImageBuffer::drawsUsingCopy() const 87{ 88 return true; 89} 90 91PassRefPtr<Image> ImageBuffer::copyImage() const 92{ 93 ASSERT(context()); 94 95 SkCanvas* canvas = imageBufferCanvas(this); 96 if (!canvas) 97 return 0; 98 99 SkDevice* device = canvas->getDevice(); 100 const SkBitmap& orig = device->accessBitmap(false); 101 102 SkBitmap copy; 103 if (PlatformBridge::canSatisfyMemoryAllocation(orig.getSize())) 104 orig.copyTo(©, orig.config()); 105 106 SkBitmapRef* ref = new SkBitmapRef(copy); 107 RefPtr<Image> image = BitmapImage::create(ref, 0); 108 ref->unref(); 109 return image; 110} 111 112void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const 113{ 114 SkDebugf("xxxxxxxxxxxxxxxxxx clip not implemented\n"); 115} 116 117void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, bool useLowQualityScale) 118{ 119 RefPtr<Image> imageCopy = copyImage(); 120 context->drawImage(imageCopy.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); 121} 122 123void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) 124{ 125 RefPtr<Image> imageCopy = copyImage(); 126 imageCopy->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect); 127} 128 129PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const 130{ 131 GraphicsContext* gc = this->context(); 132 if (!gc) { 133 return 0; 134 } 135 136 const SkBitmap& src = imageBufferCanvas(this)->getDevice()->accessBitmap(false); 137 SkAutoLockPixels alp(src); 138 if (!src.getPixels()) { 139 return 0; 140 } 141 142 RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); 143 unsigned char* data = result->data(); 144 145 if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_size.width() || rect.maxY() > m_size.height()) 146 memset(data, 0, result->length()); 147 148 int originx = rect.x(); 149 int destx = 0; 150 if (originx < 0) { 151 destx = -originx; 152 originx = 0; 153 } 154 int endx = rect.x() + rect.width(); 155 if (endx > m_size.width()) 156 endx = m_size.width(); 157 int numColumns = endx - originx; 158 159 int originy = rect.y(); 160 int desty = 0; 161 if (originy < 0) { 162 desty = -originy; 163 originy = 0; 164 } 165 int endy = rect.y() + rect.height(); 166 if (endy > m_size.height()) 167 endy = m_size.height(); 168 int numRows = endy - originy; 169 170 unsigned srcPixelsPerRow = src.rowBytesAsPixels(); 171 unsigned destBytesPerRow = 4 * rect.width(); 172 173 const SkPMColor* srcRows = src.getAddr32(originx, originy); 174 unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; 175 for (int y = 0; y < numRows; ++y) { 176 for (int x = 0; x < numColumns; x++) { 177 // ugh, it appears they want unpremultiplied pixels 178 SkColor c = SkUnPreMultiply::PMColorToColor(srcRows[x]); 179 int basex = x * 4; 180 destRows[basex + 0] = SkColorGetR(c); 181 destRows[basex + 1] = SkColorGetG(c); 182 destRows[basex + 2] = SkColorGetB(c); 183 destRows[basex + 3] = SkColorGetA(c); 184 } 185 srcRows += srcPixelsPerRow; 186 destRows += destBytesPerRow; 187 } 188 return result.release(); 189} 190 191void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) 192{ 193 GraphicsContext* gc = this->context(); 194 if (!gc) { 195 return; 196 } 197 198 const SkBitmap& dst = imageBufferCanvas(this)->getDevice()->accessBitmap(true); 199 SkAutoLockPixels alp(dst); 200 if (!dst.getPixels()) { 201 return; 202 } 203 204 ASSERT(sourceRect.width() > 0); 205 ASSERT(sourceRect.height() > 0); 206 207 int originx = sourceRect.x(); 208 int destx = destPoint.x() + sourceRect.x(); 209 ASSERT(destx >= 0); 210 ASSERT(destx < m_size.width()); 211 ASSERT(originx >= 0); 212 ASSERT(originx <= sourceRect.maxX()); 213 214 int endx = destPoint.x() + sourceRect.maxX(); 215 ASSERT(endx <= m_size.width()); 216 217 int numColumns = endx - destx; 218 219 int originy = sourceRect.y(); 220 int desty = destPoint.y() + sourceRect.y(); 221 ASSERT(desty >= 0); 222 ASSERT(desty < m_size.height()); 223 ASSERT(originy >= 0); 224 ASSERT(originy <= sourceRect.maxY()); 225 226 int endy = destPoint.y() + sourceRect.maxY(); 227 ASSERT(endy <= m_size.height()); 228 int numRows = endy - desty; 229 230 unsigned srcBytesPerRow = 4 * sourceSize.width(); 231 unsigned dstPixelsPerRow = dst.rowBytesAsPixels(); 232 233 unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4; 234 SkPMColor* dstRows = dst.getAddr32(destx, desty); 235 for (int y = 0; y < numRows; ++y) { 236 for (int x = 0; x < numColumns; x++) { 237 int basex = x * 4; 238 dstRows[x] = SkPackARGB32(srcRows[basex + 3], 239 srcRows[basex + 0], 240 srcRows[basex + 1], 241 srcRows[basex + 2]); 242 } 243 dstRows += dstPixelsPerRow; 244 srcRows += srcBytesPerRow; 245 } 246} 247 248 249String ImageBuffer::toDataURL(const String&, const double*) const 250{ 251 // Encode the image into a vector. 252 SkDynamicMemoryWStream pngStream; 253 const SkBitmap& dst = imageBufferCanvas(this)->getDevice()->accessBitmap(true); 254 SkImageEncoder::EncodeStream(&pngStream, dst, SkImageEncoder::kPNG_Type, 100); 255 256 // Convert it into base64. 257 Vector<char> pngEncodedData; 258 SkData* streamData = pngStream.copyToData(); 259 pngEncodedData.append((char*)streamData->data(), streamData->size()); 260 streamData->unref(); 261 Vector<char> base64EncodedData; 262 base64Encode(pngEncodedData, base64EncodedData); 263 // Append with a \0 so that it's a valid string. 264 base64EncodedData.append('\0'); 265 266 // And the resulting string. 267 return String::format("data:image/png;base64,%s", base64EncodedData.data()); 268} 269 270void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookupTable) 271{ 272 notImplemented(); 273} 274 275size_t ImageBuffer::dataSize() const 276{ 277 return m_size.width() * m_size.height() * 4; 278} 279 280} 281