1/*
2 * Copyright (C) 2010, 2011 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// Implementation notes:
20// Current implementation provides the source image size without doing a full
21// decode. It seems that libimg does not support partial decoding.
22
23#include "config.h"
24#include "JPEGImageDecoder.h"
25
26#include <errno.h>
27#include <img/img.h>
28#include <string.h>
29#include <wtf/OwnArrayPtr.h>
30#include <wtf/PassOwnPtr.h>
31
32namespace WebCore {
33
34static img_lib_t s_ilib;
35
36static inline int libInit()
37{
38    static bool s_initialized;
39    if (s_initialized)
40        return 0;
41
42    if (img_lib_attach(&s_ilib) != IMG_ERR_OK) {
43        LOG_ERROR("Unable to attach to libimg.");
44        return -1;
45    }
46    s_initialized = true;
47    return 0;
48}
49
50class ImageReader {
51public:
52    ImageReader(const char* data, size_t dataSize)
53        : m_data(data)
54        , m_size(dataSize)
55        , m_width(0)
56        , m_height(0)
57    {
58        libInit();
59    }
60
61    void updateData(const char* data, size_t dataSize);
62    int setSize(size_t width, size_t height);
63    int sizeExtract(size_t& width, size_t& height);
64    int decode(size_t width, size_t height, ImageFrame*);
65
66private:
67    const char* m_data;
68    size_t m_size;
69    size_t m_width;
70    size_t m_height;
71};
72
73// Function to get the original size of an image
74static int imgDecodeSetup(_Uintptrt data, img_t* img, unsigned flags)
75{
76    ImageReader* reader = reinterpret_cast<ImageReader*>(data);
77
78    if ((img->flags & (IMG_W | IMG_H)) == (IMG_W | IMG_H))
79        reader->setSize(img->w, img->h);
80
81    // Always: want to stop processing whether get a size or not.
82    return IMG_ERR_INTR;
83}
84
85void ImageReader::updateData(const char* data, size_t dataSize)
86{
87    m_data = data;
88    m_size = dataSize;
89}
90
91int ImageReader::setSize(size_t width, size_t height)
92{
93    m_width = width;
94    m_height = height;
95    return 0;
96}
97
98int ImageReader::sizeExtract(size_t& width, size_t& height)
99{
100    img_decode_callouts_t callouts;
101    img_t img;
102    io_stream_t* iostream = io_open(IO_MEM, IO_READ, m_size, m_data);
103    if (!iostream)
104        return -1;
105
106    memset(&img, 0, sizeof(img));
107    memset(&callouts, 0, sizeof(callouts));
108    callouts.setup_f = imgDecodeSetup;
109    callouts.data = reinterpret_cast<_Uintptrt>(this);
110
111    int rc = img_load(s_ilib, iostream, &callouts, &img);
112    io_close(iostream);
113    if (rc != IMG_ERR_INTR)
114        return -1;
115    if (!m_width || !m_height)
116        return -1;
117
118    width = m_width;
119    height = m_height;
120
121    return 0;
122}
123
124int ImageReader::decode(size_t width, size_t height, ImageFrame* aFrame)
125{
126    if (libInit() == -1)
127        return -1;
128
129    img_t img;
130    memset(&img, 0, sizeof(img));
131    img.format = IMG_FMT_RGB888;
132    img.w = width;
133    img.h = height;
134
135    const int ColorComponents = 3;
136    // Use a multiple of 2 bytes to improve performance
137    int stride = (ColorComponents * width + 3) & ~3;
138    OwnArrayPtr<_uint8> buffer = adoptArrayPtr(new _uint8[stride * height]);
139    if (!buffer)
140        return -1;
141    img.access.direct.data = buffer.get();
142    img.access.direct.stride = stride;
143    img.flags = IMG_W | IMG_H | IMG_DIRECT | IMG_FORMAT;
144
145    io_stream_t* iostream = io_open(IO_MEM, IO_READ, m_size, m_data);
146    if (!iostream)
147        return -1;
148
149    int rc = img_load_resize(s_ilib, iostream, 0, &img);
150    io_close(iostream);
151    if (rc != IMG_ERR_OK)
152        return -1;
153
154    for (unsigned j = 0; j < height; j++) {
155        _uint8* curPtr = buffer.get() + j * stride;
156        for (unsigned i = 0; i < width; i++) {
157            aFrame->setRGBA(i, j, curPtr[0], curPtr[1], curPtr[2], 255);
158            curPtr += 3;
159        }
160    }
161    return 0;
162}
163
164JPEGImageDecoder::JPEGImageDecoder(ImageSource::AlphaOption alphaOption,
165    ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
166    : ImageDecoder(alphaOption, gammaAndColorProfileOption)
167{
168}
169
170void JPEGImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
171{
172    ImageDecoder::setData(data, allDataReceived);
173
174    if (m_reader)
175        m_reader->updateData(m_data->data(), m_data->size());
176}
177
178bool JPEGImageDecoder::isSizeAvailable()
179{
180    if (!ImageDecoder::isSizeAvailable()) {
181        if (!m_reader) {
182            if (m_data)
183                m_reader = adoptPtr(new ImageReader(m_data->data(), m_data->size()));
184            if (!m_reader)
185                return false;
186        }
187        size_t width, height;
188
189        if (m_reader->sizeExtract(width, height) == -1)
190            return false;
191        if (!setSize(width, height))
192            return false;
193    }
194
195    return ImageDecoder::isSizeAvailable();
196}
197
198ImageFrame* JPEGImageDecoder::frameBufferAtIndex(size_t index)
199{
200    if (!isAllDataReceived())
201        return 0;
202
203    if (index)
204        return 0;
205
206    if (m_frameBufferCache.isEmpty())
207        m_frameBufferCache.resize(1);
208
209    ImageFrame& frame = m_frameBufferCache[0];
210
211    // Check to see if it's already decoded.
212    // FIXME: Could size change between calls to this method?
213    if (frame.status() == ImageFrame::FrameComplete)
214        return &frame;
215
216    if (frame.status() == ImageFrame::FrameEmpty) {
217        if (!size().width() || !size().height()) {
218            if (!isSizeAvailable())
219                return 0;
220        }
221        if (!frame.setSize(size().width(), size().height())) {
222            setFailed();
223            return 0;
224        }
225        frame.setStatus(ImageFrame::FramePartial);
226        // For JPEGs, the frame always fills the entire image.
227        frame.setOriginalFrameRect(IntRect(IntPoint(), size()));
228    }
229
230    if (!m_reader || m_reader->decode(size().width(), size().height(), &frame) == -1) {
231        setFailed();
232        return 0;
233    }
234
235    frame.setStatus(ImageFrame::FrameComplete);
236    return &frame;
237}
238
239} // namespace WebCore
240
241