Photo d'un smartphone Android dans les widgets Qt

Si vous n'avez pas le temps de lire ou si vous connaissez les informations, le code final pour obtenir une image en taille réelle à partir d'un appareil photo de smartphone Android se trouve à la fin de l'article.





description du problème

Si vous écrivez une application multiplateforme, vous pouvez utiliser la classe QCamera pour obtenir une image d'une caméra PC , un exemple pour lequel est décrit dans la documentation Qt.





Selon l'exemple ci-dessus, nous ajoutons au fichier .pro





QT += multimedia multimediawidgets







Ensuite, nous créons un widget dans notre programme qui affiche une image d'une webcam et l'enregistre dans un QPixmap ou QImage pour une utilisation future.





Lorsque la tâche se présente de faire de même sur Android, il s'avère que les widgets multimédias ne sont pas pris en charge par ce système d'exploitation et que l'appareil photo prendra et enregistrera des photos, mais ce qu'il affiche pour le moment sera un mystère, car QCameraViewfinder utilise des widgets multimédias et ne le fait pas. afficher sur rien Android. Une recherche plus poussée d'une solution au problème conduit à deux solutions:





  1. utilisez QML et écrivez votre propre élément Qt Quick qui exécute cette fonction, puis ancrez-le avec le reste de l'application dans Qt Widgets , C ++;





  2. utilisez l'application smartphone Android par défaut pour recevoir une photo, puis traitez-la dans votre application.





Considérez la première option

Si vous êtes un programmeur C ++ de Qt Widgets , alors l'approfondissement épisodique suivant dans QML vous prendra du temps, ajoutez à ce temps pour écrire un élément Qt Quick , ancrez cet élément avec du code C ++, déboguez le code écrit. Si vous n'êtes pas un professionnel de QML, cela s'avère long et difficile.





Considérez la deuxième option

Android- -, , , Java- (JNI — Java Native Interface) ++ QtAndroid. . , , , Android .





, Android- .





Android , . , GitHub , . , , FileUriExposedException , .





, , , Java- — .





.pro .





Android.





android {
    QT       +=androidextras
}
      
      



, QAndroidActivityResultReceiver. , , Qt, QObject.





(.h) :





#ifndef CAMSHOT_H
#define CAMSHOT_H
#include <QObject>
#include <QString>
#include <cstring>
#include <QImage>
#include <QDebug>
#include <QtAndroid>
#include <QAndroidActivityResultReceiver>
#include <QAndroidParcel>
class CamShot : public QObject, public QAndroidActivityResultReceiver
{
    Q_OBJECT
public:
    CamShot(QObject *parent = nullptr):QObject(parent),QAndroidActivityResultReceiver(){}
    
    static const int RESULT_OK = -1; 
    static const int REQUEST_IMAGE_CAPTURE = 1;
    static const int REQUEST_TAKE_PHOTO = REQUEST_IMAGE_CAPTURE;
    void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)  override;
    static QImage camThumbnailToQImage(const QAndroidJniObject &data);
public slots:
    void aMakeShot();
signals:
    void createNew(const QImage &img);
};

#endif // CAMSHOT_H
      
      



(.cpp) :





