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