1 /* 2 * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20 #include "config.h" 21 #include "ImageBuffer.h" 22 23 #include "Base64.h" 24 #include "GraphicsContext.h" 25 #include "Image.h" 26 #include "ImageData.h" 27 #include "JPEGEncoder.h" 28 #include "PNGEncoder.h" 29 #include "SharedBitmap.h" 30 31 namespace WebCore { 32 33 class BufferedImage: public Image { 34 35 public: 36 BufferedImage(const ImageBufferData* data) 37 : m_data(data) 38 { 39 } 40 41 virtual IntSize size() const { return IntSize(m_data->m_bitmap->width(), m_data->m_bitmap->height()); } 42 virtual void destroyDecodedData(bool destroyAll = true) {} 43 virtual unsigned decodedSize() const { return 0; } 44 virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); 45 virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, 46 const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); 47 48 const ImageBufferData* m_data; 49 }; 50 51 void BufferedImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp) 52 { 53 IntRect intDstRect = enclosingIntRect(dstRect); 54 IntRect intSrcRect(srcRect); 55 m_data->m_bitmap->draw(ctxt, intDstRect, intSrcRect, compositeOp); 56 } 57 58 void BufferedImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRectIn, const AffineTransform& patternTransform, 59 const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) 60 { 61 m_data->m_bitmap->drawPattern(ctxt, tileRectIn, patternTransform, phase, op, destRect, size()); 62 } 63 64 ImageBufferData::ImageBufferData(const IntSize& size) 65 : m_bitmap(SharedBitmap::createInstance(false, size.width(), size.height(), false)) 66 { 67 // http://www.w3.org/TR/2009/WD-html5-20090212/the-canvas-element.html#canvaspixelarray 68 // "When the canvas is initialized it must be set to fully transparent black." 69 m_bitmap->resetPixels(true); 70 m_bitmap->setHasAlpha(true); 71 } 72 73 ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace colorSpace, bool& success) 74 : m_data(size) 75 , m_size(size) 76 { 77 // FIXME: colorSpace is not used 78 UNUSED_PARAM(colorSpace); 79 80 m_context.set(new GraphicsContext(0)); 81 m_context->setBitmap(m_data.m_bitmap); 82 success = true; 83 } 84 85 ImageBuffer::~ImageBuffer() 86 { 87 } 88 89 GraphicsContext* ImageBuffer::context() const 90 { 91 return m_context.get(); 92 } 93 94 Image* ImageBuffer::image() const 95 { 96 if (!m_image) 97 m_image = adoptRef(new BufferedImage(&m_data)); 98 99 return m_image.get(); 100 } 101 102 template <bool premultiplied> PassRefPtr<ImageData> 103 static getImageData(const IntRect& rect, const SharedBitmap* bitmap) 104 { 105 PassRefPtr<ImageData> imageData = ImageData::create(rect.width(), rect.height()); 106 107 const unsigned char* src = static_cast<const unsigned char*>(bitmap->bytes()); 108 if (!src) 109 return imageData; 110 111 IntRect sourceRect(0, 0, bitmap->width(), bitmap->height()); 112 sourceRect.intersect(rect); 113 if (sourceRect.isEmpty()) 114 return imageData; 115 116 unsigned char* dst = imageData->data()->data()->data(); 117 memset(dst, 0, imageData->data()->data()->length()); 118 src += (sourceRect.y() * bitmap->width() + sourceRect.x()) * 4; 119 dst += ((sourceRect.y() - rect.y()) * imageData->width() + sourceRect.x() - rect.x()) * 4; 120 int bytesToCopy = sourceRect.width() * 4; 121 int srcSkip = (bitmap->width() - sourceRect.width()) * 4; 122 int dstSkip = (imageData->width() - sourceRect.width()) * 4; 123 const unsigned char* dstEnd = dst + sourceRect.height() * imageData->width() * 4; 124 while (dst < dstEnd) { 125 const unsigned char* dstRowEnd = dst + bytesToCopy; 126 while (dst < dstRowEnd) { 127 // Convert ARGB little endian to RGBA big endian 128 int blue = *src++; 129 int green = *src++; 130 int red = *src++; 131 int alpha = *src++; 132 if (premultiplied) { 133 *dst++ = static_cast<unsigned char>((red * alpha + 254) / 255); 134 *dst++ = static_cast<unsigned char>((green * alpha + 254) / 255); 135 *dst++ = static_cast<unsigned char>((blue * alpha + 254) / 255); 136 *dst++ = static_cast<unsigned char>(alpha); 137 } else { 138 *dst++ = static_cast<unsigned char>(red); 139 *dst++ = static_cast<unsigned char>(green); 140 *dst++ = static_cast<unsigned char>(blue); 141 *dst++ = static_cast<unsigned char>(alpha); 142 ++src; 143 } 144 } 145 src += srcSkip; 146 dst += dstSkip; 147 } 148 149 return imageData; 150 } 151 152 PassRefPtr<ImageData> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const 153 { 154 return getImageData<false>(rect, m_data.m_bitmap.get()); 155 } 156 157 PassRefPtr<ImageData> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const 158 { 159 return getImageData<true>(rect, m_data.m_bitmap.get()); 160 } 161 162 template <bool premultiplied> 163 static void putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint, SharedBitmap* bitmap) 164 { 165 unsigned char* dst = (unsigned char*)bitmap->bytes(); 166 if (!dst) 167 return; 168 169 IntRect destRect(destPoint, sourceRect.size()); 170 destRect.intersect(IntRect(0, 0, bitmap->width(), bitmap->height())); 171 172 if (destRect.isEmpty()) 173 return; 174 175 const unsigned char* src = source->data()->data()->data(); 176 dst += (destRect.y() * bitmap->width() + destRect.x()) * 4; 177 src += (sourceRect.y() * source->width() + sourceRect.x()) * 4; 178 int bytesToCopy = destRect.width() * 4; 179 int dstSkip = (bitmap->width() - destRect.width()) * 4; 180 int srcSkip = (source->width() - destRect.width()) * 4; 181 const unsigned char* dstEnd = dst + destRect.height() * bitmap->width() * 4; 182 while (dst < dstEnd) { 183 const unsigned char* dstRowEnd = dst + bytesToCopy; 184 while (dst < dstRowEnd) { 185 // Convert RGBA big endian to ARGB little endian 186 int red = *src++; 187 int green = *src++; 188 int blue = *src++; 189 int alpha = *src++; 190 if (premultiplied) { 191 *dst++ = static_cast<unsigned char>(blue * 255 / alpha); 192 *dst++ = static_cast<unsigned char>(green * 255 / alpha); 193 *dst++ = static_cast<unsigned char>(red * 255 / alpha); 194 *dst++ = static_cast<unsigned char>(alpha); 195 } else { 196 *dst++ = static_cast<unsigned char>(blue); 197 *dst++ = static_cast<unsigned char>(green); 198 *dst++ = static_cast<unsigned char>(red); 199 *dst++ = static_cast<unsigned char>(alpha); 200 } 201 } 202 src += srcSkip; 203 dst += dstSkip; 204 } 205 } 206 207 void ImageBuffer::putUnmultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) 208 { 209 putImageData<false>(source, sourceRect, destPoint, m_data.m_bitmap.get()); 210 } 211 212 void ImageBuffer::putPremultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) 213 { 214 putImageData<true>(source, sourceRect, destPoint, m_data.m_bitmap.get()); 215 } 216 217 String ImageBuffer::toDataURL(const String& mimeType) const 218 { 219 if (!m_data.m_bitmap->bytes()) 220 return "data:,"; 221 222 Vector<char> output; 223 const char* header; 224 if (mimeType.lower() == "image/png") { 225 if (!compressBitmapToPng(m_data.m_bitmap.get(), output)) 226 return "data:,"; 227 header = "data:image/png;base64,"; 228 } else { 229 if (!compressBitmapToJpeg(m_data.m_bitmap.get(), output)) 230 return "data:,"; 231 header = "data:image/jpeg;base64,"; 232 } 233 234 Vector<char> base64; 235 base64Encode(output, base64); 236 237 output.clear(); 238 239 Vector<char> url; 240 url.append(header, strlen(header)); 241 url.append(base64); 242 243 return String(url.data(), url.size()); 244 } 245 246 } 247