QImage CamShot::camThumbnailToQImage(const QAndroidJniObject &data){
    QAndroidJniObject bundle = data.callObjectMethod("getExtras","()Landroid/os/Bundle;");
    qDebug()<<"bundle.isValid() "<<bundle.isValid()<<bundle.toString();
    QAndroidJniObject bundleKey = QAndroidJniObject::fromString("data");
    const QAndroidJniObject aBitmap (data.callObjectMethod("getParcelableExtra", "(Ljava/lang/String;)Landroid/os/Parcelable;", bundleKey.object<jstring>()));
    qDebug()<<"aBitmap.isValid() "<<aBitmap.isValid()<<aBitmap.toString();
    jint aBitmapWidth = aBitmap.callMethod<jint>("getWidth");
    jint aBitmapHeight = aBitmap.callMethod<jint>("getHeight");
    QAndroidJniEnvironment env;
    const int32_t aBitmapPixelsCount = aBitmapWidth * aBitmapHeight;
    jintArray pixels = env->NewIntArray(aBitmapPixelsCount);
    jint aBitmapOffset = 0;
    jint aBitmapStride = aBitmapWidth;
    jint aBitmapX = 0;
    jint aBitmapY = 0;
    aBitmap.callMethod<void>("getPixels","([IIIIIII)V", pixels, aBitmapOffset, aBitmapStride, aBitmapX, aBitmapY, aBitmapWidth, aBitmapHeight);
    jint *pPixels = env->GetIntArrayElements(pixels, nullptr);
    QImage img(aBitmapWidth, aBitmapHeight, QImage::Format_ARGB32);
    int lineSzB = aBitmapWidth * sizeof(jint);
    for (int i = 0; i < aBitmapHeight; ++i){
        uchar *pDst = img.scanLine(i);
        const uchar *pSrc = reinterpret_cast<const uchar*>(pPixels + aBitmapWidth * i + aBitmapWidth);
        memcpy(pDst, pSrc, lineSzB);
    }
    env->DeleteLocalRef(pixels); //env->ReleaseIntArrayElements(pixels, pPixels, 0);       ,     ,  DeleteLocalRef.

    return img;
}
void CamShot::aMakeShot() {
    QAndroidJniObject action = QandroidJniObject::fromString("android.media.action.IMAGE_CAPTURE");
    //   Java- (  ),      (-   "/"),   "android/content/Intent", "java/lang/String".
    //   Java-,       "L"  ";"  ,  "Landroid/content/Intent ;", "Ljava/lang/String;".
    //    ,      ,  "V" (void)  "[IIIIIII" ( jint,  6 jint  )
    //,   :
    QAndroidJniObject intent=QAndroidJniObject("android/content/Intent","(Ljava/lang/String;)V", action.object<jstring>());
    QtAndroid::startActivity(intent, REQUEST_IMAGE_CAPTURE, this);
}
void CamShot::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data){
    if ( receiverRequestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK )
    {
        const QImage thumbnail (camThumbnailToQImage(data));
        if (!thumbnail.isNull())
            emit createNew(thumbnail);
    }
}
      
      



