Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Note the stretching algorithm used here to verify that it's correct: please read the comments for the next patch in the series for more details. Short story is: Windows doesn't use gdi32 to do the scaling, as none of the APIs produced the correct scaling on Windows.
dlls/qedit/tests/mediadet.c | 210 +++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 1 deletion(-)
diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c index 412d574..98372b6 100644 --- a/dlls/qedit/tests/mediadet.c +++ b/dlls/qedit/tests/mediadet.c @@ -660,6 +660,157 @@ static HRESULT get_first_pin(IBaseFilter *filter, PIN_DIRECTION pin_dir, IPin ** return E_NOTIMPL; }
+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; +} + +static void stretch_to_bitmap(BYTE *dst, ULONG dst_width, ULONG dst_height, BYTE *src, ULONG src_width, ULONG src_height) +{ + ULONG dst_stride = (dst_width * 3 + 3) & ~3; + ULONG src_stride = (src_width * 3 + 3) & ~3; + ULONG ratio, rem, drift, i = dst_height; + BYTE *dup; + + if (dst_height < src_height) + { + ratio = src_height / dst_height; + rem = src_height % dst_height; + drift = 0; + while (i--) + { + stretch_line(dst, dst_width, src, src_width); + dst += dst_stride; + src += ratio * src_stride; + if (drift < rem) + { + src += src_stride; + drift += dst_height; + } + drift -= rem; + } + } + else + { + drift = dst_height - 1; + while (i--) + { + stretch_line(dst, dst_width, src, src_width); + dup = dst; + dst += dst_stride; + while (drift >= src_height && i--) + { + memcpy(dst, dup, dst_stride); + dst += dst_stride; + drift -= src_height; + } + drift += dst_height - src_height; + src += src_stride; + } + } +} + +#define check_bitmap(buffer, width, height, seek_time) check_bitmap_(__LINE__, buffer, width, height, seek_time) +static void check_bitmap_(unsigned line, void *buffer, LONG width, LONG height, double seek_time) +{ + DWORD fill = (DWORD)(seek_time * 1000.0) ^ 0xccaabb; + BYTE *p = (BYTE*)buffer + sizeof(BITMAPINFOHEADER); + DWORD line_width = (width * 3 + 3) & ~3; + BYTE *img, *src = malloc(640 * 480 * 3); + const BITMAPINFOHEADER *h = buffer; + unsigned i, j, v; + + ok_(__FILE__,line)(h->biSize == sizeof(BITMAPINFOHEADER), "Got biSize %u.\n", h->biSize); + ok_(__FILE__,line)(h->biWidth == width, "Got biWidth %d.\n", h->biWidth); + ok_(__FILE__,line)(h->biHeight == height, "Got biHeight %d.\n", h->biHeight); + ok_(__FILE__,line)(h->biPlanes == 1, "Got biPlanes %d.\n", h->biPlanes); + ok_(__FILE__,line)(h->biBitCount == 24, "Got biBitCount %d.\n", h->biBitCount); + ok_(__FILE__,line)(h->biCompression == BI_RGB, "Got biCompression %d.\n", h->biCompression); + ok_(__FILE__,line)(h->biXPelsPerMeter == 0, "Got biXPelsPerMeter %d.\n", h->biXPelsPerMeter); + ok_(__FILE__,line)(h->biYPelsPerMeter == 0, "Got biYPelsPerMeter %d.\n", h->biYPelsPerMeter); + ok_(__FILE__,line)(h->biClrUsed == 0, "Got biClrUsed %d.\n", h->biClrUsed); + ok_(__FILE__,line)(h->biClrImportant == 0, "Got biClrImportant %d.\n", h->biClrImportant); + + /* The lines are reversed since our source was top-down */ + src = malloc(640 * 480 * 3); + img = src; + for (j = 640 * 480 * 3; j != 0;) + { + j -= 640 * 3; + v = j; + for (i = 0; i < 640 * 3; i += 3, v += 3) + { + img[i] = fill ^ v; + img[i + 1] = fill >> 8 ^ v; + img[i + 2] = fill >> 16 ^ v; + } + img += 640 * 3; + } + + img = malloc(line_width * height); + stretch_to_bitmap(img, width, height, src, 640, 480); + free(src); + + for (i = 0; i < line_width * height; i += 3) + if (p[i] != img[i] || p[i + 1] != img[i + 1] || p[i + 2] != img[i + 2]) + { + ok_(__FILE__,line)(0, "Wrong bitmap data at offset %u (got 0x%06x, expected 0x%06x).\n", + p + i - (BYTE*)buffer, p[i] | p[i + 1] << 8 | p[i + 2] << 16, + img[i] | img[i + 1] << 8 | img[i + 2] << 16); + break; + } + free(img); +} + static BOOL init_tests(void) { return unpack_avi_file(TEST_AVI_RES, test_avi_filename) @@ -1351,7 +1502,7 @@ static void test_bitmap_grab_mode(void) &TIME_FORMAT_BYTE, &TIME_FORMAT_MEDIA_TIME }; - char *buf = malloc(640 * 480 * 3); + char *buf = malloc(sizeof(BITMAPINFOHEADER) + 960 * 720 * 3); struct testfilter testfilter; FILTER_INFO filter_info; IReferenceClock *clock; @@ -1378,6 +1529,9 @@ static void test_bitmap_grab_mode(void)
hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); 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); hr = IMediaDet_GetSampleGrabber(detector, &sg); ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
@@ -1525,6 +1679,42 @@ static void test_bitmap_grab_mode(void) ok(clock == NULL, "Got non-NULL clock.\n"); IMediaFilter_Release(mf);
+ /* 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); + 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); + 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(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); + 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); + + /* 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(size == sizeof(BITMAPINFOHEADER) + 960 * 720 * 3, "Got size %d.\n", size); + todo_wine 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); + 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); + 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); + 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); + /* Changing filter resets bitmap grab mode */ testfilter.bitmap_grab_mode = FALSE; hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); @@ -1535,6 +1725,24 @@ static void test_bitmap_grab_mode(void) ok(hr == S_OK, "Got hr %#x.\n", hr); ok(count == 1, "Got %d streams.\n", count);
+ /* 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); + hr = IMediaDet_GetSampleGrabber(detector, &sg); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + hr = IMediaDet_get_OutputStreams(detector, &count); + ok(hr == S_OK, "Got hr %#x.\n", hr); + 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); + hr = IMediaDet_GetSampleGrabber(detector, &sg); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + if (SUCCEEDED(hr)) ISampleGrabber_Release(sg); + hr = IMediaDet_get_OutputStreams(detector, &count); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ref = IMediaDet_Release(detector); ok(!ref, "Got outstanding refcount %d.\n", ref); ref = IBaseFilter_Release(&testfilter.filter.IBaseFilter_iface);