Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development

Reply
Developer
dbigham
Posts: 532
Registered: ‎04-01-2009
My Device: Z10, PlayBook
Accepted Solution

ImageView should auto-sense orientation

Some feedback to the API people:

 

Shouldn't the ImageView read the EXIF data of a JPEG and auto-rotate the image? And if not by default, shouldn't it have a property such as "autoOrient" or "autoRotate" so that this occurs?

 

Having developers manually read EXIF data, manually rotate images, etc, seems like a waste of developer's time.

 

Follow-up question: If I do this manually myself and end up with a QImage, how can I tell an ImageView to use the QImage? Is that possible?

 

And a final follow-up question: What type is the "image" property of the ImageView?  It says it's a QVariant, but that's not very helpful.  Can we set that property, or is it read only?  Overall I think the documentation of the "image" property could use some improvement.

 

Thanks,

Daniel

Please use plain text.
Developer
DrShavargo
Posts: 133
Registered: ‎05-10-2013
My Device: Blackberry Z10

Re: ImageView should auto-sense orientation

I'll work backwards, since I'm more knowledgeble regarding your last question.

 

3) The QVariant class acts like a union for the most common Qt classes. Calling image() will return the image in a QVariant; you can call type() to find it's type and the appropriate to____() function. Now, the "image" property is of type Image, an image ressource. You cannot set it directly, since setImage(const Image &image) is a Q_SLOT; it can be done through the QUrl of the image's location:

 

ImageView* imageView1 = new ImageView();
imageView->setImage(Image("an_image.png"));

 2) QImage is a metadata type (or, a "hardware-independent image representation that allows direct access to the pixel data, and can be used as a paint device"); how would you theoretically end up with it? What you might end up with is an encoded image, which you can decode() to ImageData, and go from there.

 

1) I agree with you on this point, it would simplify things. There are samples of using EXIF lib in cascades.

For example: https://github.com/blackberry/Presentations/tree/master/2012-BlackBerryJam-Americas/JAM15/FlashPhoto...

----------------------------------------
Remember to mark the thread as solved at the post that solved your problem, and if you like a post, like it!
Please use plain text.
Developer
dbigham
Posts: 532
Registered: ‎04-01-2009
My Device: Z10, PlayBook

Re: ImageView should auto-sense orientation

Please use plain text.
Developer
dbigham
Posts: 532
Registered: ‎04-01-2009
My Device: Z10, PlayBook

Re: ImageView should auto-sense orientation

So yes, using the setRotationZ property does seem to rotate the image on the screen, so that's good.

 

But unfortunately, the "scalingMode" doesn't seem to work properly with:

 

scalingMethod: ScalingMethod.AspectFit

 

... what it does is apply the scalingMethod and THEN rotate, which results in a portrait photo being way too small to fill the screen. Ugh.

 

So I'm having to use absolute positioning to make the photo as large as I'm going to want it to be post-rotation, and then rotate it... and obviously doing absolute positioning stuff is a bit of a bear.

 

There's still a bug whereby the pottom fifth of my portrait photo is getting cropped post-rotation. Not sure what's going on there...

Please use plain text.
Developer
dbigham
Posts: 532
Registered: ‎04-01-2009
My Device: Z10, PlayBook

Re: ImageView should auto-sense orientation

[ Edited ]

Figured that out via:

http://supportforums.blackberry.com/t5/Cascades-Development/rotation-and-translation-cuts-the-image/...

 

... my container wasn't large enough. I change the container that has the ImageView to Fill the page, and that prevented cropping. Glad it was something simple.

 

Here's some very rough code incase it's useful to anyone else:

 

 

#include <libexif/exif-data.h>
#include <libexif/exif-log.h>
#include <libexif/exif-mem.h>
#include <libexif/exif-loader.h>

...

// Gets called when the root container is resized, so that we can track
// how large it is.
void PhotosModel::updateContainerSize(qreal width, qreal height)
{
    // Is this the initial call to updateContainerSize, or did
    // the size of the container change because of an orientation
    // change?
    bool initOrReInit = (containerWidth != (int)width);

    containerWidth = width;
    containerHeight = height;

    if (initOrReInit && imageLoadedFlag)
    {
        // When we tried to load this, we didn't yet know the width of the
        // screen. Now that we do, display the image.
        qDebug() << "Delayed image load";
        loadImage(photos.at(curPhoto).absoluteFilePath());
    }
}

