1/*
2 * Copyright (C) 2010, 2011, 2012, 2013 Research In Motion Limited. 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 Lesser 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 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#include "config.h"
20#include "Image.h"
21
22#include "AffineTransform.h"
23#include "BitmapImage.h"
24#include "FloatConversion.h"
25#include "FloatRect.h"
26#include "GraphicsContext.h"
27#include "ImageBuffer.h"
28#include "ImageDecoder.h"
29#include "ImageObserver.h"
30#include "IntSize.h"
31#include "NotImplemented.h"
32#include "SharedBuffer.h"
33
34#include <BlackBerryPlatformGraphicsContext.h>
35#include <BlackBerryPlatformResourceStore.h>
36#include <wtf/MathExtras.h>
37
38using BlackBerry::Platform::ResourceData;
39using BlackBerry::Platform::ResourceStore;
40
41namespace WebCore {
42
43PassRefPtr<Image> Image::loadPlatformResource(const char *name)
44{
45    ResourceData data = ResourceStore::instance()->requestResource(BlackBerry::Platform::String::fromUtf8(name));
46    if (!data.data())
47        return BitmapImage::nullImage();
48
49    RefPtr<SharedBuffer> buffer = SharedBuffer::create(data.data(), data.len());
50    if (!buffer)
51        return BitmapImage::nullImage();
52
53    RefPtr<BitmapImage> img = BitmapImage::create();
54    img->setData(buffer.release(), true /* allDataReceived */);
55    return img.release();
56}
57
58PassNativeImagePtr ImageFrame::asNewNativeImage() const
59{
60    return new BlackBerry::Platform::Graphics::TiledImage(m_size, m_bytes);
61}
62
63bool FrameData::clear(bool clearMetadata)
64{
65    if (clearMetadata)
66        m_haveMetadata = false;
67
68    if (m_frame) {
69        delete m_frame;
70        m_frame = 0;
71        return true;
72    }
73    return false;
74}
75
76BitmapImage::BitmapImage(PassNativeImagePtr nativeImage, ImageObserver* observer)
77    : Image(observer)
78    , m_currentFrame(0)
79    , m_frames(0)
80    , m_frameTimer(0)
81    , m_repetitionCount(cAnimationNone)
82    , m_repetitionCountStatus(Unknown)
83    , m_repetitionsComplete(0)
84    , m_decodedSize(0)
85    , m_frameCount(1)
86    , m_isSolidColor(false)
87    , m_checkedForSolidColor(false)
88    , m_animationFinished(true)
89    , m_allDataReceived(true)
90    , m_haveSize(true)
91    , m_sizeAvailable(true)
92    , m_haveFrameCount(true)
93{
94    int width = nativeImage->size().width();
95    int height = nativeImage->size().height();
96    m_decodedSize = width * height * 4;
97    m_size = IntSize(width, height);
98
99    m_frames.grow(1);
100    m_frames[0].m_frame = nativeImage;
101    m_frames[0].m_hasAlpha = nativeImage->hasAlpha();
102    m_frames[0].m_haveMetadata = true;
103    checkForSolidColor();
104}
105
106void BitmapImage::checkForSolidColor()
107{
108    m_isSolidColor = size().width() == 1 && size().height() == 1 && frameCount() == 1;
109    m_checkedForSolidColor = true;
110    if (m_isSolidColor) {
111        unsigned buffer;
112        NativeImagePtr image = nativeImageForCurrentFrame();
113        image->readPixels(&buffer, 1);
114        m_solidColor = Color(buffer);
115    }
116}
117
118void BitmapImage::invalidatePlatformData()
119{
120}
121
122void BitmapImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode)
123{
124    draw(context, dstRect, srcRect, styleColorSpace, op, blendMode, DoNotRespectImageOrientation);
125}
126
127void BitmapImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator op, BlendMode, RespectImageOrientationEnum shouldRespectImageOrientation)
128{
129    startAnimation();
130
131    NativeImagePtr image = nativeImageForCurrentFrame();
132    if (!image)
133        return;
134
135    FloatRect normDstRect = dstRect.normalized();
136    FloatRect normSrcRect = srcRect.normalized();
137
138    if (normSrcRect.isEmpty() || normDstRect.isEmpty())
139        return; // Nothing to draw.
140
141#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
142    normSrcRect = adjustSourceRectForDownSampling(normSrcRect, image->size());
143#endif
144
145    // use similar orientation code as ImageSkia
146    ImageOrientation orientation = DefaultImageOrientation;
147    if (shouldRespectImageOrientation == RespectImageOrientation)
148        orientation = frameOrientationAtIndex(m_currentFrame);
149
150    GraphicsContextStateSaver saveContext(*context, false);
151    if (orientation != DefaultImageOrientation) {
152        saveContext.save();
153
154        // ImageOrientation expects the origin to be at (0, 0)
155        context->translate(normDstRect.x(), normDstRect.y());
156        normDstRect.setLocation(FloatPoint());
157
158        context->concatCTM(orientation.transformFromDefault(normDstRect.size()));
159        if (orientation.usesWidthAsHeight()) {
160            // The destination rect will have its width and height already reversed for the orientation of
161            // the image, as it was needed for page layout, so we need to reverse it back here.
162            normDstRect = FloatRect(normDstRect.x(), normDstRect.y(), normDstRect.height(), normDstRect.width());
163        }
164    }
165
166    CompositeOperator oldOperator = context->compositeOperation();
167    context->setCompositeOperation(op);
168    context->platformContext()->addImage(normDstRect, normSrcRect, image);
169    context->setCompositeOperation(oldOperator);
170
171    if (ImageObserver* observer = imageObserver())
172        observer->didDraw(this);
173}
174
175void Image::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransformation, const FloatPoint& phase, ColorSpace, CompositeOperator op, const FloatRect& dstRect, BlendMode)
176{
177    NativeImagePtr image = nativeImageForCurrentFrame();
178    if (!image)
179        return;
180
181    // patternTransformation contains the tile scale factor relative to
182    // document coordinates. phase is given in document coordinates and
183    // corresponds to a texture drawn with src starting at (0, 0).
184    // Thus, dstRect starts at phase + patternTransformation.mapPoint(srcRect.location()).
185    AffineTransform srcTransform;
186    srcTransform.translate(phase.x(), phase.y());
187    srcTransform.multiply(patternTransformation);
188#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
189    FloatRect srcRectLocal(srcRect);
190    const IntSize unscaledSize = size();
191    const IntSize scaledSize = image->size();
192    if (unscaledSize != scaledSize) {
193        srcRectLocal = adjustSourceRectForDownSampling(srcRect, scaledSize);
194        // If the image has been down sampled, the transformation should take the scale into account.
195        float xscale = static_cast<float>(unscaledSize.width()) / scaledSize.width();
196        float yscale = static_cast<float>(unscaledSize.height()) / scaledSize.height();
197        srcTransform.scaleNonUniform(xscale, yscale);
198    }
199#else
200    FloatRect srcRectLocal(srcRect);
201#endif
202    srcTransform.translate(srcRectLocal.x(), srcRectLocal.y());
203    double srcTransformArray[6] = {
204        srcTransform.a(), srcTransform.b(),
205        srcTransform.c(), srcTransform.d(),
206        srcTransform.e(), srcTransform.f()
207    };
208
209    CompositeOperator oldOperator = context->compositeOperation();
210    context->setCompositeOperation(op);
211    context->platformContext()->addPattern(dstRect, srcRectLocal, srcTransformArray, image);
212    context->setCompositeOperation(oldOperator);
213}
214
215} // namespace WebCore
216