From 2914b6f1303445025764ec2d9f01a3be2df5cec0 Mon Sep 17 00:00:00 2001 From: Henrique Nakashima Date: Wed, 17 Jan 2018 20:00:37 +0000 Subject: Fix behavior of FPDF_RenderPageBitmapWithMatrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This functional part of this CL is mostly a revert of "Change behaviour of FPDF_RenderPageBitmapWithMatrix" 24b0733a72bbc4013bff8628f198b0aea807aa06 Besides the revert, the parameters passed to pPage->GetDisplayMatrix() are changed to fix a bug with the previous implementation: the page was scaled to fit inside the clipping_rect, instead of clipped. Bug: pdfium:849 Change-Id: I95d0a303a979c998026a3bd6963c8684a1209f03 Reviewed-on: https://pdfium-review.googlesource.com/22931 Reviewed-by: Nicolás Peña Moreno Reviewed-by: dsinclair Commit-Queue: Henrique Nakashima --- core/fpdfapi/page/cpdf_page.cpp | 29 ----- core/fpdfapi/page/cpdf_page.h | 6 - fpdfsdk/fpdfview.cpp | 17 ++- fpdfsdk/fpdfview_embeddertest.cpp | 266 ++++++++++++++++++++++++-------------- 4 files changed, 179 insertions(+), 139 deletions(-) diff --git a/core/fpdfapi/page/cpdf_page.cpp b/core/fpdfapi/page/cpdf_page.cpp index c6f4fb2a39..ee7b5d408a 100644 --- a/core/fpdfapi/page/cpdf_page.cpp +++ b/core/fpdfapi/page/cpdf_page.cpp @@ -177,35 +177,6 @@ CFX_Matrix CPDF_Page::GetDisplayMatrix(int xPos, return matrix; } -// This method follows the same apparent logic as GetDisplayMatrix(). For -// example, consider point 0. First, take the top left coordinate of the -// rectangle in the transformed space. Now, calculate what this point was in the -// original space by inverting. Note that this is not necessarily the same as -// the top left corner of the original rectangle. -CFX_Matrix CPDF_Page::GetDisplayMatrixWithTransformation( - int xPos, - int yPos, - int xSize, - int ySize, - const CFX_Matrix& transformation) { - CFX_FloatRect rect(xPos, yPos, xPos + xSize, yPos + ySize); - rect = transformation.TransformRect(rect); - CFX_Matrix inverse = transformation.GetInverse(); - CFX_PointF point0(rect.left, rect.top); - CFX_PointF point1(rect.left, rect.bottom); - CFX_PointF point2(rect.right, rect.top); - point0 = inverse.Transform(point0); - point1 = inverse.Transform(point1); - point2 = inverse.Transform(point2); - - CFX_Matrix matrix = m_PageMatrix; - matrix.Concat(CFX_Matrix( - (point2.x - point0.x) / m_PageWidth, (point2.y - point0.y) / m_PageWidth, - (point1.x - point0.x) / m_PageHeight, - (point1.y - point0.y) / m_PageHeight, point0.x, point0.y)); - return matrix; -} - int CPDF_Page::GetPageRotation() const { CPDF_Object* pRotate = GetPageAttr("Rotate"); int rotate = pRotate ? (pRotate->GetInteger() / 90) % 4 : 0; diff --git a/core/fpdfapi/page/cpdf_page.h b/core/fpdfapi/page/cpdf_page.h index e32a7f0fe8..e1b5bcfa44 100644 --- a/core/fpdfapi/page/cpdf_page.h +++ b/core/fpdfapi/page/cpdf_page.h @@ -38,12 +38,6 @@ class CPDF_Page : public CPDF_PageObjectHolder { int xSize, int ySize, int iRotate) const; - CFX_Matrix GetDisplayMatrixWithTransformation( - int xPos, - int yPos, - int xSize, - int ySize, - const CFX_Matrix& transformation); float GetPageWidth() const { return m_PageWidth; } float GetPageHeight() const { return m_PageHeight; } diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp index cec44a48f9..97fc02a234 100644 --- a/fpdfsdk/fpdfview.cpp +++ b/fpdfsdk/fpdfview.cpp @@ -1034,13 +1034,16 @@ FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap, if (clipping) clipping_rect = CFXFloatRectFromFSRECTF(*clipping); FX_RECT clip_rect = clipping_rect.ToFxRect(); - RenderPageImpl( - pContext, pPage, - pPage->GetDisplayMatrixWithTransformation( - clip_rect.left, clip_rect.top, clip_rect.Width(), clip_rect.Height(), - CFX_Matrix(matrix->a, matrix->b, matrix->c, matrix->d, matrix->e, - matrix->f)), - clip_rect, flags, true, nullptr); + + CFX_Matrix transform_matrix = pPage->GetDisplayMatrix( + 0, 0, pPage->GetPageWidth(), pPage->GetPageHeight(), 0); + + if (matrix) { + transform_matrix.Concat(CFX_Matrix(matrix->a, matrix->b, matrix->c, + matrix->d, matrix->e, matrix->f)); + } + RenderPageImpl(pContext, pPage, transform_matrix, clip_rect, flags, true, + nullptr); pPage->SetRenderContext(nullptr); } diff --git a/fpdfsdk/fpdfview_embeddertest.cpp b/fpdfsdk/fpdfview_embeddertest.cpp index 7e93574059..cca77c9031 100644 --- a/fpdfsdk/fpdfview_embeddertest.cpp +++ b/fpdfsdk/fpdfview_embeddertest.cpp @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include #include -#include "core/fxcrt/fx_coordinates.h" #include "fpdfsdk/fpdfview_c_api_test.h" #include "public/fpdfview.h" #include "testing/embedder_test.h" @@ -33,7 +33,15 @@ TEST(fpdf, CApiTest) { EXPECT_TRUE(CheckPDFiumCApi()); } -class FPDFViewEmbeddertest : public EmbedderTest {}; +class FPDFViewEmbeddertest : public EmbedderTest { + protected: + void TestRenderPageBitmapWithMatrix(FPDF_PAGE page, + const int bitmap_width, + const int bitmap_height, + const FS_MATRIX& matrix, + const FS_RECTF& rect, + const char* expected_md5); +}; TEST_F(FPDFViewEmbeddertest, Document) { EXPECT_TRUE(OpenDocument("about_blank.pdf")); @@ -377,113 +385,177 @@ TEST_F(FPDFViewEmbeddertest, Hang_360) { EXPECT_FALSE(OpenDocument("bug_360.pdf")); } +void FPDFViewEmbeddertest::TestRenderPageBitmapWithMatrix( + FPDF_PAGE page, + const int bitmap_width, + const int bitmap_height, + const FS_MATRIX& matrix, + const FS_RECTF& rect, + const char* expected_md5) { + FPDF_BITMAP bitmap = FPDFBitmap_Create(bitmap_width, bitmap_height, 0); + FPDFBitmap_FillRect(bitmap, 0, 0, bitmap_width, bitmap_height, 0xFFFFFFFF); + FPDF_RenderPageBitmapWithMatrix(bitmap, page, &matrix, &rect, 0); + CompareBitmap(bitmap, bitmap_width, bitmap_height, expected_md5); + FPDFBitmap_Destroy(bitmap); +} + TEST_F(FPDFViewEmbeddertest, FPDF_RenderPageBitmapWithMatrix) { - const char* const kRotatedMD5[4] = { - "0a90de37f52127619c3dfb642b5fa2fe", "d599429574ff0dcad3bc898ea8b874ca", - "0113386bb0bd45125bacc6dee78bfe78", "051fcfa4c1f9de28765705633a8ef3a9"}; - const char kTopLeftQuarterMD5[] = "4982be08db3f6d2e6409186ebbced9eb"; - const char kHoriStretchedMD5[] = "004bf38f3c5c76a644e6fca204747f21"; - const char kRotateandStretchMD5[] = "0ea95cacc716d003cf063a2c5ed6c8d7"; + const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe"; + const char kClippedMD5[] = "a84cab93c102b9b9290fba3047ba702c"; + const char kTopLeftQuarterMD5[] = "f11a11137c8834389e31cf555a4a6979"; + const char kHoriStretchedMD5[] = "48ef9205941ed19691ccfa00d717187e"; + const char kRotated90ClockwiseMD5[] = "d8da2c7bf77521550d0f2752b9cf3482"; + const char kRotated180ClockwiseMD5[] = "0113386bb0bd45125bacc6dee78bfe78"; + const char kRotated270ClockwiseMD5[] = "a287e0f74ce203699cda89f9cc97a240"; + const char kMirrorHoriMD5[] = "6e8d7a6fde39d8e720fb9e620102918c"; + const char kMirrorVertMD5[] = "8f3a555ef9c0d5031831ae3715273707"; + const char kLargerTopLeftQuarterMD5[] = "172a2f4adafbadbe98017b1c025b9e27"; + const char kLargerMD5[] = "c806145641c3e6fc4e022c7065343749"; + const char kLargerClippedMD5[] = "091d3b1c7933c8f6945eb2cb41e588e9"; + const char kLargerRotatedMD5[] = "115f13353ebfc82ddb392d1f0059eb12"; + const char kLargerRotatedLandscapeMD5[] = "c901239d17d84ac84cb6f2124da71b0d"; + const char kLargerRotatedDiagonalMD5[] = "3d62417468bdaff0eb14391a0c30a3b1"; + const char kTileMD5[] = "0a190003c97220bf8877684c8d7e89cf"; EXPECT_TRUE(OpenDocument("rectangles.pdf")); FPDF_PAGE page = LoadPage(0); EXPECT_NE(nullptr, page); - const int initial_width = static_cast(FPDF_GetPageWidth(page)); - const int initial_height = static_cast(FPDF_GetPageHeight(page)); - EXPECT_EQ(200, initial_width); - EXPECT_EQ(300, initial_height); + const int page_width = static_cast(FPDF_GetPageWidth(page)); + const int page_height = static_cast(FPDF_GetPageHeight(page)); + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); FPDF_BITMAP bitmap = RenderPage(page); - CompareBitmap(bitmap, initial_width, initial_height, kRotatedMD5[0]); + CompareBitmap(bitmap, page_width, page_height, kOriginalMD5); FPDFBitmap_Destroy(bitmap); - // Try the easy rotations: 0, 90, 180, 270 clockwise. The output should be the - // same as FPDF_RenderPageBitmap with the appropriate rotation flag. Per PDF - // spec section 4.2.2, a t degree rotation is represented by [cos(t) sin(t) - // -sin(t) cos(t) 0 0] (matrix goes on the right in the multiplication). - FS_RECTF rect = {0, 0, initial_width, initial_height}; - CFX_Matrix rot_matrices[4] = { - CFX_Matrix(1, 0, 0, 1, 0, 0), CFX_Matrix(0, -1, 1, 0, 0, 0), - CFX_Matrix(-1, 0, 0, -1, 0, 0), CFX_Matrix(0, 1, -1, 0, 0, 0)}; - for (int rot = 0; rot < 4; ++rot) { - int width; - int height; - FS_MATRIX matrix; - matrix.a = rot_matrices[rot].a; - matrix.b = rot_matrices[rot].b; - matrix.c = rot_matrices[rot].c; - matrix.d = rot_matrices[rot].d; - matrix.e = rot_matrices[rot].e; - matrix.f = rot_matrices[rot].f; - if (rot % 2 == 0) { - width = initial_width; - height = initial_height; - } else { - width = initial_height; - height = initial_width; - } - rect.right = width; - rect.bottom = height; - - bitmap = FPDFBitmap_Create(width, height, 0); - FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF); - FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, rot, 0); - CompareBitmap(bitmap, width, height, kRotatedMD5[rot]); - FPDFBitmap_Destroy(bitmap); - - bitmap = FPDFBitmap_Create(width, height, 0); - FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF); - FPDF_RenderPageBitmapWithMatrix(bitmap, page, &matrix, &rect, 0); - CompareBitmap(bitmap, width, height, kRotatedMD5[rot]); - FPDFBitmap_Destroy(bitmap); - } - // TODO(npm): what to do with transformations that do not align the page with - // the axis, like a 45 degree rotation (currently, part of the page gets cut - // out). pdfium:849 - - // Now render again with the image scaled smaller. - int width = initial_width / 2; - int height = initial_height / 2; - FS_MATRIX matrix = {0.5, 0, 0, 0.5, 0, 0}; - - rect.right = width; - rect.bottom = height; + FS_RECTF page_rect{0, 0, page_width, page_height}; - bitmap = FPDFBitmap_Create(width, height, 0); - FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF); - FPDF_RenderPageBitmapWithMatrix(bitmap, page, &matrix, &rect, 0); - CompareBitmap(bitmap, width, height, kTopLeftQuarterMD5); - FPDFBitmap_Destroy(bitmap); + // Try rendering with an identity matrix. The output should be the same as + // the RenderPage() output. + FS_MATRIX identity_matrix{1, 0, 0, 1, 0, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix, + page_rect, kOriginalMD5); - // Now render again with the image scaled larger horizontally. - width = initial_width * 2; - height = initial_height; - matrix.a = 2; - matrix.d = 1; - rect.right = width; - rect.bottom = height; - bitmap = FPDFBitmap_Create(width, height, 0); - FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF); - FPDF_RenderPageBitmapWithMatrix(bitmap, page, &matrix, &rect, 0); - CompareBitmap(bitmap, width, height, kHoriStretchedMD5); - FPDFBitmap_Destroy(bitmap); + // Again render with an identity matrix but with a smaller clipping rect. + FS_RECTF middle_of_page_rect{page_width / 4, page_height / 4, + page_width * 3 / 4, page_height * 3 / 4}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix, + middle_of_page_rect, kClippedMD5); - // Test a rotation followed by a stretch. - width = initial_height * 2; - height = initial_width; - matrix.a = 0; - matrix.b = -1; - matrix.c = 2; - matrix.d = 0; - matrix.e = 0; - matrix.f = 0; - rect.right = width; - rect.bottom = height; - bitmap = FPDFBitmap_Create(width, height, 0); - FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF); - FPDF_RenderPageBitmapWithMatrix(bitmap, page, &matrix, &rect, 0); - CompareBitmap(bitmap, width, height, kRotateandStretchMD5); - FPDFBitmap_Destroy(bitmap); + // Now render again with the image scaled smaller. + FS_MATRIX half_scale_matrix{0.5, 0, 0, 0.5, 0, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + half_scale_matrix, page_rect, + kTopLeftQuarterMD5); + + // Now render again with the image scaled larger horizontally (the right half + // will be clipped). + FS_MATRIX stretch_x_matrix{2, 0, 0, 1, 0, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + stretch_x_matrix, page_rect, + kHoriStretchedMD5); + + // Try a 90 degree rotation clockwise but with the same bitmap size, so part + // will be clipped. + FS_MATRIX rotate_90_matrix{0, 1, -1, 0, page_width, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + rotate_90_matrix, page_rect, + kRotated90ClockwiseMD5); + + // 180 degree rotation clockwise. + FS_MATRIX rotate_180_matrix{-1, 0, 0, -1, page_width, page_height}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + rotate_180_matrix, page_rect, + kRotated180ClockwiseMD5); + + // 270 degree rotation clockwise. + FS_MATRIX rotate_270_matrix{0, -1, 1, 0, 0, page_width}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + rotate_270_matrix, page_rect, + kRotated270ClockwiseMD5); + + // Mirror horizontally. + FS_MATRIX mirror_hori_matrix{-1, 0, 0, 1, page_width, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + mirror_hori_matrix, page_rect, kMirrorHoriMD5); + + // Mirror vertically. + FS_MATRIX mirror_vert_matrix{1, 0, 0, -1, 0, page_height}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + mirror_vert_matrix, page_rect, kMirrorVertMD5); + + // Tests rendering to a larger bitmap + const int bitmap_width = page_width * 2; + const int bitmap_height = page_height * 2; + + // Render using an identity matrix and the whole bitmap area as clipping rect. + FS_RECTF bitmap_rect{0, 0, bitmap_width, bitmap_height}; + TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height, + identity_matrix, bitmap_rect, + kLargerTopLeftQuarterMD5); + + // Render using a scaling matrix to fill the larger bitmap. + FS_MATRIX double_scale_matrix{2, 0, 0, 2, 0, 0}; + TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height, + double_scale_matrix, bitmap_rect, kLargerMD5); + + // Render the larger image again but with clipping. + FS_RECTF middle_of_bitmap_rect{bitmap_width / 4, bitmap_height / 4, + bitmap_width * 3 / 4, bitmap_height * 3 / 4}; + TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height, + double_scale_matrix, middle_of_bitmap_rect, + kLargerClippedMD5); + + // On the larger bitmap, try a 90 degree rotation but with the same bitmap + // size, so part will be clipped. + FS_MATRIX rotate_90_scale_2_matrix{0, 2, -2, 0, bitmap_width, 0}; + TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height, + rotate_90_scale_2_matrix, bitmap_rect, + kLargerRotatedMD5); + + // On the larger bitmap, apply 90 degree rotation to a bitmap with the + // appropriate dimensions. + const int landscape_bitmap_width = bitmap_height; + const int landscape_bitmap_height = bitmap_width; + FS_RECTF landscape_bitmap_rect{0, 0, landscape_bitmap_width, + landscape_bitmap_height}; + FS_MATRIX landscape_rotate_90_scale_2_matrix{ + 0, 2, -2, 0, landscape_bitmap_width, 0}; + TestRenderPageBitmapWithMatrix( + page, landscape_bitmap_width, landscape_bitmap_height, + landscape_rotate_90_scale_2_matrix, landscape_bitmap_rect, + kLargerRotatedLandscapeMD5); + + // On the larger bitmap, apply 45 degree rotation to a bitmap with the + // appropriate dimensions. + const float sqrt2 = 1.41421356f; + const int diagonal_bitmap_size = ceil((bitmap_width + bitmap_height) / sqrt2); + FS_RECTF diagonal_bitmap_rect{0, 0, diagonal_bitmap_size, + diagonal_bitmap_size}; + FS_MATRIX rotate_45_scale_2_matrix{ + sqrt2, sqrt2, -sqrt2, sqrt2, bitmap_height / sqrt2, 0}; + TestRenderPageBitmapWithMatrix(page, diagonal_bitmap_size, + diagonal_bitmap_size, rotate_45_scale_2_matrix, + diagonal_bitmap_rect, + kLargerRotatedDiagonalMD5); + + // Render the (2, 1) tile of the page (third column, second row) when the page + // is divided in 50x50 pixel tiles. The tile is scaled by a factor of 7. + const float scale = 7.0; + const int tile_size = 50; + const int tile_x = 2; + const int tile_y = 1; + int tile_bitmap_size = scale * tile_size; + FS_RECTF tile_bitmap_rect{0, 0, tile_bitmap_size, tile_bitmap_size}; + FS_MATRIX tile_2_1_matrix{scale, + 0, + 0, + scale, + -tile_x * tile_bitmap_size, + -tile_y * tile_bitmap_size}; + TestRenderPageBitmapWithMatrix(page, tile_bitmap_size, tile_bitmap_size, + tile_2_1_matrix, tile_bitmap_rect, kTileMD5); UnloadPage(page); } -- cgit v1.2.3