void PhotosModel::loadImage(QString file)
{
    imageLoadedFlag = true;

    // If the screen hasn't loaded yet and we don't know how wide
    // the container is, then hold off until we do.
    if (containerWidth == 0)
    {
        qDebug() << "Can't display image yet";
        return;
    }

    int width = -1;
    int height = -1;
    bool xyFlip = false;

    ExifLoader* loader = exif_loader_new();
    exif_loader_write_file(loader, file.toStdString().c_str());
    ExifData* data = exif_loader_get_data(loader);

    imageView->setRotationZ(0);

    int desiredRotation = 0;

    if (data != NULL)
    {
        //exif_data_dump(data);

        // Treating this as int* seems to result in corruption / bad behavior.
        // I think orientation is supposed to be a 'short int', which I would have
        // thought would be 2 bytes, but short int * doesn't seem to be consistent
        // either. So far char* seems to work.
        char* orientationPtr = (char *) GetExifValue(data, EXIF_TAG_ORIENTATION);

        if (orientationPtr != NULL)
        {
            //int orientation = *orientationPtr;

            int orientation = orientationPtr[0];

            // 1 -> OK
            // 3 -> Rotate 180
            // 6 -> CC 90
            // 8 -> C 90

            qDebug() << "Orientation: " << orientation;

            if (orientation == 3)
            {
                desiredRotation = -180;
            }
            // NOTE: Also doing this for orientation == 0, because I have
            //       a photo with orientation 0 that seems to need this.
            //       Confused. Picasa seems to know that the image needs
            //       this rotation.
            else if (orientation == 6 || orientation == 0)
            {
                desiredRotation = 90;
                xyFlip = true;
            }
            else if (orientation == 8)
            {
                desiredRotation = -90;
                xyFlip = true;
            }
            else
            {
                desiredRotation = 0;
            }

            //qDebug() << "Width: " << width << ", Height: " << height;
        }

        long* widthPtr = (long *) GetExifValue(data, EXIF_TAG_PIXEL_X_DIMENSION);
        if (widthPtr != NULL)
        {
            width = (int)*widthPtr;
        }

        long* heightPtr = (long *) GetExifValue(data, EXIF_TAG_PIXEL_Y_DIMENSION);
        if (heightPtr != NULL)
        {
            height = (int)*heightPtr;
        }
    }

    if (width == -1 || height == -1)
    {
        // Trouble. We don't know how large the photo is.
        // For now we'll just show nothing. Will this ever happen? How can we
        // fail more gracefully?
        imageView->setVisible(false);
        ErrorHelpers::showDialog("Missing EXIF Data", "The EXIF data for this image doesn't indicate its width/height, and so it cannot be displayed.");
    }
    else
    {
        imageView->setVisible(true);
    }

    int virtualContainerWidth;
    int virtualContainerHeight;

    if (xyFlip)
    {
        qDebug() << "xyFlip";
        virtualContainerWidth = containerHeight;
        virtualContainerHeight = containerWidth;
    }
    else
    {
        virtualContainerWidth = containerWidth;
        virtualContainerHeight = containerHeight;
    }

    qDebug() << "virtualContainerWidth: " << virtualContainerWidth;
    qDebug() << "virtualContainerHeight: " << virtualContainerHeight;

    float imageAspect = (float)width / (float)height;

    qDebug() << "imageAspect: " << imageAspect;

    float screenAspect = (float)virtualContainerWidth / (float)virtualContainerHeight;

    qDebug() << "screenAspect: " << screenAspect;

    int imageScreenWidth;
    int imageScreenHeight;

    if (imageAspect > screenAspect)
    {
        // Image is wider than screen aspect wise, so X will be limiting dimension.
        if (width > virtualContainerWidth)
        {
            imageScreenWidth = virtualContainerWidth;
        }
        else
        {
            imageScreenWidth = width;
        }
        // Better to round here?
        imageScreenHeight = (int)((float)imageScreenWidth / imageAspect);

        qDebug() << "Limited by X";
        qDebug() << "imageScreenWidth: " << imageScreenWidth;
        qDebug() << "imageScreenHeight: " << imageScreenHeight;
    }
    else
    {
        // Image is taller than screen aspect wise, to Y will be limiting dimension.
        if (height > virtualContainerHeight)
        {
            imageScreenHeight = virtualContainerHeight;
        }
        else
        {
            imageScreenHeight = height;
        }
        // Better to round here?
        imageScreenWidth = (int)((float)imageScreenHeight * imageAspect);

        qDebug() << "Limited by Y";
        qDebug() << "imageScreenWidth: " << imageScreenWidth;
        qDebug() << "imageScreenHeight: " << imageScreenHeight;
    }

    int imageX;
    int imageY;
    imageX = (int)((float)containerWidth / 2.0f - (float)imageScreenWidth / 2.0f);
    imageY = (int)((float)containerHeight / 2.0f - (float)imageScreenHeight / 2.0f);

    qDebug() << "imageX: " << imageX;
    qDebug() << "imageY: " << imageY;

    imageView->setPreferredWidth(imageScreenWidth);
    imageView->setPreferredHeight(imageScreenHeight);

    AbsoluteLayoutProperties* layoutProperties = new AbsoluteLayoutProperties();

    layoutProperties->setPositionX(imageX);
    layoutProperties->setPositionY(imageY);

    imageView->setLayoutProperties(layoutProperties);

    qDebug() << "desiredRotation: " << desiredRotation;

    imageView->setRotationZ(desiredRotation);

    imageView->setImage(QUrl("file://" + file));
}

