[PATCH v2 5/8] qedit: Implement IMediaDet::GetBitmapBits.

Gabriel Ivăncescu gabrielopcode at gmail.com
Fri Oct 23 10:18:55 CDT 2020


Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
 dlls/qedit/mediadet.c       | 191 +++++++++++++++++++++++++++++++++++-
 dlls/qedit/tests/mediadet.c |  57 ++++++-----
 2 files changed, 222 insertions(+), 26 deletions(-)

diff --git a/dlls/qedit/mediadet.c b/dlls/qedit/mediadet.c
index 6a9b000..acf659b 100644
--- a/dlls/qedit/mediadet.c
+++ b/dlls/qedit/mediadet.c
@@ -292,6 +292,60 @@ done:
     return hr;
 }
 
+static void stretch_line(BYTE *dst, ULONG dst_width, BYTE *src, ULONG src_width)
+{
+    ULONG ratio, rem, drift, i = dst_width;
+
+    if (dst_width < src_width)
+    {
+        ratio = src_width / dst_width;
+        rem   = src_width % dst_width;
+        drift = 0;
+        while (i--)
+        {
+            dst[0] = src[0];
+            dst[1] = src[1];
+            dst[2] = src[2];
+            dst += 3;
+            src += ratio * 3;
+            if (drift < rem)
+            {
+                src += 3;
+                drift += dst_width;
+            }
+            drift -= rem;
+        }
+    }
+    else if (dst_width > src_width)
+    {
+        drift = dst_width - 1;
+        while (i--)
+        {
+            dst[0] = src[0];
+            dst[1] = src[1];
+            dst[2] = src[2];
+            dst += 3;
+            if (drift < src_width)
+            {
+                drift += dst_width;
+                src += 3;
+            }
+            drift -= src_width;
+        }
+    }
+    else
+    {
+        memcpy(dst, src, dst_width * 3);
+        dst += dst_width * 3;
+    }
+
+    /* Fill the stride padding with zeros */
+    i = (dst_width * 3) % 4;
+    if (i)
+        for (i = 4 - i; i--;)
+            *dst++ = 0;
+}
+
 /* MediaDet inner IUnknown */
 static HRESULT WINAPI MediaDet_inner_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
 {
@@ -695,10 +749,139 @@ static HRESULT WINAPI MediaDet_GetBitmapBits(IMediaDet* iface,
                                              LONG *pBufferSize, char *pBuffer,
                                              LONG Width, LONG Height)
 {
-    MediaDetImpl *This = impl_from_IMediaDet(iface);
-    FIXME("(%p)->(%f %p %p %d %d): not implemented!\n", This, StreamTime, pBufferSize, pBuffer,
-          Width, Height);
-    return E_NOTIMPL;
+    MediaDetImpl *detector = impl_from_IMediaDet(iface);
+    LONG ratio, rem, stride = (Width * 3 + 3) & ~3;
+    LONG src_x, src_y, src_stride, size, buf_size;
+    ULONG src_width, src_height, drift, i;
+    BITMAPINFOHEADER hdr = { 0 };
+    BYTE *buf, *dup, *dst, *src;
+    VIDEOINFOHEADER *info;
+    AM_MEDIA_TYPE mt;
+    HRESULT hr;
+
+    TRACE("(%p)->(%.16e %p %p %d %d)\n", detector, StreamTime, pBufferSize, pBuffer, Width, Height);
+
+    if (!pBuffer && !pBufferSize) return E_POINTER;
+    if (Width < 0 || Height < 0) return E_INVALIDARG;
+    if (!pBuffer)
+    {
+        *pBufferSize = sizeof(BITMAPINFOHEADER) + stride * Height;
+        return S_OK;
+    }
+    if (StreamTime < 0.0) return E_INVALIDARG;
+    if (pBufferSize)
+    {
+        if (*pBufferSize < 0)
+            return E_INVALIDARG;
+        if (*pBufferSize < sizeof(BITMAPINFOHEADER) + stride * Height)
+            return E_OUTOFMEMORY;
+    }
+
+    if (detector->grabber)
+        hr = seek_graph(detector, StreamTime);
+    else
+        hr = IMediaDet_EnterBitmapGrabMode(&detector->IMediaDet_iface, StreamTime);
+    if (FAILED(hr))
+        return hr;
+
+    hr = ISampleGrabber_GetConnectedMediaType(detector->grabber, &mt);
+    if (FAILED(hr)) return hr;
+
+    info = (VIDEOINFOHEADER*)mt.pbFormat;
+    if (!IsEqualGUID(&mt.majortype, &MEDIATYPE_Video) ||
+        !IsEqualGUID(&mt.subtype, &MEDIASUBTYPE_RGB24) ||
+        !IsEqualGUID(&mt.formattype, &FORMAT_VideoInfo) ||
+        mt.cbFormat != sizeof(VIDEOINFOHEADER) ||
+        info->bmiHeader.biBitCount != 24 ||
+        info->bmiHeader.biCompression != BI_RGB)
+    {
+        FreeMediaType(&mt);
+        return VFW_E_INVALID_MEDIA_TYPE;
+    }
+
+    src_x = src_y = 0;
+    src_width = info->bmiHeader.biWidth;
+    src_height = abs(info->bmiHeader.biHeight);
+    src_stride = (src_width * 3 + 3) & ~3;
+    buf_size = src_stride * src_height;
+    if (!IsRectEmpty(&info->rcTarget))
+    {
+        src_x = max(info->rcTarget.left, 0);
+        src_y = max(info->rcTarget.top, 0);
+        src_width = min(info->rcTarget.right - src_x, src_width);
+        src_height = min(info->rcTarget.bottom - src_y, src_height);
+    }
+    if (info->bmiHeader.biHeight < 0)
+    {
+        src_y += info->bmiHeader.biHeight + 1;
+        src_stride = -src_stride;
+    }
+    FreeMediaType(&mt);
+
+    if (!(buf = HeapAlloc(GetProcessHeap(), 0, buf_size)))
+        return E_OUTOFMEMORY;
+    size = buf_size;
+    hr = ISampleGrabber_GetCurrentBuffer(detector->grabber, &size, (LONG*)buf);
+    if (FAILED(hr)) goto err;
+    if (size != buf_size)
+    {
+        hr = E_UNEXPECTED;
+        goto err;
+    }
+
+    hdr.biSize = sizeof(BITMAPINFOHEADER);
+    hdr.biWidth = Width;
+    hdr.biHeight = Height;
+    hdr.biPlanes = 1;
+    hdr.biBitCount = 24;
+    hdr.biCompression = BI_RGB;
+    memcpy(pBuffer, &hdr, sizeof(BITMAPINFOHEADER));
+
+    /* Copy and potentially stretch the image (differently than StretchBlt) */
+    dst = (BYTE*)pBuffer + sizeof(BITMAPINFOHEADER);
+    src = buf + src_x * 3 + src_y * src_stride;
+    i = Height;
+    if (Height < src_height)
+    {
+        ratio = src_height / Height;
+        rem   = src_height % Height;
+        drift = 0;
+        while (i--)
+        {
+            stretch_line(dst, Width, src, src_width);
+            dst += stride;
+            src += ratio * src_stride;
+            if (drift < rem)
+            {
+                src += src_stride;
+                drift += Height;
+            }
+            drift -= rem;
+        }
+    }
+    else
+    {
+        drift = Height - 1;
+        while (i--)
+        {
+            stretch_line(dst, Width, src, src_width);
+            dup = dst;
+            dst += stride;
+            while (drift >= src_height && i--)
+            {
+                memcpy(dst, dup, stride);
+                dst += stride;
+                drift -= src_height;
+            }
+            drift += Height - src_height;
+            src += src_stride;
+        }
+    }
+
+    hr = S_OK;
+err:
+    HeapFree(GetProcessHeap(), 0, buf);
+    return hr;
 }
 
 static HRESULT WINAPI MediaDet_WriteBitmapBits(IMediaDet* iface,
diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c
index 8c5ef4b..9e88d0a 100644
--- a/dlls/qedit/tests/mediadet.c
+++ b/dlls/qedit/tests/mediadet.c
@@ -1488,10 +1488,23 @@ static void test_bitmap_grab_mode(void)
     ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
     size = sizeof(BITMAPINFOHEADER) + 640 * 480 * 3;
     hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480);
-    todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
+    ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
     hr = IMediaDet_GetSampleGrabber(detector, &sg);
     ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
 
+    /* Lines are rounded up to the bitmap stride */
+    hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, NULL, 640, 480);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    ok(size == sizeof(BITMAPINFOHEADER) + 640 * 480 * 3, "Got size %d.\n", size);
+    hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, NULL, 640, 0);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    ok(size == sizeof(BITMAPINFOHEADER), "Got size %d.\n", size);
+    hr = IMediaDet_GetBitmapBits(detector, -1.0, &size, NULL, 151, 131);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    ok(size == sizeof(BITMAPINFOHEADER) + ((151 * 3 + 3) & ~3) * 131, "Got size %d.\n", size);
+    hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, NULL, -59, -79);
+    ok(hr == E_INVALIDARG || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr);
+
     /* EnterBitmapGrabMode only works with Video major type */
     testfilter_init(&testfilter);
     testfilter.majortype = &MEDIATYPE_Audio;
