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