JNI-

  1. Java- ( Java-), (- "/"), "android/content/Intent", "java/lang/String";





  2. Java-, "L" ";" , "Landroid/content/Intent;", "Ljava/lang/String;";





  3. , ( ), "V" (void), "I" (jint) "[IIIIIII" ( jint, 6 jint );





  4. :







    C/C++





    JNI





    Java





    Signature





    uint8_t/unsigned char





    jboolean





    bool





    Z





    int8_t/char/signed char





    jbyte





    byte





    B





    uint16_t/unsigned short





    jchar





    char





    C





    int16_t/short





    jshort





    short





    S





    int32_t/int/(long)





    jint





    int





    I





    int64_t/(long)/long long





    jlong





    long





    J





    float





    jfloat





    float





    F





    double





    jdouble





    double





    D





    void











    void





    V





  5. :







    JNI





    Java





    Signature





    jbooleanArray





    bool[]





    [Z





    jbyteArray





    byte[]





    [B





    jcharArray





    char[]





    [C





    jshortArray





    short[]





    [S





    jintArray





    int[]





    [I





    jlongArray





    long[]





    [L





    jfloatArray





    float[]





    [F





    jdoubleArray





    double[]





    [D





    jarray





    type[]





    [Lfully/qualified/type/name;





    jarray





    String[]





    [Ljava/lang/String;







    , JNI- QAndroidJniEnvironment, : NewIntArray, GetIntArrayElements, DeleteLocalRef GetArrayLength,GetObjectArrayElement, SetObjectArrayElement, ..





(pdf) Practical Qt on Android JNI — qtcon.





class CamShot :





  1. , Android ( Java-);





  2. void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override;



    Java- Intent ;







  3. static QImage camThumbnailToQImage(const QAndroidJniObject &data);





    Java- Intent Java- Bitmap, (32- ) QImage;







  4. void aMakeShot();





    ;







  5. void createNew(const QImage &img);





    .





void aMakeShot() Java- Intent , , — . (Intent) (Activity).





- . , handleActivityResult, : . , camThumbnailToQImage QImage Java- Bitmap Qt.







static QImage camThumbnailToQImage(const QAndroidJniObject &data) override;







Java- Intent Java- Bundle, Intent:

Bundle getExtras()



Bundle <->:<>. Android , . "data".





Java- Bitmap , Intent:

T getParcelableExtra (String name)



C Bitmap QImage , . . Bitmap . ( ) QImage .





Bitmap QImage Bitmap:

void getPixels (int[] pixels, int offset, int stride, int x, int y, int width, int height)





jintArray pixels = env->NewIntArray(aBitmapPixelsCount);





, , , C++ :

jint *pPixels = env->GetIntArrayElements(pixels, nullptr);





Qimage. ,

env->DeleteLocalRef(pixels);





QImage.





. .





FileProvider, Uri . , Android, , :





  1. androidx.core.content.FileProvider;





  2. android.support.v4.content.FileProvider.





— , Qt, QtCreator:





()→ «» → «» → «»→ «Android»→ «SDK Manager»→ «» → «Extras»→ «Android Support Repository» - «» .





Android

QtCreator «». « »→ «». «Build Android APK» → «Create Templates». « Gradle Android», «»:





«android», .





Android

- Android, .pro android: :





android {
    QT       +=androidextras
}
# … 
DISTFILES += \
android:    android/AndroidManifest.xml \
android:    android/build.gradle \
android:    android/gradle/wrapper/gradle-wrapper.jar \
android:    android/gradle/wrapper/gradle-wrapper.properties \
android:    android/gradlew \
android:    android/gradlew.bat \
android:    android/res/values/libs.xml \
    todo.txt
      
      



AndroidManifest.xml

«AndroidManifest.xml» android/AndroidManifest.xml,





</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices ->
      
      



:





<provider android:name="android.support.v4.content.FileProvider" android:authorities="org.qtproject.example.qsketch.fileprovider" android:grantUriPermissions="true" android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/>
</provider>
      
      



, - .





, «AndroidManifest.xml» «res» «values», «xml», «file_paths.xml» (… /abin/AndroidManifest.xml) (… /abin/res/xml/file_paths.xml). :





<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="shared" path="shared/" />
</paths>
      
      



shared/





, FileProvider

android/build.gradle, dependencies :





compile'com.android.support:support-v4:25.3.1'
      
      



:





dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
compile'com.android.support:support-v4:25.3.1'
}
      
      



, Android Support Repository.





Sharing Files on Android or iOS from or with your Qt App - Part 4 .





(.h) :





#ifndef CAMSHOT_H
#define CAMSHOT_H
#include <QObject>
#include <QImage>
#include <QString>
#include <QDebug>

#include <QtAndroid>
#include <QAndroidActivityResultReceiver>
#include <QAndroidParcel>

#include "auxfunc.h"

class CamShot : public QObject, public QAndroidActivityResultReceiver
{
    Q_OBJECT
public:
    static const int RESULT_OK = -1;
    static const int REQUEST_IMAGE_CAPTURE = 1;
    static const int REQUEST_TAKE_PHOTO = REQUEST_IMAGE_CAPTURE;
    enum ImgOrientation {ORIENTATION_UNDEFINED = 0, ORIENTATION_NORMAL = 1, ORIENTATION_FLIP_HORIZONTAL = 2, ORIENTATION_ROTATE_180 = 3, ORIENTATION_FLIP_VERTICAL = 4, ORIENTATION_TRANSPOSE = 5,
                       ORIENTATION_ROTATE_90 = 6, ORIENTATION_TRANSVERSE = 7, ORIENTATION_ROTATE_270 = 8};
    void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override;
    static QImage aBitmapToQImage(const QAndroidJniObject &aBitmap);
    static QImage camThumbnailToQImage(const QAndroidJniObject &data);
    ImgOrientation needRotateAtRightAngle();
    QImage camImageToQImage();
    static void applyOrientation(QImage &img, const ImgOrientation &orientation);


    explicit CamShot(QObject *parent = nullptr):QObject(parent),QAndroidActivityResultReceiver(){}
    ~CamShot();
private:    
    QAndroidJniObject tempImgURI;
    QAndroidJniObject tempImgFile;
    QAndroidJniObject tempImgAbsPath;
    bool _thumbnailNotFullScaleRequested;
public slots:
    void aMakeShot(const bool &thumbnailNotFullScale = false);
signals:
    void createNew(const QImage &img);
};
#endif // CAMSHOT_H
      
      



(.cpp) :





QImage CamShot::aBitmapToQImage(const QAndroidJniObject &aBitmap){
    if (!aBitmap.isValid())
        return QImage();
    jint aBitmapWidth = aBitmap.callMethod<jint>("getWidth");
    jint aBitmapHeight = aBitmap.callMethod<jint>("getHeight");
    QAndroidJniEnvironment env;
    const int32_t aBitmapPixelsCount = aBitmapWidth * aBitmapHeight;
    jintArray pixels = env->NewIntArray(aBitmapPixelsCount);
    jint aBitmapOffset = 0;
    jint aBitmapStride = aBitmapWidth;
    jint aBitmapX = 0;
    jint aBitmapY = 0;
    aBitmap.callMethod<void>("getPixels","([IIIIIII)V", pixels, aBitmapOffset, aBitmapStride, aBitmapX, aBitmapY, aBitmapWidth, aBitmapHeight);
    jint *pPixels = env->GetIntArrayElements(pixels, nullptr);
    QImage img(aBitmapWidth, aBitmapHeight, QImage::Format_ARGB32);
    int lineSzB = aBitmapWidth * sizeof(jint);
    for (int i = 0; i < aBitmapHeight; ++i){
        uchar *pDst = img.scanLine(i);
        const uchar *pSrc = reinterpret_cast<const uchar*>(pPixels + aBitmapWidth * i + aBitmapWidth);
        memcpy(pDst, pSrc, lineSzB);
    }
    env->DeleteLocalRef(pixels); //env->ReleaseIntArrayElements(pixels, pPixels, 0);       ,     ,  DeleteLocalRef.
    return img;
}
QImage CamShot::camThumbnailToQImage(const QAndroidJniObject &data){
    //  
    QAndroidJniObject bundle = data.callObjectMethod("getExtras","()Landroid/os/Bundle;");
    qDebug()<<"bundle.isValid() "<<bundle.isValid()<<bundle.toString();
    //   jstring ( Java)   "data" -        <, >  -   Bitmap (Java)
    QAndroidJniObject bundleKey = QAndroidJniObject::fromString("data");
    //   "data"  :     Bitmap
    const QAndroidJniObject aBitmap (data.callObjectMethod("getParcelableExtra", "(Ljava/lang/String;)Landroid/os/Parcelable;", bundleKey.object<jstring>()));
    qDebug()<<"aBitmap.isValid() "<<aBitmap.isValid()<<aBitmap.toString();
    return aBitmapToQImage(aBitmap);
}
QImage CamShot::camImageToQImage(){
    QAndroidJniObject bitmap = QAndroidJniObject::callStaticObjectMethod("android/graphics/BitmapFactory","decodeFile","(Ljava/lang/String;)Landroid/graphics/Bitmap;",tempImgAbsPath.object<jobject>());
    qDebug()<<"bitmap.isValid() "<<bitmap.isValid()<<bitmap.toString();
    QImage img = aBitmapToQImage(bitmap);
    // 
    if (tempImgFile.isValid())
        tempImgFile.callMethod<jboolean>("delete");
    return img;
}
CamShot::ImgOrientation CamShot::needRotateAtRightAngle(){
    //  
    QAndroidJniObject exifInterface = QAndroidJniObject("android/media/ExifInterface","(Ljava/lang/String;)V",
                                                     tempImgAbsPath.object<jstring>());
    qDebug() << __FUNCTION__ << "exifInterface.isValid()=" << exifInterface.isValid();
    QAndroidJniObject TAG_ORIENTATION = QAndroidJniObject::getStaticObjectField<jstring>("android/media/ExifInterface", "TAG_ORIENTATION");
    qDebug() << __FUNCTION__ << "TAG_ORIENTATION.isValid()=" << TAG_ORIENTATION.isValid()<<TAG_ORIENTATION.toString();
    const jint orientation = exifInterface.callMethod<jint>("getAttributeInt","(Ljava/lang/String;I)I",TAG_ORIENTATION.object<jstring>(),static_cast<jint>(ORIENTATION_UNDEFINED));
    return static_cast<ImgOrientation>(orientation);
}
void CamShot::applyOrientation(QImage &img, const ImgOrientation &orientation){
    switch (orientation){
    case ORIENTATION_UNDEFINED:
    case ORIENTATION_NORMAL:
        break;
    case ORIENTATION_FLIP_HORIZONTAL:{
        img = img.mirrored(true, false);
        break;
    }
    case ORIENTATION_ROTATE_180:
        Aux::rotateImgCW180(img);
        break;
    case ORIENTATION_FLIP_VERTICAL:{
        img = img.mirrored(false, true);
        break;
    }
    case ORIENTATION_TRANSPOSE:{
        img = img.mirrored(true, false);
        Aux::rotateImgCW270(img);
        break;
    }
    case ORIENTATION_ROTATE_90:
        Aux::rotateImgCW90(img);
        break;
    case ORIENTATION_TRANSVERSE:{
        img = img.mirrored(true, false);
        Aux::rotateImgCW90(img);
        break;
    }
        break;
    case ORIENTATION_ROTATE_270:
        Aux::rotateImgCW270(img);
        break;
    }
}
void CamShot::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data){
    if ( receiverRequestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK )
    {
        if (_thumbnailNotFullScaleRequested){
            const QImage thumbnail (camThumbnailToQImage(data));
            if (!thumbnail.isNull())
                emit createNew(thumbnail);
            return;
        }
        const ImgOrientation orientation = needRotateAtRightAngle();
        QImage image (camImageToQImage());
        if (!image.isNull()){
            applyOrientation(image, orientation);
            emit createNew(image);
        }
    }
}
void CamShot::aMakeShot(const bool &thumbnailNotFullScale) {
    QAndroidJniObject action = QAndroidJniObject::fromString("android.media.action.IMAGE_CAPTURE");
    //  
    QAndroidJniObject intent=QAndroidJniObject("android/content/Intent","(Ljava/lang/String;)V",
                                                 action.object<jstring>());
    qDebug() << __FUNCTION__ << "intent.isValid()=" << intent.isValid();
    _thumbnailNotFullScaleRequested = thumbnailNotFullScale;
    if (thumbnailNotFullScale) {
        //  
        QtAndroid::startActivity(intent, REQUEST_IMAGE_CAPTURE, this);
        return;
    }
    //    
    QAndroidJniObject context = QtAndroid::androidContext();
    QString contextStr (context.toString());
    qDebug() <<"Context: "<<contextStr;
    //    
    QAndroidJniObject extDir = context.callObjectMethod("getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;", NULL);
    qDebug() << __FUNCTION__ << "extDir.isValid()=" << extDir.isValid()<<extDir.toString();
    //          
    QAndroidJniObject extDirAbsPath = extDir.callObjectMethod("getAbsolutePath","()Ljava/lang/String;");
    //          . . /res/xml/file_paths.xml
    extDirAbsPath = QAndroidJniObject::fromString(extDirAbsPath.toString() + "/shared");
    const QString extDirAbsPathStr = extDirAbsPath.toString();
    qDebug() << __FUNCTION__ << "extDirAbsPath.isValid()=" << extDirAbsPath.isValid()<<extDirAbsPathStr;
    //      
    QAndroidJniObject sharedFolder=QAndroidJniObject("java.io.File","(Ljava/lang/String;)V",
                                                      extDirAbsPath.object<jstring>());
    qDebug() << __FUNCTION__ << "sharedFolder.isValid()=" << sharedFolder.isValid()<<sharedFolder.toString();
    const jboolean sharedFolderCreated = sharedFolder.callMethod<jboolean>("mkdirs");
    Q_UNUSED(sharedFolderCreated);
    //       ,        
    //    
    QAndroidJniObject suggestedFilePath = QAndroidJniObject::fromString(extDirAbsPathStr+"/"+"_tmp.jpg");
    qDebug() << __FUNCTION__ << "suggestedFilePath.isValid()=" << suggestedFilePath.isValid()<<suggestedFilePath.toString();
    //   
    //  
    QAndroidJniObject tempImgFile=QAndroidJniObject("java.io.File","(Ljava/lang/String;)V",
                                                 suggestedFilePath.object<jstring>());
    qDebug() << __FUNCTION__ << "fileExistsCheck.isValid()=" << tempImgFile.isValid()<<tempImgFile.toString();
    // ,   
    if (tempImgFile.isValid()){
        const jboolean deleted = tempImgFile.callMethod<jboolean>("delete");
        Q_UNUSED(deleted);
    }
    //          
    const jboolean fileCreated = tempImgFile.callMethod<jboolean>("createNewFile");
    Q_UNUSED(fileCreated);
    //       
    tempImgAbsPath = tempImgFile.callObjectMethod("getAbsolutePath","()Ljava/lang/String;");
    qDebug() << __FUNCTION__ << "tempImgAbsPath.isValid()=" << tempImgAbsPath.isValid()<<tempImgAbsPath.toString();
    // authority  fileprovider
    const QString contextFileProviderStr ("org.qtproject.example.qsketch.fileprovider");
    const char androidFileProvider  [] = "android/support/v4/content/FileProvider";
    //const char androidxFileProvider [] = "androidx/core/content/FileProvider"; -   Qt
    /*QAndroidJniObject*/ tempImgURI = QAndroidJniObject::callStaticObjectMethod(androidFileProvider, "getUriForFile", "(Landroid/content/Context;Ljava/lang/String;Ljava/io/File;)Landroid/net/Uri;",
                                                                             context.object<jobject>(), QAndroidJniObject::fromString(contextFileProviderStr).object<jstring>(), tempImgFile.object<jobject>());
    qDebug() << __FUNCTION__ << "tempImgURI.isValid()=" << tempImgURI.isValid()<<tempImgURI.toString();
    //   MediaStore.EXTRA_OUTPUT
    QAndroidJniObject MediaStore__EXTRA_OUTPUT
        = QAndroidJniObject::getStaticObjectField("android/provider/MediaStore", "EXTRA_OUTPUT", "Ljava/lang/String;");
    qDebug() << "MediaStore__EXTRA_OUTPUT.isValid()=" << MediaStore__EXTRA_OUTPUT.isValid();
    // URI         
    intent.callObjectMethod("putExtra","(Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;",MediaStore__EXTRA_OUTPUT.object<jstring>(), tempImgURI.object<jobject>());
    qDebug() << __FUNCTION__ << "intent.isValid()=" << intent.isValid();
    QtAndroid::startActivity(intent, REQUEST_IMAGE_CAPTURE, this);
}
      
      



- Aux .





(.h) Aux :





#ifndef AUXFUNC_H
#define AUXFUNC_H

#include <QImage>
#include <QColor>
#include <QPainter>
#include <QMatrix>
#include <QSize>
#include <QPoint>

class Aux
{
public:
    static void resizeCenteredImg(QImage *image, const QSize &newSize, const QColor bgColor);
    static void rotateImg(QImage &img, qreal degrees);
    static void rotateImgCW90(QImage &img);
    static void rotateImgCW180(QImage &img);
    static void rotateImgCW270(QImage &img);
};


#endif // AUXFUNC_H
      
      



(.cpp) Aux :





void Aux::resizeCenteredImg(QImage *image, const QSize &newSize, const QColor bgColor){
    if (image->size() == newSize)
        return;
    const QSize szDiff = newSize - image->size();
    QImage newImage(newSize, QImage::Format_ARGB32);
    newImage.fill(bgColor);
    QPainter painter(&newImage);
    painter.drawImage(QPoint(szDiff.width()/2, szDiff.height()/2), *image);
    *image = newImage;
}
void Aux::rotateImg(QImage &img, qreal degrees){
    QPoint center = img.rect().center();
    QMatrix matrix;
    matrix.translate(center.x(), center.y());
    matrix.rotate(degrees);
    img = img.transformed(matrix, Qt::SmoothTransformation);
}
void Aux::rotateImgCW90(QImage &img){
    const int w = img.width();
    const int h = img.height();
    const int maxDim = std::max(w, h);
    resizeCenteredImg(&img, QSize(maxDim, maxDim), Qt::white);
    rotateImg(img, 90);
    resizeCenteredImg(&img, QSize(h, w), Qt::white);
}
void Aux::rotateImgCW180(QImage &img){
    rotateImg(img, 180);
}
void Aux::rotateImgCW270(QImage &img){
    const int w = img.width();
    const int h = img.height();
    const int maxDim = std::max(w, h);
    resizeCenteredImg(&img, QSize(maxDim, maxDim), Qt::white);
    rotateImg(img, 270);
    resizeCenteredImg(&img, QSize(h, w), Qt::white);
}
      
      



.





«thumbnailNotFullScale». , , , . JNI-.





Si la vignette est toujours correctement orientée, l'image en taille réelle est dirigée dans une direction et doit être tournée. Des informations sur les transformations requises peuvent être obtenues à partir des propriétés exif de l'image en utilisant ExifInterface . Dans les exemples Java trouvés sur Internet , la conversion vers l'orientation normale se fait en code Java, dans le cas de Qt il ne sert à rien de se torturer avec des appels JNI difficiles à déboguer et encombrants, et il est plus facile d'effectuer tous les transformations nécessaires dans Qt.








All Articles