@@ -1657,39 +1670,39 @@ static void test_bitmap_grab_mode(void)
 
     /* Despite what MSDN states, size must be properly supplied on newer Windows versions */
     hr = IMediaDet_GetBitmapBits(detector, 0.0, NULL, NULL, 640, 480);
-    todo_wine ok(hr == E_POINTER, "Got hr %#x.\n", hr);
+    ok(hr == E_POINTER, "Got hr %#x.\n", hr);
     size = -1;
     hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480);
-    todo_wine ok(hr == E_INVALIDARG || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr);
+    ok(hr == E_INVALIDARG || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr);
     size = 640 * 480 * 3;
     hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480);
-    todo_wine ok(hr == E_OUTOFMEMORY || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr);
+    ok(hr == E_OUTOFMEMORY || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr);
     ok(size == 640 * 480 * 3, "Got size %d.\n", size);
     size = sizeof(BITMAPINFOHEADER) + 640 * 480 * 3;
     hr = IMediaDet_GetBitmapBits(detector, -1.0, &size, buf, 640, 480);
-    todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
+    ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
     hr = IMediaDet_GetBitmapBits(detector, 2.5, &size, buf, 640, 480);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
-    todo_wine check_bitmap(buf, 640, 480, 2.5);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    check_bitmap(buf, 640, 480, 2.5);
 
     /* GetBitmapBits/WriteBitmapBits can scale the image */
     size = sizeof(BITMAPINFOHEADER) + 960 * 720 * 3;
     hr = IMediaDet_GetBitmapBits(detector, 1.5, &size, buf, 131, 151);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
     ok(size == sizeof(BITMAPINFOHEADER) + 960 * 720 * 3, "Got size %d.\n", size);
