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 "CachedImage.h"
23#include "HTMLImageElement.h"
24#include "JSGlobalObject.h"
25#include "JSHTMLImageElement.h"
26#include "JSLock.h"
27#include "ObjectPrototype.h"
28#include "StillImageQt.h"
29#include <QBuffer>
30#include <QByteArray>
31#include <QImage>
32#include <QPixmap>
33#include <QVariant>
34#include <runtime_method.h>
35#include <runtime_object.h>
36#include <runtime_root.h>
37#include "runtime/FunctionPrototype.h"
38
39using namespace WebCore;
40namespace JSC {
41
42namespace Bindings {
43
44class QtPixmapClass : public Class {
45public:
46    QtPixmapClass();
47    virtual MethodList methodsNamed(const Identifier&, Instance*) const;
48    virtual Field* fieldNamed(const Identifier&, Instance*) const;
49};
50
51
52class QtPixmapWidthField : public Field {
53public:
54    static const char* name() { return "width"; }
55    virtual JSValue valueFromInstance(ExecState*, const Instance* instance) const
56    {
57        return jsNumber(static_cast<const QtPixmapInstance*>(instance)->width());
58    }
59    virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const {}
60};
61
62class QtPixmapHeightField : public Field {
63public:
64    static const char* name() { return "height"; }
65    virtual JSValue valueFromInstance(ExecState*, const Instance* instance) const
66    {
67        return jsNumber(static_cast<const QtPixmapInstance*>(instance)->height());
68    }
69    virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const {}
70};
71
72class QtPixmapRuntimeMethod : public Method {
73public:
74    virtual int numParameters() const
75    {
76        return 0;
77    }
78    virtual JSValue invoke(ExecState* exec, QtPixmapInstance*) = 0;
79
80};
81
82// this function receives an HTML image element as a parameter, makes it display the pixmap/image from Qt
83class QtPixmapAssignToElementMethod : public QtPixmapRuntimeMethod {
84public:
85    static const char* name() { return "assignToHTMLImageElement"; }
86    JSValue invoke(ExecState* exec, QtPixmapInstance* instance)
87    {
88        if (!exec->argumentCount())
89            return jsUndefined();
90
91        JSObject* objectArg = exec->argument(0).toObject(exec);
92        if (!objectArg)
93            return jsUndefined();
94
95        if (!objectArg->inherits(&JSHTMLImageElement::s_info))
96            return jsUndefined();
97
98        // we now know that we have a valid <img> element as the argument, we can attach the pixmap to it.
99        PassRefPtr<StillImage> stillImage = WebCore::StillImage::create(instance->toPixmap());
100        HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(static_cast<JSHTMLImageElement*>(objectArg)->impl());
101        imageElement->setCachedImage(new CachedImage(stillImage.get()));
102        JSDOMGlobalObject* global = static_cast<JSDOMGlobalObject*>(instance->rootObject()->globalObject());
103        toJS(exec, global, imageElement->document());
104        return jsUndefined();
105    }
106
107    virtual int numParameters() const
108    {
109        return 1;
110    }
111};
112
113// this function encodes the image to a dataUrl, to be used in background etc. Note: very slow.
114class QtPixmapToDataUrlMethod : public QtPixmapRuntimeMethod {
115public:
116    static const char* name() { return "toDataUrl"; }
117    JSValue invoke(ExecState* exec, QtPixmapInstance* instance)
118    {
119        QByteArray byteArray;
120        QBuffer buffer(&byteArray);
121        instance->toImage().save(&buffer, "PNG");
122        const QString encodedString = QLatin1String("data:image/png;base64,") + QLatin1String(byteArray.toBase64());
123        const UString ustring((UChar*)encodedString.utf16(), encodedString.length());
124        return jsString(exec, ustring);
125    }
126};
127
128class QtPixmapToStringMethod : public QtPixmapRuntimeMethod {
129    public:
130    static const char* name() { return "toString"; }
131    JSValue invoke(ExecState* exec, QtPixmapInstance* instance)
132    {
133        return instance->valueOf(exec);
134    }
135};
136
137struct QtPixmapMetaData {
138    QtPixmapToDataUrlMethod toDataUrlMethod;
139    QtPixmapAssignToElementMethod assignToElementMethod;
140    QtPixmapToStringMethod toStringMethod;
141    QtPixmapHeightField heightField;
142    QtPixmapWidthField widthField;
143    QtPixmapClass cls;
144} qt_pixmap_metaData;
145
146// Derived RuntimeObject
147class QtPixmapRuntimeObject : public RuntimeObject {
148public:
149    QtPixmapRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<Instance>);
150
151    static const ClassInfo s_info;
152
153    static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
154    {
155        return Structure::create(globalData, prototype, TypeInfo(ObjectType,  StructureFlags), AnonymousSlotCount, &s_info);
156    }
157
158protected:
159    static const unsigned StructureFlags = RuntimeObject::StructureFlags | OverridesMarkChildren;
160};
161
162QtPixmapRuntimeObject::QtPixmapRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance)
163    : RuntimeObject(exec, globalObject, WebCore::deprecatedGetDOMStructure<QtPixmapRuntimeObject>(exec), instance)
164{
165}
166
167const ClassInfo QtPixmapRuntimeObject::s_info = { "QtPixmapRuntimeObject", &RuntimeObject::s_info, 0, 0 };
168
169QtPixmapClass::QtPixmapClass()
170{
171}
172
173
174Class* QtPixmapInstance::getClass() const
175{
176    return &qt_pixmap_metaData.cls;
177}
178
179JSValue QtPixmapInstance::getMethod(ExecState* exec, const Identifier& propertyName)
180{
181    MethodList methodList = getClass()->methodsNamed(propertyName, this);
182    return new (exec) RuntimeMethod(exec, exec->lexicalGlobalObject(), WebCore::deprecatedGetDOMStructure<RuntimeMethod>(exec), propertyName, methodList);
183}
184
185JSValue QtPixmapInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod)
186{
187    const MethodList& methods = *runtimeMethod->methods();
188
189    if (methods.size() == 1) {
190        QtPixmapRuntimeMethod* method = static_cast<QtPixmapRuntimeMethod*>(methods[0]);
191        return method->invoke(exec, this);
192    }
193    return jsUndefined();
194}
195
196MethodList QtPixmapClass::methodsNamed(const Identifier& identifier, Instance*) const
197{
198    MethodList methods;
199    if (identifier == QtPixmapToDataUrlMethod::name())
200        methods.append(&qt_pixmap_metaData.toDataUrlMethod);
201    else if (identifier == QtPixmapAssignToElementMethod::name())
202        methods.append(&qt_pixmap_metaData.assignToElementMethod);
203    else if (identifier == QtPixmapToStringMethod::name())
204        methods.append(&qt_pixmap_metaData.toStringMethod);
205    return methods;
206}
207
208Field* QtPixmapClass::fieldNamed(const Identifier& identifier, Instance*) const
209{
210    if (identifier == QtPixmapWidthField::name())
211        return &qt_pixmap_metaData.widthField;
212    if (identifier == QtPixmapHeightField::name())
213        return &qt_pixmap_metaData.heightField;
214    return 0;
215}
216
217void QtPixmapInstance::getPropertyNames(ExecState*exec, PropertyNameArray& arr)
218{
219    arr.add(Identifier(exec, UString(QtPixmapToDataUrlMethod::name())));
220    arr.add(Identifier(exec, UString(QtPixmapAssignToElementMethod::name())));
221    arr.add(Identifier(exec, UString(QtPixmapToStringMethod::name())));
222    arr.add(Identifier(exec, UString(QtPixmapWidthField::name())));
223    arr.add(Identifier(exec, UString(QtPixmapHeightField::name())));
224}
225
226JSValue QtPixmapInstance::defaultValue(ExecState* exec, PreferredPrimitiveType ptype) const
227{
228    if (ptype == PreferNumber) {
229        return jsBoolean(
230                (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>()) && !(data.value<QImage>()).isNull())
231                || (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>()) && !data.value<QPixmap>().isNull()));
232    }
233
234    if (ptype == PreferString)
235        return valueOf(exec);
236
237    return jsUndefined();
238}
239
240JSValue QtPixmapInstance::valueOf(ExecState* exec) const
241{
242    const QString stringValue = QString::fromLatin1("[Qt Native Pixmap %1,%2]").arg(width()).arg(height());
243    UString ustring((UChar*)stringValue.utf16(), stringValue.length());
244    return jsString(exec, ustring);
245}
246
247QtPixmapInstance::QtPixmapInstance(PassRefPtr<RootObject> rootObj, const QVariant& d)
248        :Instance(rootObj), data(d)
249{
250}
251
252int QtPixmapInstance::width() const
253{
254    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>()))
255        return data.value<QPixmap>().width();
256    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>()))
257        return data.value<QImage>().width();
258    return 0;
259}
260
261int QtPixmapInstance::height() const
262{
263    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>()))
264        return data.value<QPixmap>().height();
265    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>()))
266        return data.value<QImage>().height();
267    return 0;
268}
269
270QPixmap QtPixmapInstance::toPixmap()
271{
272    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>()))
273        return data.value<QPixmap>();
274
275    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>())) {
276        const QPixmap pixmap = QPixmap::fromImage(data.value<QImage>());
277        data = QVariant::fromValue<QPixmap>(pixmap);
278        return pixmap;
279    }
280
281    return QPixmap();
282}
283
284QImage QtPixmapInstance::toImage()
285{
286    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>()))
287        return data.value<QImage>();
288
289    if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>())) {
290        const QImage image = data.value<QPixmap>().toImage();
291        data = QVariant::fromValue<QImage>(image);
292        return image;
293    }
294
295    return QImage();
296}
297
298QVariant QtPixmapInstance::variantFromObject(JSObject* object, QMetaType::Type hint)
299{
300    if (!object)
301        goto returnEmptyVariant;
302
303    if (object->inherits(&JSHTMLImageElement::s_info)) {
304        JSHTMLImageElement* elementJSWrapper = static_cast<JSHTMLImageElement*>(object);
305        HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(elementJSWrapper->impl());
306
307        if (!imageElement)
308            goto returnEmptyVariant;
309
310        CachedImage* cachedImage = imageElement->cachedImage();
311        if (!cachedImage)
312            goto returnEmptyVariant;
313
314        Image* image = cachedImage->image();
315        if (!image)
316            goto returnEmptyVariant;
317
318        QPixmap* pixmap = image->nativeImageForCurrentFrame();
319        if (!pixmap)
320            goto returnEmptyVariant;
321
322        return (hint == static_cast<QMetaType::Type>(qMetaTypeId<QPixmap>()))
323                  ? QVariant::fromValue<QPixmap>(*pixmap)
324                  : QVariant::fromValue<QImage>(pixmap->toImage());
325    }
326
327    if (object->inherits(&QtPixmapRuntimeObject::s_info)) {
328        QtPixmapRuntimeObject* runtimeObject = static_cast<QtPixmapRuntimeObject*>(object);
329        QtPixmapInstance* instance = static_cast<QtPixmapInstance*>(runtimeObject->getInternalInstance());
330        if (!instance)
331            goto returnEmptyVariant;
332
333        if (hint == qMetaTypeId<QPixmap>())
334            return QVariant::fromValue<QPixmap>(instance->toPixmap());
335
336        if (hint == qMetaTypeId<QImage>())
337            return QVariant::fromValue<QImage>(instance->toImage());
338    }
339
340returnEmptyVariant:
341    if (hint == qMetaTypeId<QPixmap>())
342        return QVariant::fromValue<QPixmap>(QPixmap());
343    if (hint == qMetaTypeId<QImage>())
344        return QVariant::fromValue<QImage>(QImage());
345    return QVariant();
346}
347
348RuntimeObject* QtPixmapInstance::newRuntimeObject(ExecState* exec)
349{
350    return new(exec) QtPixmapRuntimeObject(exec, exec->lexicalGlobalObject(), this);
351}
352
353JSObject* QtPixmapInstance::createPixmapRuntimeObject(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& data)
354{
355    JSLock lock(SilenceAssertionsOnly);
356    RefPtr<QtPixmapInstance> instance = adoptRef(new QtPixmapInstance(root, data));
357    return instance->createRuntimeObject(exec);
358}
359
360bool QtPixmapInstance::canHandle(QMetaType::Type hint)
361{
362    return hint == qMetaTypeId<QImage>() || hint == qMetaTypeId<QPixmap>();
363}
364
365}
366
367}
368