[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