1/*
2 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
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 "qt_pixmapruntime.h"
21
22#include "APICast.h"
23#include "CachedImage.h"
24#include "HTMLImageElement.h"
25#include "ImageData.h"
26#include "IntSize.h"
27#include "JSDOMBinding.h"
28#include "JSGlobalObject.h"
29#include "JSHTMLImageElement.h"
30#include "JSImageData.h"
31#include "JSRetainPtr.h"
32#include "JavaScript.h"
33#include "StillImageQt.h"
34#include <QBuffer>
35#include <QByteArray>
36#include <QColor>
37#include <QImage>
38#include <QPixmap>
39#include <QVariant>
40#include <QtEndian>
41
42using namespace WebCore;
43namespace JSC {
44
45namespace Bindings {
46
47static void copyPixelsInto(const QImage& sourceImage, int width, int height, unsigned char* destPixels)
48{
49    QImage image(sourceImage);
50    switch (image.format()) {
51    case QImage::Format_RGB888:
52        for (int y = 0; y < height; y++) {
53            const uchar* scanLine = image.scanLine(y);
54            for (int x = 0; x < width; x++) {
55                *(destPixels++) = *(scanLine++);
56                *(destPixels++) = *(scanLine++);
57                *(destPixels++) = *(scanLine++);
58                *(destPixels++) = 0xFF;
59            }
60        }
61        break;
62    default:
63        image = image.convertToFormat(QImage::Format_ARGB32);
64        // Fall through
65    case QImage::Format_RGB32:
66    case QImage::Format_ARGB32:
67        for (int y = 0; y < height; y++) {
68            const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(image.scanLine(y));
69            for (int x = 0; x < width; x++) {
70                QRgb pixel = scanLine[x];
71                qToBigEndian<quint32>((pixel << 8) | qAlpha(pixel), destPixels);
72                destPixels += 4;
73            }
74        }
75        break;
76    }
77}
78
79static QPixmap toPixmap(const QVariant& data)
80{
81    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>()))
82        return data.value<QPixmap>();
83
84    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>()))
85        return QPixmap::fromImage(data.value<QImage>());
86
87    return QPixmap();
88}
89
90static QImage toImage(const QVariant& data)
91{
92    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>()))
93        return data.value<QImage>();
94
95    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>()))
96        return data.value<QPixmap>().toImage();
97
98    return QImage();
99}
100
101static QSize imageSizeForVariant(const QVariant& data)
102{
103    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>()))
104        return data.value<QPixmap>().size();
105    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>()))
106        return data.value<QImage>().size();
107    return QSize(0, 0);
108}
109
110static JSValueRef getPixmapWidth(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef*)
111{
112    QVariant& data = *static_cast<QVariant*>(JSObjectGetPrivate(object));
113    return JSValueMakeNumber(context, imageSizeForVariant(data).width());
114}
115
116static JSValueRef getPixmapHeight(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef*)
117{
118    QVariant& data = *static_cast<QVariant*>(JSObjectGetPrivate(object));
119    return JSValueMakeNumber(context, imageSizeForVariant(data).height());
120}
121
122static JSValueRef assignToHTMLImageElement(JSContextRef context, JSObjectRef function, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
123{
124    if (!argumentCount)
125        return JSValueMakeUndefined(context);
126
127    JSObjectRef objectArg = JSValueToObject(context, arguments[0], exception);
128    if (!objectArg)
129        return JSValueMakeUndefined(context);
130
131    JSObject* jsObject = ::toJS(objectArg);
132
133    if (!jsObject->inherits(&JSHTMLImageElement::s_info))
134        return JSValueMakeUndefined(context);
135
136    QVariant& data = *static_cast<QVariant*>(JSObjectGetPrivate(object));
137
138    // We now know that we have a valid <img> element as the argument, we can attach the pixmap to it.
139    RefPtr<StillImage> stillImage = WebCore::StillImage::create(toPixmap(data));
140    HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(static_cast<JSHTMLImageElement*>(jsObject)->impl());
141    imageElement->setCachedImage(new CachedImage(stillImage.get()));
142    return JSValueMakeUndefined(context);
143}
144
145static JSValueRef pixmapToImageData(JSContextRef context, JSObjectRef function, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
146{
147    QVariant& data = *static_cast<QVariant*>(JSObjectGetPrivate(object));
148    QImage image = toImage(data);
149    int width = image.width();
150    int height = image.height();
151
152    RefPtr<ImageData> imageData = ImageData::create(IntSize(width, height));
153    copyPixelsInto(image, width, height, imageData->data()->data());
154    JSDOMGlobalObject* globalObject = static_cast<JSDOMGlobalObject*>(::toJS(JSContextGetGlobalObject(context)));
155    JSC::ExecState* exec = ::toJS(context);
156    return ::toRef(exec, toJS(exec, globalObject, imageData.get()));
157}
158
159static JSValueRef pixmapToDataUrl(JSContextRef context, JSObjectRef function, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
160{
161    QVariant& data = *static_cast<QVariant*>(JSObjectGetPrivate(object));
162    QByteArray byteArray;
163    QBuffer buffer(&byteArray);
164    toImage(data).save(&buffer, "PNG");
165    QByteArray encoded = QByteArray("data:image/png;base64,") + byteArray.toBase64();
166    JSRetainPtr<JSStringRef> str(Adopt, JSStringCreateWithUTF8CString(encoded.constData()));
167    JSValueRef value = JSValueMakeString(context, str.get());
168
169    return value;
170}
171
172static JSValueRef pixmapToString(JSContextRef context, JSObjectRef function, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
173{
174    QVariant& data = *static_cast<QVariant*>(JSObjectGetPrivate(object));
175    QSize size = imageSizeForVariant(data);
176    QString stringValue = QString::fromLatin1("[Qt Native Pixmap %1,%2]").arg(size.width()).arg(size.height());
177    JSRetainPtr<JSStringRef> str(Adopt, JSStringCreateWithUTF8CString(stringValue.toUtf8().constData()));
178    JSValueRef value = JSValueMakeString(context, str.get());
179
180    return value;
181}
182
183static void finalizePixmap(JSObjectRef object)
184{
185    delete static_cast<QVariant*>(JSObjectGetPrivate(object));
186}
187
188JSObjectRef QtPixmapRuntime::toJS(JSContextRef context, const QVariant& value, JSValueRef* exception)
189{
190    return JSObjectMake(context, getClassRef(), new QVariant(value));
191}
192
193static QVariant emptyVariantForHint(QMetaType::Type hint)
194{
195    if (hint == qMetaTypeId<QPixmap>())
196        return QVariant::fromValue(QPixmap());
197    if (hint == qMetaTypeId<QImage>())
198        return QVariant::fromValue(QImage());
199    return QVariant();
200}
201
202QVariant QtPixmapRuntime::toQt(JSContextRef context, JSObjectRef obj, QMetaType::Type hint, JSValueRef* exception)
203{
204    if (!obj)
205        return emptyVariantForHint(hint);
206
207    if (JSValueIsObjectOfClass(context, obj, QtPixmapRuntime::getClassRef())) {
208        QVariant* originalVariant = static_cast<QVariant*>(JSObjectGetPrivate(obj));
209        if (hint == qMetaTypeId<QPixmap>())
210            return QVariant::fromValue<QPixmap>(toPixmap(*originalVariant));
211
212        if (hint == qMetaTypeId<QImage>())
213            return QVariant::fromValue<QImage>(toImage(*originalVariant));
214    }
215
216    JSObject* jsObject = ::toJS(obj);
217    if (!jsObject->inherits(&JSHTMLImageElement::s_info))
218        return emptyVariantForHint(hint);
219
220    JSHTMLImageElement* elementJSWrapper = static_cast<JSHTMLImageElement*>(jsObject);
221    HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(elementJSWrapper->impl());
222
223    if (!imageElement)
224        return emptyVariantForHint(hint);
225
226    CachedImage* cachedImage = imageElement->cachedImage();
227    if (!cachedImage)
228        return emptyVariantForHint(hint);
229
230    Image* image = cachedImage->imageForRenderer(imageElement->renderer());
231    if (!image)
232        return emptyVariantForHint(hint);
233
234    QPixmap* pixmap = image->nativeImageForCurrentFrame();
235    if (!pixmap)
236        return emptyVariantForHint(hint);
237
238    return (hint == static_cast<QMetaType::Type>(qMetaTypeId<QPixmap>()))
239        ? QVariant::fromValue<QPixmap>(*pixmap)
240        : QVariant::fromValue<QImage>(pixmap->toImage());
241}
242
243bool QtPixmapRuntime::canHandle(QMetaType::Type hint)
244{
245    return hint == qMetaTypeId<QImage>() || hint == qMetaTypeId<QPixmap>();
246}
247
248JSClassRef QtPixmapRuntime::getClassRef()
249{
250    static const JSStaticValue staticValues[] = {
251        { "width", getPixmapWidth, 0, 0 },
252        { "height", getPixmapHeight, 0, 0 },
253        { 0, 0, 0, 0}
254    };
255
256    static const JSStaticFunction staticFunctions[] = {
257        { "assignToHTMLImageElement", assignToHTMLImageElement, 0 },
258        { "toDataUrl", pixmapToDataUrl, 0 },
259        { "toImageData", pixmapToImageData, 0 },
260        { "toString", pixmapToString, 0 },
261        { 0, 0, 0 }
262    };
263
264    static const JSClassDefinition classDefinition = {
265        0, 0, "QtPixmapRuntimeObject", 0, staticValues, staticFunctions,
266        0, finalizePixmap, 0, 0, 0, 0, 0, 0, 0, 0, 0
267    };
268
269    static JSClassRef classRef = JSClassCreate(&classDefinition);
270    return classRef;
271}
272
273
274}
275
276}
277