File indexing completed on 2024-11-24 03:56:27

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "register_machinery.hpp"
0008 
0009 #include <utility>
0010 
0011 #include <QColor>
0012 #include <QUuid>
0013 #include <QVariant>
0014 #include <QPointF>
0015 #include <QSizeF>
0016 #include <QVector2D>
0017 #include <QRectF>
0018 #include <QByteArray>
0019 #include <QDateTime>
0020 #include <QDate>
0021 #include <QTime>
0022 #include <QGradient>
0023 
0024 #include "app/log/log.hpp"
0025 
0026 
0027 namespace app::scripting::python {
0028 
0029 template<class T> const char* type_name()
0030 {
0031 #if QT_VERSION_MAJOR >= 6
0032     return QMetaType(qMetaTypeId<T>()).name();
0033 #else
0034     return QMetaType::typeName(qMetaTypeId<T>());
0035 #endif
0036 }
0037 template<int> struct meta_2_cpp_s;
0038 template<class> struct cpp_2_meta_s;
0039 
0040 #define TYPE_NAME(Type) //template<> const char* type_name<Type>() { return #Type; }
0041 #define SETUP_TYPE(MetaInt, Type)                                   \
0042     TYPE_NAME(Type)                                                 \
0043     template<> struct meta_2_cpp_s<MetaInt> { using type = Type; }; \
0044     template<> struct cpp_2_meta_s<Type> { static constexpr const int value = MetaInt; };
0045 
0046 template<int meta_type> using meta_2_cpp = typename meta_2_cpp_s<meta_type>::type;
0047 template<class T> constexpr const int cpp_2_meta = cpp_2_meta_s<T>::value;
0048 
0049 SETUP_TYPE(QMetaType::Int,          int)
0050 SETUP_TYPE(QMetaType::Bool,         bool)
0051 SETUP_TYPE(QMetaType::Double,       double)
0052 SETUP_TYPE(QMetaType::Float,        float)
0053 SETUP_TYPE(QMetaType::UInt,         unsigned int)
0054 SETUP_TYPE(QMetaType::Long,         long)
0055 SETUP_TYPE(QMetaType::LongLong,     long long)
0056 SETUP_TYPE(QMetaType::Short,        short)
0057 SETUP_TYPE(QMetaType::ULong,        unsigned long)
0058 SETUP_TYPE(QMetaType::ULongLong,    unsigned long long)
0059 SETUP_TYPE(QMetaType::UShort,       unsigned short)
0060 SETUP_TYPE(QMetaType::QString,      QString)
0061 SETUP_TYPE(QMetaType::QColor,       QColor)
0062 SETUP_TYPE(QMetaType::QUuid,        QUuid)
0063 SETUP_TYPE(QMetaType::QObjectStar,  QObject*)
0064 SETUP_TYPE(QMetaType::QVariantList, QVariantList)
0065 SETUP_TYPE(QMetaType::QVariant,     QVariant)
0066 SETUP_TYPE(QMetaType::QStringList,  QStringList)
0067 SETUP_TYPE(QMetaType::QVariantMap,  QVariantMap)
0068 SETUP_TYPE(QMetaType::QVariantHash, QVariantHash)
0069 SETUP_TYPE(QMetaType::QPointF,      QPointF)
0070 SETUP_TYPE(QMetaType::QSizeF,       QSizeF)
0071 SETUP_TYPE(QMetaType::QSize,        QSize)
0072 SETUP_TYPE(QMetaType::QVector2D,    QVector2D)
0073 SETUP_TYPE(QMetaType::QRectF,       QRectF)
0074 SETUP_TYPE(QMetaType::QByteArray,   QByteArray)
0075 SETUP_TYPE(QMetaType::QDateTime,    QDateTime)
0076 SETUP_TYPE(QMetaType::QDate,        QDate)
0077 SETUP_TYPE(QMetaType::QTime,        QTime)
0078 SETUP_TYPE(QMetaType::QImage,       QImage)
0079 // If you add stuff here, remember to add it to supported_types too
0080 
0081 TYPE_NAME(std::vector<QObject*>)
0082 
0083 using supported_types = std::integer_sequence<int,
0084     QMetaType::Bool,
0085     QMetaType::Int,
0086     QMetaType::Double,
0087     QMetaType::Float,
0088     QMetaType::UInt,
0089     QMetaType::Long,
0090     QMetaType::LongLong,
0091     QMetaType::Short,
0092     QMetaType::ULong,
0093     QMetaType::ULongLong,
0094     QMetaType::UShort,
0095     QMetaType::QString,
0096     QMetaType::QColor,
0097     QMetaType::QUuid,
0098     QMetaType::QObjectStar,
0099     QMetaType::QVariantList,
0100     QMetaType::QVariant,
0101     QMetaType::QStringList,
0102     QMetaType::QVariantMap,
0103     QMetaType::QVariantHash,
0104     QMetaType::QPointF,
0105     QMetaType::QSizeF,
0106     QMetaType::QSize,
0107     QMetaType::QVector2D,
0108     QMetaType::QRectF,
0109     QMetaType::QByteArray,
0110     QMetaType::QDateTime,
0111     QMetaType::QDate,
0112     QMetaType::QTime,
0113     QMetaType::QImage
0114     // Ensure new types have SETUP_TYPE
0115 >;
0116 
0117 template<class T> QVariant qvariant_from_cpp(const T& t) { return QVariant::fromValue(t); }
0118 template<class T> T qvariant_to_cpp(const QVariant& v) { return v.value<T>(); }
0119 
0120 template<> QVariant qvariant_from_cpp<std::string>(const std::string& t) { return QString::fromStdString(t); }
0121 template<> std::string qvariant_to_cpp<std::string>(const QVariant& v) { return v.toString().toStdString(); }
0122 
0123 template<> QVariant qvariant_from_cpp<QVariant>(const QVariant& t) { return t; }
0124 template<> QVariant qvariant_to_cpp<QVariant>(const QVariant& v) { return v; }
0125 
0126 
0127 template<> void qvariant_to_cpp<void>(const QVariant&) {}
0128 
0129 
0130 template<>
0131 QVariant qvariant_from_cpp<std::vector<QObject*>>(const std::vector<QObject*>& t)
0132 {
0133     QVariantList list;
0134     for ( QObject* obj : t )
0135         list.push_back(QVariant::fromValue(obj));
0136     return list;
0137 
0138 }
0139 template<> std::vector<QObject*> qvariant_to_cpp<std::vector<QObject*>>(const QVariant& v)
0140 {
0141     std::vector<QObject*> objects;
0142     for ( const QVariant& vi : v.toList() )
0143         objects.push_back(vi.value<QObject*>());
0144     return objects;
0145 }
0146 
0147 template<int i, template<class FuncT> class Func, class RetT, class... FuncArgs>
0148 bool type_dispatch_impl_step(int meta_type, RetT& ret, FuncArgs&&... args)
0149 {
0150     if ( meta_type != i )
0151         return false;
0152 
0153     ret = Func<meta_2_cpp<i>>::do_the_thing(std::forward<FuncArgs>(args)...);
0154     return true;
0155 }
0156 
0157 template<template<class FuncT> class Func, class RetT, class... FuncArgs, int... i>
0158 bool type_dispatch_impl(int meta_type, RetT& ret, std::integer_sequence<int, i...>, FuncArgs&&... args)
0159 {
0160     return (type_dispatch_impl_step<i, Func>(meta_type, ret, std::forward<FuncArgs>(args)...)||...);
0161 }
0162 
0163 template<template<class FuncT> class Func, class RetT, class... FuncArgs>
0164 RetT type_dispatch(int meta_type, FuncArgs&&... args)
0165 {
0166     if ( meta_type >= QMetaType::User )
0167     {
0168         if ( QMetaType(meta_type).flags() & QMetaType::IsEnumeration )
0169             return Func<int>::do_the_thing(std::forward<FuncArgs>(args)...);
0170         return Func<QObject*>::do_the_thing(std::forward<FuncArgs>(args)...);
0171     }
0172     RetT ret;
0173     type_dispatch_impl<Func>(meta_type, ret, supported_types(), std::forward<FuncArgs>(args)...);
0174     return ret;
0175 }
0176 
0177 template<template<class FuncT> class Func, class RetT, class... FuncArgs>
0178 static RetT type_dispatch_maybe_void(int meta_type, FuncArgs&&... args)
0179 {
0180     if ( meta_type == QMetaType::Void )
0181         return Func<void>::do_the_thing(std::forward<FuncArgs>(args)...);
0182     return type_dispatch<Func, RetT>(meta_type, std::forward<FuncArgs>(args)...);
0183 }
0184 
0185 std::string fix_type(QByteArray ba)
0186 {
0187     if ( ba.endsWith('*') || ba.endsWith('&') )
0188         ba.remove(ba.size()-1, 1);
0189 
0190     if ( ba.startsWith("const ") )
0191         ba.remove(0, 6);
0192 
0193     if ( ba == "QString" )
0194         return "str";
0195     else if ( ba == "QVariantList" )
0196         return "list";
0197     if ( ba == "QStringList" )
0198         return "List[str]";
0199     else if ( ba == "double" )
0200         return "float";
0201     else if ( ba == "void" )
0202         return "None";
0203     else if ( ba == "QImage" )
0204         return "PIL.Image.Image";
0205 
0206     return ba.toStdString();
0207 }
0208 
0209 
0210 template<class CppType>
0211     struct RegisterProperty
0212     {
0213         static PyPropertyInfo do_the_thing(const QMetaProperty& prop)
0214         {
0215             PyPropertyInfo py;
0216             py.name = prop.name();
0217             std::string sig = "Type: " + fix_type(prop.typeName());
0218             py.get = py::cpp_function(
0219                 [prop](const QObject* o) { return qvariant_to_cpp<CppType>(prop.read(o)); },
0220                 py::return_value_policy::automatic_reference,
0221                 sig.c_str()
0222             );
0223 
0224             if ( prop.isWritable() )
0225                 py.set = py::cpp_function([prop](QObject* o, const CppType& v) {
0226                     prop.write(o, qvariant_from_cpp<CppType>(v));
0227                 });
0228             return py;
0229         }
0230     };
0231 
0232 PyPropertyInfo register_property(const QMetaProperty& prop, const QMetaObject& meta)
0233 {
0234     if ( !prop.isScriptable() )
0235         return {};
0236 
0237     PyPropertyInfo pyprop = type_dispatch<RegisterProperty, PyPropertyInfo>(prop.userType(), prop);
0238     if ( !pyprop.name )
0239         log::LogStream("Python", "", log::Error) << "Invalid property" << meta.className() << "::" << prop.name() << "of type" << prop.userType() << prop.typeName();
0240     return pyprop;
0241 }
0242 
0243 class ArgumentBuffer
0244 {
0245 public:
0246     ArgumentBuffer(const QMetaMethod& method) : method(method) {}
0247     ArgumentBuffer(const ArgumentBuffer&) = delete;
0248     ArgumentBuffer& operator=(const ArgumentBuffer&) = delete;
0249     ~ArgumentBuffer()
0250     {
0251         for ( int i = 0; i < destructors_used; i++)
0252         {
0253             destructors[i]->destruct();
0254             delete destructors[i];
0255         }
0256     }
0257 
0258     template<class CppType>
0259     const char* object_type_name(const CppType&)
0260     {
0261         return type_name<CppType>();
0262     }
0263 
0264     std::string object_type_name(QObject* value)
0265     {
0266         std::string s = value->metaObject()->className();
0267         std::string target = method.parameterTypes()[arguments].toStdString();
0268         if ( !target.empty() && target.back() == '*' )
0269         {
0270             target.pop_back();
0271             if ( s != target )
0272             {
0273                 for ( auto mo = value->metaObject()->superClass(); mo; mo = mo->superClass() )
0274                 {
0275                     std::string moname = mo->className();
0276                     if ( moname == target )
0277                         return target + "*";
0278                 }
0279             }
0280         }
0281         return s + "*";
0282     }
0283 
0284     template<class CppType>
0285     CppType* allocate(const CppType& value)
0286     {
0287         if ( avail() < int(sizeof(CppType)) )
0288             throw py::type_error("Cannot allocate argument");
0289 
0290         CppType* addr = new (next_mem()) CppType;
0291         buffer_used += sizeof(CppType);
0292         names[arguments] = object_type_name(value);
0293         generic_args[arguments] = { names[arguments].c_str(), addr };
0294         ensure_destruction(addr);
0295         arguments += 1;
0296         *addr = value;
0297         return addr;
0298     }
0299 
0300     template<class CppType>
0301     void allocate_return_type(const char* name)
0302     {
0303         if ( avail() < int(sizeof(CppType)) )
0304             throw py::type_error("Cannot allocate return value");
0305 
0306         CppType* addr = new (next_mem()) CppType;
0307         buffer_used += sizeof(CppType);
0308         ret = { name, addr };
0309         ensure_destruction(addr);
0310         ret_addr = addr;
0311     }
0312 
0313     template<class CppType>
0314     CppType return_value()
0315     {
0316         return *static_cast<CppType*>(ret_addr);
0317     }
0318 
0319     const QGenericArgument& arg(int i) const { return generic_args[i]; }
0320 
0321     const QGenericReturnArgument& return_arg() const { return ret; }
0322 
0323 private:
0324     class Destructor
0325     {
0326     public:
0327         Destructor() = default;
0328         Destructor(const Destructor&) = delete;
0329         Destructor& operator=(const Destructor&) = delete;
0330         virtual ~Destructor() = default;
0331         virtual void destruct() const = 0;
0332     };
0333 
0334     template<class CppType>
0335     struct DestructorImpl : public Destructor
0336     {
0337         DestructorImpl(CppType* addr) : addr(addr) {}
0338 
0339         void destruct() const override
0340         {
0341             addr->~CppType();
0342         }
0343 
0344         CppType* addr;
0345     };
0346 
0347     int arguments = 0;
0348     int buffer_used = 0;
0349     std::array<char, 128> buffer;
0350     std::array<Destructor*, 9> destructors;
0351     std::array<QGenericArgument, 9> generic_args;
0352     std::array<std::string, 9> names;
0353     QGenericReturnArgument ret;
0354     void* ret_addr = nullptr;
0355     QMetaMethod method;
0356     int destructors_used = 0;
0357 
0358 
0359     int avail() { return buffer.size() - buffer_used; }
0360     void* next_mem() { return buffer.data() + buffer_used; }
0361 
0362 
0363     template<class CppType>
0364         std::enable_if_t<std::is_pod_v<CppType>> ensure_destruction(CppType*) {}
0365 
0366 
0367     template<class CppType>
0368         std::enable_if_t<!std::is_pod_v<CppType>> ensure_destruction(CppType* addr)
0369         {
0370             destructors[destructors_used] = new DestructorImpl<CppType>(addr);
0371             destructors_used++;
0372         }
0373 };
0374 
0375 template<> void ArgumentBuffer::allocate_return_type<void>(const char*){}
0376 template<> void ArgumentBuffer::return_value<void>(){}
0377 
0378 
0379 template<class CppType>
0380     struct ConvertArgument
0381     {
0382         static bool do_the_thing(const py::handle& val, ArgumentBuffer& buf)
0383         {
0384             buf.allocate<CppType>(val.cast<CppType>());
0385             return true;
0386         }
0387     };
0388 
0389 bool convert_argument(int meta_type, const py::handle& value, ArgumentBuffer& buf)
0390 {
0391     return type_dispatch<ConvertArgument, bool>(meta_type, value, buf);
0392 }
0393 
0394 template<class ReturnType>
0395 struct RegisterMethod
0396 {
0397     static PyMethodInfo do_the_thing(const QMetaMethod& meth, py::handle& handle)
0398     {
0399         PyMethodInfo py;
0400         py.name = meth.name().constData();
0401 
0402         std::string signature = "Signature:\n";
0403         signature += meth.name().toStdString();
0404         signature += "(self";
0405         auto names = meth.parameterNames();
0406         auto types = meth.parameterTypes();
0407         for ( int i = 0; i < meth.parameterCount(); i++ )
0408         {
0409             signature += ", ";
0410             signature += names[i].toStdString();
0411             signature += ": ";
0412             signature += fix_type(types[i]);
0413 
0414             if ( meth.parameterType(i) == QMetaType::UnknownType )
0415             {
0416                 auto cls = py::str(handle.attr("__name__"));
0417                 log::LogStream("Python", "", log::Error)
0418                     << "Invalid parameter" << QString::fromStdString(cls) << "::" << meth.name()
0419                     << i << names[i]
0420                     << "of type" << meth.parameterType(i) << types[i];
0421                 return {};
0422             }
0423         }
0424         signature += ") -> ";
0425         signature += fix_type(meth.typeName());
0426 
0427         py.method = py::cpp_function(
0428             [meth](QObject* o, py::args args) -> ReturnType
0429             {
0430                 int len = py::len(args);
0431                 if ( len > 9 )
0432                     throw pybind11::value_error("Invalid argument count");
0433 
0434                 ArgumentBuffer argbuf(meth);
0435 
0436                 argbuf.allocate_return_type<ReturnType>(meth.typeName());
0437 
0438                 for ( int i = 0; i < len; i++ )
0439                 {
0440                    if ( !convert_argument(meth.parameterType(i), args[i], argbuf) )
0441                         throw pybind11::value_error("Invalid argument");
0442                 }
0443 
0444                 // Calling by name from QMetaObject ensures that default arguments work correctly
0445                 bool ok = QMetaObject::invokeMethod(
0446                     o,
0447                     meth.name().constData(),
0448                     Qt::DirectConnection,
0449                     argbuf.return_arg(),
0450                     argbuf.arg(0),
0451                     argbuf.arg(1),
0452                     argbuf.arg(2),
0453                     argbuf.arg(3),
0454                     argbuf.arg(4),
0455                     argbuf.arg(5),
0456                     argbuf.arg(6),
0457                     argbuf.arg(7),
0458                     argbuf.arg(8),
0459                     argbuf.arg(9)
0460                 );
0461                 if ( !ok )
0462                     throw pybind11::value_error("Invalid method invocation");
0463                 return argbuf.return_value<ReturnType>();
0464             },
0465             py::name(py.name),
0466             py::is_method(handle),
0467             py::sibling(py::getattr(handle, py.name, py::none())),
0468             py::return_value_policy::automatic_reference,
0469             signature.c_str()
0470         );
0471 
0472         return py;
0473     }
0474 };
0475 
0476 PyMethodInfo register_method(const QMetaMethod& meth, py::handle& handle, const QMetaObject& cls)
0477 {
0478     if ( meth.access() != QMetaMethod::Public )
0479         return {};
0480     if ( meth.methodType() != QMetaMethod::Method && meth.methodType() != QMetaMethod::Slot )
0481         return {};
0482 
0483     if ( meth.parameterCount() > 9 )
0484     {
0485         log::LogStream("Python", "", log::Error) << "Too many arguments for method " << cls.className() << "::" << meth.name() << ": " << meth.parameterCount();
0486         return {};
0487     }
0488 
0489     PyMethodInfo pymeth = type_dispatch_maybe_void<RegisterMethod, PyMethodInfo>(meth.returnType(), meth, handle);
0490     if ( !pymeth.name )
0491         log::LogStream("Python", "", log::Error) << "Invalid method" << cls.className() << "::" << meth.name() << "return type" << meth.returnType() << meth.typeName();
0492     return pymeth;
0493 
0494 }
0495 
0496 template<int i>
0497 bool qvariant_type_caster_load_impl(QVariant& into, const pybind11::handle& src)
0498 {
0499     auto caster = pybind11::detail::make_caster<meta_2_cpp<i>>();
0500     if ( caster.load(src, false) )
0501     {
0502         into = QVariant::fromValue(pybind11::detail::cast_op<meta_2_cpp<i>>(caster));
0503         return true;
0504     }
0505     return false;
0506 }
0507 
0508 template<>
0509 bool qvariant_type_caster_load_impl<QMetaType::QVariant>(QVariant&, const pybind11::handle&) { return false; }
0510 
0511 template<int... i>
0512 bool qvariant_type_caster_load(QVariant& into, const pybind11::handle& src, std::integer_sequence<int, i...>)
0513 {
0514     return (qvariant_type_caster_load_impl<i>(into, src)||...);
0515 }
0516 
0517 template<int i>
0518 bool qvariant_type_caster_cast_impl(
0519     pybind11::handle& into, const QVariant& src,
0520     pybind11::return_value_policy policy, const pybind11::handle& parent)
0521 {
0522     if ( src.userType() == i )
0523     {
0524         into = pybind11::detail::make_caster<meta_2_cpp<i>>::cast(src.value<meta_2_cpp<i>>(), policy, parent);
0525         return true;
0526     }
0527     return false;
0528 }
0529 
0530 template<>
0531 bool qvariant_type_caster_cast_impl<QMetaType::QVariant>(
0532     pybind11::handle&, const QVariant&, pybind11::return_value_policy, const pybind11::handle&)
0533 {
0534     return false;
0535 }
0536 
0537 
0538 template<int... i>
0539 bool qvariant_type_caster_cast(
0540     pybind11::handle& into,
0541     const QVariant& src,
0542     pybind11::return_value_policy policy,
0543     const pybind11::handle& parent,
0544     std::integer_sequence<int, i...>
0545 )
0546 {
0547     return (qvariant_type_caster_cast_impl<i>(into, src, policy, parent)||...);
0548 }
0549 
0550 } // namespace app::scripting::python
0551 
0552 using namespace app::scripting::python;
0553 
0554 
0555 
0556 
0557 std::map<int, pybind11::detail::type_caster<QVariant>::CustomConverter> pybind11::detail::type_caster<QVariant>::custom_converters;
0558 
0559 bool pybind11::detail::type_caster<QVariant>::load(handle src, bool)
0560 {
0561     if ( src.ptr() == Py_None )
0562     {
0563         value = QVariant();
0564         return true;
0565     }
0566 
0567     if ( qvariant_type_caster_load(value, src, supported_types()) )
0568         return true;
0569 
0570     for ( const auto& p : custom_converters )
0571         if ( p.second.load(src, value) )
0572             return true;
0573     return false;
0574 }
0575 
0576 pybind11::handle pybind11::detail::type_caster<QVariant>::cast(QVariant src, return_value_policy policy, handle parent)
0577 {
0578     if ( src.isNull() )
0579         return pybind11::none();
0580 
0581     policy = py::return_value_policy::automatic_reference;
0582 
0583     int meta_type = src.userType();
0584     if ( meta_type >= QMetaType::User )
0585     {
0586         if ( QMetaType(meta_type).flags() & QMetaType::IsEnumeration )
0587             return pybind11::detail::make_caster<int>::cast(src.value<int>(), policy, parent);
0588         else if ( meta_type == qMetaTypeId<QGradientStops>() )
0589             return pybind11::detail::make_caster<QGradientStops>::cast(src.value<QGradientStops>(), policy, parent);
0590 
0591         auto it = custom_converters.find(meta_type);
0592         if ( it != custom_converters.end() )
0593             return it->second.cast(src, policy, parent);
0594         return pybind11::detail::make_caster<QObject*>::cast(src.value<QObject*>(), policy, parent);
0595     }
0596 
0597     pybind11::handle ret;
0598     qvariant_type_caster_cast(ret, src, policy, parent, supported_types());
0599     return ret;
0600 }
0601 
0602 
0603 bool pybind11::detail::type_caster<QUuid>::load(handle src, bool ic)
0604 {
0605     if ( isinstance(src, pybind11::module_::import("uuid").attr("UUID")) )
0606         src = py::str(src);
0607     type_caster<QString> stdc;
0608     if ( stdc.load(src, ic) )
0609     {
0610         value = QUuid::fromString((const QString &)stdc);
0611         return true;
0612     }
0613     return false;
0614 }
0615 
0616 pybind11::handle pybind11::detail::type_caster<QUuid>::cast(QUuid src, return_value_policy policy, handle parent)
0617 {
0618     auto str = type_caster<QString>::cast(src.toString(), policy, parent);
0619     return pybind11::module_::import("uuid").attr("UUID")(str).release();
0620 }
0621 
0622 
0623 
0624 bool pybind11::detail::type_caster<QImage>::load(handle src, bool)
0625 {
0626     if ( !isinstance(src, pybind11::module_::import("PIL.Image").attr("Image")) )
0627         return false;
0628 
0629     py::object obj = py::reinterpret_borrow<py::object>(src);
0630     std::string mode = obj.attr("mode").cast<std::string>();
0631     QImage::Format format;
0632     if ( mode == "RGBA" )
0633     {
0634         format = QImage::Format_RGBA8888;
0635     }
0636     else if ( mode == "RGB" )
0637     {
0638         format = QImage::Format_RGB888;
0639     }
0640     else if ( mode == "RGBa" )
0641     {
0642         format = QImage::Format_RGBA8888_Premultiplied;
0643     }
0644     else if ( mode == "RGBX" )
0645     {
0646         format = QImage::Format_RGBX8888;
0647     }
0648     else
0649     {
0650         format = QImage::Format_RGBA8888;
0651         obj = obj.attr("convert")("RGBA");
0652     }
0653 
0654     std::string data = obj.attr("tobytes")().cast<std::string>();
0655 
0656     int width = obj.attr("width").cast<int>();
0657     int height = obj.attr("height").cast<int>();
0658     value = QImage(width, height, format);
0659     if ( data.size() != std::size_t(value.sizeInBytes()) )
0660         return false;
0661     std::memcpy(value.bits(), data.data(), data.size());
0662     return true;
0663 }
0664 
0665 pybind11::handle pybind11::detail::type_caster<QImage>::cast(QImage src, return_value_policy, handle)
0666 {
0667     auto mod = pybind11::module_::import("PIL.Image");
0668     auto frombytes = mod.attr("frombytes");
0669 
0670     const char* mode;
0671 
0672     switch ( src.format() )
0673     {
0674         case QImage::Format_Invalid:
0675             return mod.attr("Image")().release();
0676         case QImage::Format_RGB888:
0677             mode = "RGB";
0678             break;
0679         case QImage::Format_RGBA8888:
0680             mode = "RGBA";
0681             break;
0682         case QImage::Format_RGBA8888_Premultiplied:
0683             mode = "RGBa";
0684             break;
0685         case QImage::Format_RGBX8888:
0686             mode = "RGBX";
0687             break;
0688         default:
0689             mode = "RGBA";
0690             src = src.convertToFormat(QImage::Format_RGBA8888);
0691             break;
0692     }
0693 
0694     py::tuple size(2);
0695     size[0] = py::int_(src.width());
0696     size[1] = py::int_(src.height());
0697 
0698     auto image = frombytes(
0699         mode,
0700         size,
0701         py::bytes((const char*)src.bits(), src.sizeInBytes())
0702     );
0703 
0704     return image.release();
0705 }