-    todo_wine check_bitmap(buf, 131, 151, 1.5);
+    check_bitmap(buf, 131, 151, 1.5);
     hr = IMediaDet_GetBitmapBits(detector, 4.0, NULL, buf, 503, 79);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
-    todo_wine check_bitmap(buf, 503, 79, 4.0);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    check_bitmap(buf, 503, 79, 4.0);
     hr = IMediaDet_GetBitmapBits(detector, 1.52, NULL, buf, 139, 487);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
-    todo_wine check_bitmap(buf, 139, 487, 1.52);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    check_bitmap(buf, 139, 487, 1.52);
     hr = IMediaDet_GetBitmapBits(detector, 2.12, NULL, buf, 640, 641);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
-    todo_wine check_bitmap(buf, 640, 641, 2.12);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    check_bitmap(buf, 640, 641, 2.12);
     hr = IMediaDet_GetBitmapBits(detector, 3.25, NULL, buf, 960, 720);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
-    todo_wine check_bitmap(buf, 960, 720, 3.25);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    check_bitmap(buf, 960, 720, 3.25);
 
     /* Changing filter resets bitmap grab mode */
     testfilter.bitmap_grab_mode = FALSE;
@@ -1704,7 +1717,7 @@ static void test_bitmap_grab_mode(void)
     /* GetBitmapBits enables it only if it retrieves the image */
     testfilter.bitmap_grab_mode = TRUE;
     hr = IMediaDet_GetBitmapBits(detector, 1.25, &size, NULL, 640, 480);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
     hr = IMediaDet_GetSampleGrabber(detector, &sg);
     ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
     hr = IMediaDet_get_OutputStreams(detector, &count);
@@ -1712,12 +1725,12 @@ static void test_bitmap_grab_mode(void)
     ok(count == 1, "Got %d streams.\n", count);
 
     hr = IMediaDet_GetBitmapBits(detector, 1.25, NULL, buf, 640, 480);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
     hr = IMediaDet_GetSampleGrabber(detector, &sg);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
-    if (SUCCEEDED(hr)) ISampleGrabber_Release(sg);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    ISampleGrabber_Release(sg);
     hr = IMediaDet_get_OutputStreams(detector, &count);
-    todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
+    ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
 
     ref = IMediaDet_Release(detector);
     ok(!ref, "Got outstanding refcount %d.\n", ref);
-- 
2.21.0




More information about the wine-devel mailing list