void* PhotosModel::GetExifValue(ExifData* data, ExifTag tag)
{
    for (int i = 0; i < EXIF_IFD_COUNT; i++)
    {
        ExifContent* content = data->ifd[i];
        ExifEntry* entry = exif_content_get_entry(content, tag);
        if (entry != NULL)
        {
            return entry->data;
        }
    }

    return NULL;
}

... and some related QML...

Page {    
    Container {
        id: rootContainer
        objectName: "rootContainer"
        background: Color.Black;
        layout: DockLayout {
        }
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
        
        attachedObjects: [
            // This handler is tracking the layout frame of the button.
            LayoutUpdateHandler {
                id: handler
                onLayoutFrameChanged: {
                    photosModel.updateContainerSize(layoutFrame.width, layoutFrame.height);
                }
            }
        ]

		Container {
            layout: AbsoluteLayout {}
            horizontalAlignment: HorizontalAlignment.Fill
            verticalAlignment: VerticalAlignment.Fill

	        ImageView {
	            id: imageView
	            objectName: "imageView"
	            
                scalingMethod: ScalingMethod.Fill
	            
	            //scalingMethod: ScalingMethod.AspectFit
	            
	            preferredWidth: 1
	            preferredHeight: 1
	            
                layoutProperties: AbsoluteLayoutProperties {
                    positionX: 1
                    positionY: 1
                }
	            
	            // Tried this, but the image is still animating into place.
	            loadEffect: ImageViewLoadEffect.None
	            
	            attachedObjects: [
	                ImplicitAnimationController {
	                    enabled: false
	                }
	            ]
	        }
	    }

This is a disappointing amount of nonsense to be caused by the ImageView not auto-sensing the EXIF orientation flag. Please enhance this BlackBerry.

Please use plain text.
New Developer
peterpan2
Posts: 6
Registered: ‎08-21-2009
My Device: Not Specified

Re: ImageView should auto-sense orientation

Totally agree. ImageView should sense orientation
Please use plain text.
Developer
slashkyle
Posts: 820
Registered: ‎10-16-2012
My Device: Red Z10

Re: ImageView should auto-sense orientation

The only likely way exif support will be added to an imageview will be if lots of people vote on this jira ticket

 

https://www.blackberry.com/jira/browse/BBTEN-2091

 

 

Until then, the code here shows how propery read exif data to rotate an image.  @ OP your current rotation method will not work on the Z30 or properly in OS 10.2.  This example will

 

http://supportforums.blackberry.com/t5/Native-Development/Inconsistency-between-Z10-and-Z30-exif-dat...

Please use plain text.