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