Adopt is/toHTMLImageElement for code cleanup
[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 "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
42 using namespace WebCore;
43 namespace JSC {
44
45 namespace Bindings {
46
47 static 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
79 static 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
90 static 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
101 static 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
110 static 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
116 static 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
122 static 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 = toHTMLImageElement(static_cast<JSHTMLImageElement*>(jsObject)->impl());
141     imageElement->setCachedImage(new CachedImage(stillImage.get()));
142     return JSValueMakeUndefined(context);
143 }
144
145 static 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
159 static 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
172 static 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
183 static void finalizePixmap(JSObjectRef object)
184 {
185     delete static_cast<QVariant*>(JSObjectGetPrivate(object));
186 }
187
188 JSObjectRef QtPixmapRuntime::toJS(JSContextRef context, const QVariant& value, JSValueRef* exception)
189 {
190     return JSObjectMake(context, getClassRef(), new QVariant(value));
191 }
192
193 static 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
202 QVariant 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 = toHTMLImageElement(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
243 bool QtPixmapRuntime::canHandle(QMetaType::Type hint)
244 {
245     return hint == qMetaTypeId<QImage>() || hint == qMetaTypeId<QPixmap>();
246 }
247
248 JSClassRef 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 }