Better explain how the FPDFBitmap_CreateEx() stride parameter works.
Add tests to show the 4 possible combinations between the first_scan and
stride parameters.
Change-Id: I458c5eac778fa5508c6a3f863df7ff604ed667ad
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/95330
Reviewed-by: Nigi <nigi@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp
index 92fb97d..c635a9c 100644
--- a/fpdfsdk/fpdf_view_embeddertest.cpp
+++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -131,38 +131,71 @@
void TestRenderPageBitmapWithInternalMemory(FPDF_PAGE page,
int format,
const char* expected_md5) {
+ TestRenderPageBitmapWithInternalMemoryAndStride(
+ page, format, /*bitmap_stride=*/0, expected_md5);
+ }
+
+ void TestRenderPageBitmapWithInternalMemoryAndStride(
+ FPDF_PAGE page,
+ int format,
+ int bitmap_stride,
+ const char* expected_md5) {
int bitmap_width = static_cast<int>(FPDF_GetPageWidth(page));
int bitmap_height = static_cast<int>(FPDF_GetPageHeight(page));
int bytes_per_pixel = BytesPerPixelForFormat(format);
ASSERT_NE(0, bytes_per_pixel);
- ScopedFPDFBitmap bitmap(
- FPDFBitmap_CreateEx(bitmap_width, bitmap_height, format, nullptr, 0));
- FPDFBitmap_FillRect(bitmap.get(), 0, 0, bitmap_width, bitmap_height,
- 0xFFFFFFFF);
- FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, bitmap_width, bitmap_height,
- 0, 0);
- CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5);
+ ScopedFPDFBitmap bitmap(FPDFBitmap_CreateEx(
+ bitmap_width, bitmap_height, format, nullptr, bitmap_stride));
+ RenderPageToBitmapAndCheck(page, bitmap.get(), expected_md5);
}
void TestRenderPageBitmapWithExternalMemory(FPDF_PAGE page,
int format,
const char* expected_md5) {
int bitmap_width = static_cast<int>(FPDF_GetPageWidth(page));
- int bitmap_height = static_cast<int>(FPDF_GetPageHeight(page));
int bytes_per_pixel = BytesPerPixelForFormat(format);
ASSERT_NE(0, bytes_per_pixel);
int bitmap_stride = bytes_per_pixel * bitmap_width;
+ return TestRenderPageBitmapWithExternalMemoryImpl(
+ page, format, bitmap_stride, expected_md5);
+ }
+
+ void TestRenderPageBitmapWithExternalMemoryAndNoStride(
+ FPDF_PAGE page,
+ int format,
+ const char* expected_md5) {
+ return TestRenderPageBitmapWithExternalMemoryImpl(
+ page, format, /*bitmap_stride=*/0, expected_md5);
+ }
+
+ private:
+ void TestRenderPageBitmapWithExternalMemoryImpl(FPDF_PAGE page,
+ int format,
+ int bitmap_stride,
+ const char* expected_md5) {
+ int bitmap_width = static_cast<int>(FPDF_GetPageWidth(page));
+ int bitmap_height = static_cast<int>(FPDF_GetPageHeight(page));
+
std::vector<uint8_t> external_memory(bitmap_stride * bitmap_height);
ScopedFPDFBitmap bitmap(FPDFBitmap_CreateEx(bitmap_width, bitmap_height,
format, external_memory.data(),
bitmap_stride));
- FPDFBitmap_FillRect(bitmap.get(), 0, 0, bitmap_width, bitmap_height,
- 0xFFFFFFFF);
- FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, bitmap_width, bitmap_height,
- 0, 0);
- CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5);
+ RenderPageToBitmapAndCheck(page, bitmap.get(), expected_md5);
+ }
+
+ void RenderPageToBitmapAndCheck(FPDF_PAGE page,
+ FPDF_BITMAP bitmap,
+ const char* expected_md5) {
+ int bitmap_width = FPDFBitmap_GetWidth(bitmap);
+ int bitmap_height = FPDFBitmap_GetHeight(bitmap);
+ EXPECT_EQ(bitmap_width, static_cast<int>(FPDF_GetPageWidth(page)));
+ EXPECT_EQ(bitmap_height, static_cast<int>(FPDF_GetPageHeight(page)));
+ FPDFBitmap_FillRect(bitmap, 0, 0, bitmap_width, bitmap_height, 0xFFFFFFFF);
+ FPDF_RenderPageBitmap(bitmap, page, 0, 0, bitmap_width, bitmap_height, 0,
+ 0);
+ CompareBitmap(bitmap, bitmap_width, bitmap_height, expected_md5);
}
};
@@ -1387,19 +1420,41 @@
static const char kGrayMD5[] = "b561c11edc44dc3972125a9b8744fa2f";
static const char kBgrMD5[] = "ab6312e04c0d3f4e46fb302a45173d05";
+ static constexpr int kBgrStride = 600; // Width of 200 * 24 bits per pixel.
TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_BGR, kBgrMD5);
+ TestRenderPageBitmapWithInternalMemoryAndStride(page, FPDFBitmap_BGR,
+ kBgrStride, kBgrMD5);
TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGR, kBgrMD5);
+ TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_BGR,
+ kBgrMD5);
#endif
+
TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_Gray, kGrayMD5);
+ static constexpr int kGrayStride = 200; // Width of 200 * 8 bits per pixel.
+ TestRenderPageBitmapWithInternalMemoryAndStride(page, FPDFBitmap_Gray,
+ kGrayStride, kGrayMD5);
TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_Gray, kGrayMD5);
+ TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_Gray,
+ kGrayMD5);
+
+ static constexpr int kBgrxStride = 800; // Width of 200 * 32 bits per pixel.
TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_BGRx,
kManyRectanglesChecksum);
+ TestRenderPageBitmapWithInternalMemoryAndStride(
+ page, FPDFBitmap_BGRx, kBgrxStride, kManyRectanglesChecksum);
TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRx,
kManyRectanglesChecksum);
+ TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_BGRx,
+ kManyRectanglesChecksum);
+
TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_BGRA,
kManyRectanglesChecksum);
+ TestRenderPageBitmapWithInternalMemoryAndStride(
+ page, FPDFBitmap_BGRA, kBgrxStride, kManyRectanglesChecksum);
TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRA,
kManyRectanglesChecksum);
+ TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_BGRA,
+ kManyRectanglesChecksum);
UnloadPage(page);
}
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 9c135a6..092847e 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -1059,9 +1059,15 @@
// above.
// first_scan - A pointer to the first byte of the first line if
// using an external buffer. If this parameter is NULL,
-// then the a new buffer will be created.
-// stride - Number of bytes for each scan line, for external
-// buffer only.
+// then a new buffer will be created.
+// stride - Number of bytes for each scan line. The value must
+// be 0 or greater. When the value is 0,
+// FPDFBitmap_CreateEx() will automatically calculate
+// the appropriate value using |width| and |format|.
+// When using an external buffer, it is recommended for
+// the caller to pass in the value.
+// When not using an external buffer, it is recommended
+// for the caller to pass in 0.
// Return value:
// The bitmap handle, or NULL if parameter error or out of memory.
// Comments:
@@ -1070,9 +1076,11 @@
// function can be used in any place that a FPDF_BITMAP handle is
// required.
//
-// If an external buffer is used, then the application should destroy
-// the buffer by itself. FPDFBitmap_Destroy function will not destroy
-// the buffer.
+// If an external buffer is used, then the caller should destroy the
+// buffer. FPDFBitmap_Destroy() will not destroy the buffer.
+//
+// It is recommended to use FPDFBitmap_GetStride() to get the stride
+// value.
FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width,
int height,
int format,