/* * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "config.h" #include "qt_pixmapruntime.h" #include "APICast.h" #include "CachedImage.h" #include "HTMLImageElement.h" #include "ImageData.h" #include "IntSize.h" #include "JSDOMBinding.h" #include "JSGlobalObject.h" #include "JSHTMLImageElement.h" #include "JSImageData.h" #include "JSRetainPtr.h" #include "JavaScript.h" #include "RenderElement.h" #include "StillImageQt.h" #include #include #include #include #include #include #include using namespace WebCore; namespace JSC { namespace Bindings { static void copyPixelsInto(const QImage& sourceImage, int width, int height, unsigned char* destPixels) { QImage image(sourceImage); switch (image.format()) { case QImage::Format_RGB888: for (int y = 0; y < height; y++) { const uchar* scanLine = image.scanLine(y); for (int x = 0; x < width; x++) { *(destPixels++) = *(scanLine++); *(destPixels++) = *(scanLine++); *(destPixels++) = *(scanLine++); *(destPixels++) = 0xFF; } } break; default: image = image.convertToFormat(QImage::Format_ARGB32); // Fall through case QImage::Format_RGB32: case QImage::Format_ARGB32: for (int y = 0; y < height; y++) { const quint32* scanLine = reinterpret_cast_ptr(image.scanLine(y)); for (int x = 0; x < width; x++) { QRgb pixel = scanLine[x]; qToBigEndian((pixel << 8) | qAlpha(pixel), destPixels); destPixels += 4; } } break; } } static QPixmap toPixmap(const QVariant& data) { if (data.type() == static_cast(qMetaTypeId())) return data.value(); if (data.type() == static_cast(qMetaTypeId())) return QPixmap::fromImage(data.value()); return QPixmap(); } static QImage toImage(const QVariant& data) { if (data.type() == static_cast(qMetaTypeId())) return data.value(); if (data.type() == static_cast(qMetaTypeId())) return data.value().toImage(); return QImage(); } static QSize imageSizeForVariant(const QVariant& data) { if (data.type() == static_cast(qMetaTypeId())) return data.value().size(); if (data.type() == static_cast(qMetaTypeId())) return data.value().size(); return QSize(0, 0); } static JSValueRef getPixmapWidth(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef*) { QVariant& data = *static_cast(JSObjectGetPrivate(object)); return JSValueMakeNumber(context, imageSizeForVariant(data).width()); } static JSValueRef getPixmapHeight(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef*) { QVariant& data = *static_cast(JSObjectGetPrivate(object)); return JSValueMakeNumber(context, imageSizeForVariant(data).height()); } static JSValueRef assignToHTMLImageElement(JSContextRef context, JSObjectRef /*function*/, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { if (!argumentCount) return JSValueMakeUndefined(context); JSObjectRef objectArg = JSValueToObject(context, arguments[0], exception); if (!objectArg) return JSValueMakeUndefined(context); JSObject* jsObject = ::toJS(objectArg); if (!jsObject->inherits(JSHTMLImageElement::info())) return JSValueMakeUndefined(context); QVariant& data = *static_cast(JSObjectGetPrivate(object)); // We now know that we have a valid element as the argument, we can attach the pixmap to it. RefPtr stillImage = WebCore::StillImage::create(toPixmap(data)); HTMLImageElement* imageElement = JSHTMLImageElement::toWrapped(jsObject); imageElement->setCachedImage(new CachedImage(stillImage.get(), SessionID::defaultSessionID())); return JSValueMakeUndefined(context); } static JSValueRef pixmapToImageData(JSContextRef context, JSObjectRef /*function*/, JSObjectRef object, size_t /*argumentCount*/, const JSValueRef /*arguments*/[], JSValueRef* /*exception*/) { QVariant& data = *static_cast(JSObjectGetPrivate(object)); QImage image = toImage(data); int width = image.width(); int height = image.height(); JSC::ExecState* exec = ::toJS(context); JSLockHolder locker(exec); RefPtr imageData = ImageData::create(IntSize(width, height)); copyPixelsInto(image, width, height, imageData->data()->data()); JSDOMGlobalObject* globalObject = static_cast(exec->lexicalGlobalObject()); return ::toRef(exec, toJS(exec, globalObject, imageData.get())); } static JSValueRef pixmapToDataUrl(JSContextRef context, JSObjectRef /*function*/, JSObjectRef object, size_t /*argumentCount*/, const JSValueRef /*arguments*/[], JSValueRef* /*exception*/) { QVariant& data = *static_cast(JSObjectGetPrivate(object)); QByteArray byteArray; QBuffer buffer(&byteArray); toImage(data).save(&buffer, "PNG"); QByteArray encoded = QByteArray("data:image/png;base64,") + byteArray.toBase64(); JSRetainPtr str(Adopt, JSStringCreateWithUTF8CString(encoded.constData())); JSValueRef value = JSValueMakeString(context, str.get()); return value; } static JSValueRef pixmapToString(JSContextRef context, JSObjectRef /*function*/, JSObjectRef object, size_t /*argumentCount*/, const JSValueRef /*arguments*/[], JSValueRef* /*exception*/) { QVariant& data = *static_cast(JSObjectGetPrivate(object)); QSize size = imageSizeForVariant(data); QString stringValue = QString::fromLatin1("[Qt Native Pixmap %1,%2]").arg(size.width()).arg(size.height()); JSRetainPtr str(Adopt, JSStringCreateWithUTF8CString(stringValue.toUtf8().constData())); JSValueRef value = JSValueMakeString(context, str.get()); return value; } static void finalizePixmap(JSObjectRef object) { delete static_cast(JSObjectGetPrivate(object)); } JSObjectRef QtPixmapRuntime::toJS(JSContextRef context, const QVariant& value, JSValueRef* /*exception*/) { return JSObjectMake(context, getClassRef(), new QVariant(value)); } static QVariant emptyVariantForHint(QMetaType::Type hint) { if (hint == qMetaTypeId()) return QVariant::fromValue(QPixmap()); if (hint == qMetaTypeId()) return QVariant::fromValue(QImage()); return QVariant(); } QVariant QtPixmapRuntime::toQt(JSContextRef context, JSObjectRef obj, QMetaType::Type hint, JSValueRef* /*exception*/) { if (!obj) return emptyVariantForHint(hint); if (JSValueIsObjectOfClass(context, obj, QtPixmapRuntime::getClassRef())) { QVariant* originalVariant = static_cast(JSObjectGetPrivate(obj)); if (hint == qMetaTypeId()) return QVariant::fromValue(toPixmap(*originalVariant)); if (hint == qMetaTypeId()) return QVariant::fromValue(toImage(*originalVariant)); } JSObject* jsObject = ::toJS(obj); if (!jsObject->inherits(JSHTMLImageElement::info())) return emptyVariantForHint(hint); HTMLImageElement* imageElement = JSHTMLImageElement::toWrapped(jsObject); if (!imageElement) return emptyVariantForHint(hint); CachedImage* cachedImage = imageElement->cachedImage(); if (!cachedImage) return emptyVariantForHint(hint); Image* image = cachedImage->imageForRenderer(imageElement->renderer()); if (!image) return emptyVariantForHint(hint); QPixmap* pixmap = image->nativeImageForCurrentFrame(); if (!pixmap) return emptyVariantForHint(hint); return (hint == static_cast(qMetaTypeId())) ? QVariant::fromValue(*pixmap) : QVariant::fromValue(pixmap->toImage()); } bool QtPixmapRuntime::canHandle(QMetaType::Type hint) { return hint == qMetaTypeId() || hint == qMetaTypeId(); } JSClassRef QtPixmapRuntime::getClassRef() { static const JSStaticValue staticValues[] = { { "width", getPixmapWidth, 0, 0 }, { "height", getPixmapHeight, 0, 0 }, { 0, 0, 0, 0} }; static const JSStaticFunction staticFunctions[] = { { "assignToHTMLImageElement", assignToHTMLImageElement, 0 }, { "toDataUrl", pixmapToDataUrl, 0 }, { "toImageData", pixmapToImageData, 0 }, { "toString", pixmapToString, 0 }, { 0, 0, 0 } }; static const JSClassDefinition classDefinition = { 0, 0, "QtPixmapRuntimeObject", 0, staticValues, staticFunctions, 0, finalizePixmap, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static JSClassRef classRef = JSClassCreate(&classDefinition); return classRef; } } }