2

当我从随机访问流构建位图时,我希望它根据其 EXIF 方向自动旋转。让我们扩展以下代码片段:

ComPtr<IWICImagingFactory2> wicFactory2; // Details of factory creation omitted
ComPtr<IWICBitmapDecoder> wicBitmapDecoder;
wicFactory2->CreateDecoderFromStream(
    stream.Get(), // stream is a valid ComPtr<IStream>
    nullptr,
    WICDecodeMetadataCacheOnDemand,
    &wicBitmapDecoder
    );

ComPtr<IWICBitmapFrameDecode> wicFrameDecode;
wicBitmapDecoder->GetFrame(
    0,
    &wicFrameDecode
    );

ComPtr<IWICFormatConverter> wicFormatConverter;
wicFactory2->CreateFormatConverter(
    &wicFormatConverter
    );

wicFormatConverter->Initialize(
    wicFrameDecode.Get(),
    GUID_WICPixelFormat32bppPBGRA,
    WICBitmapDitherTypeNone,
    NULL,
    0.f,
    WICBitmapPaletteTypeMedianCut
    );

// <-- What code to insert here to respect EXIF orientation???

ComPtr<ID2D1Bitmap1> bitmap;
m_d2dContext->CreateBitmapFromWicBitmap(
    wicFormatConverter.Get(),
    &bitmap
    );

我正在寻找的行为类似于调用BitmapDecoder.GetPixelDataAsync并将 ExifOrientationMode 设置为 RespectExifOrientation。但是,我不能使用这个函数,因为位图应该作为 Direct2D 效果的输入,所以 CPU 不需要访问原始像素数据。

4

1 回答 1

0

我有同样的问题。当直接从 WIC 位图创建 Texture2D 而不像 GetPixelDataAsync 那样通过 byte[] 时,我无法想出让 WIC 或 DirectX 自动应用 EXIF 方向指令的方法。因此,我改为阅读 EXIF 属性 0x112 并按照Problem reading JPEG Metadata (Orientation)的答案之一中的建议确定要应用的方向。然后我在上面代码中的 IWICFormatConverter 顶部插入一个 IWICBitmapFlipRotator,并指定 BitmapTransformOptions 以匹配 EXIF 指令。

我正在使用 SharpDX 用 C# 编写代码,因此语法与上面的 C++ 略有不同。但翻译起来应该很简单:

获取元数据:

        using (MetadataQueryReader metadata = bitmapObjects.frame.MetadataQueryReader)
        {
            EXIForientation = (Nullable<UInt16>)metadata.TryGetMetadataByName("/app1/{ushort=0}/{ushort=274}"); //0x0112
        }

将 EXIF 方向信息转换为我需要的变量(复制/粘贴并从我链接到的另一篇文章中稍作修改。我在另一篇文章的代码中发现了一个错误 - 对于案例“5”):

            // If the EXIF orientation specifies that the image needs to be flipped or rotated before display, set that up to happen
        BitmapTransformOptions bitmapTransformationOptions = BitmapTransformOptions.Rotate0;
        bool flipHeightWidth = false;
        if (EXIForientation != null)
        {
            switch (EXIForientation)
            {
                case 1:
                    // No rotation required.
                    break;
                case 2:
                    bitmapTransformationOptions = BitmapTransformOptions.Rotate0 | BitmapTransformOptions.FlipHorizontal;
                    break;
                case 3:
                    bitmapTransformationOptions = BitmapTransformOptions.Rotate180;
                    break;
                case 4:
                    bitmapTransformationOptions = BitmapTransformOptions.Rotate180 | BitmapTransformOptions.FlipHorizontal;
                    break;
                case 5:
                    bitmapTransformationOptions = BitmapTransformOptions.Rotate270 | BitmapTransformOptions.FlipHorizontal;
                    flipHeightWidth = true;
                    break;
                case 6:
                    bitmapTransformationOptions = BitmapTransformOptions.Rotate90;
                    flipHeightWidth = true;
                    break;
                case 7:
                    bitmapTransformationOptions = BitmapTransformOptions.Rotate90 | BitmapTransformOptions.FlipHorizontal;
                    flipHeightWidth = true;
                    break;
                case 8:
                    bitmapTransformationOptions = BitmapTransformOptions.Rotate270;
                    flipHeightWidth = true;
                    break;
            }
            if (flipHeightWidth)
            {
                originalWidth = bitmapObjects.frame.Size.Height;
                originalHeight = bitmapObjects.frame.Size.Width;
            }
        }

我所做的位图创建(与您插入该管道的转换略有不同):

        bitmapObjects.scaler.Initialize(bitmapObjects.frame, (int)bitmapRectangle.Width, (int)bitmapRectangle.Height, SharpDX.WIC.BitmapInterpolationMode.Linear);
        bitmapObjects.pixelFormatConverter.Initialize(bitmapObjects.scaler, SharpDX.WIC.PixelFormat.Format32bppPBGRA);
        bitmapObjects.bitmapFlipRotator.Initialize(bitmapObjects.pixelFormatConverter, bitmapTransformationOptions);
        bitmapObjects.bitmap = SharpDX.Direct2D1.Bitmap1.FromWicBitmap(D2DObjectCollection.D2Dcontext2,
            bitmapObjects.bitmapFlipRotator,
            new BitmapProperties1(
                new SharpDX.Direct2D1.PixelFormat(
                    Format.B8G8R8A8_UNorm,
                    SharpDX.Direct2D1.AlphaMode.Ignore),
                96,
                96,
                BitmapOptions.Target));

顺便说一句,我将所有用于渲染位图的对象都保存在“bitmapObjects”集合中,这样我就可以按照它们初始化的相反顺序来处理它们。我发现如果我不这样做,我会从 Windows Codecs DLL 的深处得到非常罕见的奇怪的内存访问冲突实例。

编辑 自从实现了这个,我意识到这个解决方案需要注意一些事情:当缩放器首先进入管道时,它(在我的应用程序场景中)显着减小了当今大多数来自现代相机的大型 jpg 图像的解码位图的大小. 这对性能非常有用。但是,为了正确应用旋转和翻转(与我上面共享的代码段中的内容不同),我必须将其放在管道的开头,这会导致解码为图像文件中分辨率大小的位图。为了尽可能解决这个问题,我将代码更改为仅在特定文件实际上包含方向说明时才应用 bitmapFlipRotator。否则,不使用 bitmapFlipRotator。

于 2016-12-20T00:04:14.633 回答