winapirustgdiwic

The result of loading image from stream using WIC is flipped upside down


I try to load images from the IStream and render them onto a dc. The code below works fine

pub fn to_GDI_bitmap(&mut self, ctx: *mut HDC__, hMEM_BUFF: &MEM_BUFF, (flip_hor, flip_vert): (bool, bool)) -> *mut HBITMAP__ {

        let hStream: HIStream = unsafe {
            SHCreateMemStream(hMEM_BUFF.get_buf_ptr(), hMEM_BUFF.get_size() as u32)
        };

        self.reset_converter();

        let decoder: *mut IWICBitmapDecoder =  unsafe {

            let mut ret: *mut IWICBitmapDecoder =  zeroed() ;

            let hr = self.factory
                .as_ref()
                .unwrap()
                .CreateDecoderFromStream(
                    hStream,
                    null_mut(),
                    WICDecodeMetadataCacheOnDemand, &mut ret as *mut *mut IWICBitmapDecoder,
                );

            assert_eq!(hr, S_OK);

            ret
        };


        let frame = unsafe {
            let mut pFrame: *mut IWICBitmapFrameDecode = zeroed();
            let hr = decoder.as_ref()
                .unwrap().GetFrame(
                {
                    let mut idx: UINT = zeroed();
                    let hr = decoder.as_ref()
                        .unwrap()
                        .GetFrameCount(&mut idx as *mut UINT);

                    assert_eq!(hr, S_OK);

                    idx-1
                },
                &mut pFrame as *mut *mut IWICBitmapFrameDecode
            );

            assert_eq!(hr, S_OK);
            pFrame
        };


        let (width, height) = unsafe {
            let (mut width, mut height): (UINT, UINT) = (0, 0);
            frame.as_ref().unwrap().GetSize(&mut width as *mut UINT, &mut height as *mut UINT);
            (width, height)
        };

        let hr = unsafe {
            self.fmt_converter
            .as_ref()
            .unwrap()
            .Initialize(
                &**frame
                    .as_ref()
                    .unwrap() as *const IWICBitmapSource,
            &GUID_WICPixelFormat24bppBGR,
            WICBitmapDitherTypeNone,
            null_mut(),
            0.0 as c_double,
            WICBitmapPaletteTypeMedianCut
            )
        };



        assert_eq!(hr, S_OK);

        let flip = |thing: u32| {

            let mut pFlipRotator: *mut IWICBitmapFlipRotator = unsafe { zeroed() };

            let hr = unsafe {
                self.factory.as_ref().unwrap().CreateBitmapFlipRotator(
                    &mut pFlipRotator as *mut *mut IWICBitmapFlipRotator
                )
            };

            assert_eq!(hr, S_OK);
            assert!(!pFlipRotator.is_null());

            let hr = unsafe {
                pFlipRotator
                    .as_ref()
                    .unwrap()
                    .Initialize(
                        &**frame
                            .as_ref()
                            .unwrap() as *const IWICBitmapSource,
                        thing,

                    )
            };

            assert_eq!(hr, S_OK);
        };

        if flip_hor  {
            flip(0x00000008);
        }

        if flip_vert {
            flip(0x00000010);
        }

        let mut buff = unsafe {
            let mut buff: Vec<u8> = Vec::with_capacity(width as usize * height as usize * 3);
            buff.fill(0);

            let rect: WICRect = WICRect {
                X: 0,
                Y: 0,
                Width: width as i32,
                Height: height as i32,
            };

            let hr = frame
                .as_ref()
                .unwrap()
                .CopyPixels(
                    &rect,
                    width * 3,
                    width * height * 3,
                    buff.as_mut_ptr()
                );

            assert_eq!(hr, S_OK);

            buff
        };


        unsafe {

            let bmp_iheader: BITMAPINFOHEADER = BITMAPINFOHEADER {
                biSize: size_of::<BITMAPINFOHEADER>() as u32,
                biWidth: width as i32,
                biHeight: height as i32,
                biPlanes: 1,
                biBitCount: 24,
                biCompression: BI_RGB,
                biSizeImage: 0,
                biXPelsPerMeter: GetDeviceCaps(ctx, HORZRES),
                biYPelsPerMeter: GetDeviceCaps(ctx, VERTRES),
                biClrUsed: 0,
                biClrImportant: 0
            };

            let bmp_i: BITMAPINFO = BITMAPINFO {
                bmiHeader: bmp_iheader.clone(),
                bmiColors: [RGBQUAD::default();1],
            };

            CreateDIBitmap(ctx,
                           &bmp_iheader,
                           CBM_INIT,
                           buff.as_ptr() as *const _ as *const c_void,
                           &bmp_i,
                           DIB_RGB_COLORS)
        }

    }
pub fn transfer(&self, srcBmp: *mut HBITMAP__ , destDC: *mut HDC__, (x, y): (c_int, c_int), size: SIZE) {


        let temp_dc = unsafe { CreateCompatibleDC(null_mut()) };

        unsafe {
            DeleteObject(SelectObject(temp_dc, srcBmp as HGDIOBJ));
        };

        let outcome: BOOL = unsafe {
            GdiTransparentBlt(destDC,
                              x.into(),
                              y.into(),
                              size.cx,
                              size.cy,
                              temp_dc,
                              0,
                              0,
                              size.cx,
                              size.cy,
                              RGB(255, 255, 255),
            )
        };

        assert!(outcome.is_positive());

        let outcome: BOOL = unsafe { DeleteDC(temp_dc) };
    }

except for the fact that the rendered image is flipped upside down like so: result of the code and the source bitmap As you see in the code I've tried to implement a system to flip the images to make a "dirty" fix which weirdly doesn't seem to result in any visible effect (that probably indicates that something is wrong - but If I could figure that out, I wouldn't be asking it here).

Another thing I tried to resolve the bug is to reverse the pixel buffer used to copy pixels from WICFrameDecode onto DIBitmap.


Solution

  • See BITMAPINFOHEADER structure reference:

    biHeight

    Specifies the height of the bitmap, in pixels.

    For uncompressed RGB bitmaps, if biHeight is positive, the bitmap is a bottom-up DIB with the origin at the lower left corner. If biHeight is negative, the bitmap is a top-down DIB with the origin at the upper left corner.

    So, you can change the sign of this field to flip an image vertically for display, without transforming the whole image in memory.