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