summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Faizan Naikwade <faizannaikwade@google.com> 2025-03-18 11:14:11 +0000
committer Faizan Naikwade <faizannaikwade@google.com> 2025-03-24 11:11:45 +0000
commitc0acbf4e5b13fc4b2b0bc78aa18f12fe6d35cbae (patch)
tree3db7ded29699fe089f63572f911bdb65acb53079
parent15770948c53b976503efbe303089e0b247783950 (diff)
PdfPageImageObject Bitmap Color Channel Issue
The Android Bitmap ARGB_8888 which we support for PdfPageImageObject stores the color channel in the native layer as RGBA and the Pdfium expects the Bitmap color channel to be BGRA which resulted in different image content being rendered. This CL implements RGBA to BGRA on addPageObject and vice-versa on getPageObjects. Bug: 401988277 Test: CTS test for data consistency and manual testing on test app. Flag: EXEMPT (401988277) Change-Id: Ic08d822c673199fd8ea4045015045b1400e4cc8c
-rw-r--r--pdf/framework/libs/pdfClient/image_object.cc34
-rw-r--r--pdf/framework/libs/pdfClient/image_object.h14
-rw-r--r--pdf/framework/libs/pdfClient/jni_conversion.cc130
-rw-r--r--pdf/framework/libs/pdfClient/jni_conversion.h2
4 files changed, 155 insertions, 25 deletions
diff --git a/pdf/framework/libs/pdfClient/image_object.cc b/pdf/framework/libs/pdfClient/image_object.cc
index 61bb85c69..69dedb0bc 100644
--- a/pdf/framework/libs/pdfClient/image_object.cc
+++ b/pdf/framework/libs/pdfClient/image_object.cc
@@ -26,6 +26,23 @@
namespace pdfClient {
+BitmapFormat GetBitmapFormat(int bitmap_format) {
+ switch (bitmap_format) {
+ case FPDFBitmap_BGR: {
+ return BitmapFormat::BGR;
+ }
+ case FPDFBitmap_BGRA: {
+ return BitmapFormat::BGRA;
+ }
+ case FPDFBitmap_BGRx: {
+ return BitmapFormat::BGRx;
+ }
+ default: {
+ return BitmapFormat::Unknown;
+ }
+ }
+}
+
ImageObject::ImageObject() : PageObject(Type::Image) {}
ScopedFPDFPageObject ImageObject::CreateFPDFInstance(FPDF_DOCUMENT document, FPDF_PAGE page) {
@@ -61,31 +78,42 @@ bool ImageObject::UpdateFPDFInstance(FPDF_PAGEOBJECT image_object, FPDF_PAGE pag
return false;
}
+ // Set the updated dimensions.
width_ = FPDFBitmap_GetWidth(bitmap_.get());
height_ = FPDFBitmap_GetHeight(bitmap_.get());
+ // Set the updated bitmap format.
+ bitmap_format_ = GetBitmapFormat(FPDFBitmap_GetFormat(bitmap_.get()));
+
return true;
}
bool ImageObject::PopulateFromFPDFInstance(FPDF_PAGEOBJECT image_object, FPDF_PAGE page) {
- // Get Bitmap
+ // Get bitmap.
bitmap_ = ScopedFPDFBitmap(FPDFImageObj_GetBitmap(image_object));
if (bitmap_.get() == nullptr) {
return false;
}
- // Get Matrix
+ // Get matrix.
if (!GetPageToDeviceMatrix(image_object, page)) {
return false;
}
+ // Get dimensions.
width_ = FPDFBitmap_GetWidth(bitmap_.get());
height_ = FPDFBitmap_GetHeight(bitmap_.get());
+ // Get bitmap format.
+ bitmap_format_ = GetBitmapFormat(FPDFBitmap_GetFormat(bitmap_.get()));
+ if (bitmap_format_ == BitmapFormat::Unknown) {
+ LOGE("Bitmap format unknown");
+ return false;
+ }
return true;
}
-void* ImageObject::GetBitmapReadableBuffer() const {
+void* ImageObject::GetBitmapBuffer() const {
return FPDFBitmap_GetBuffer(bitmap_.get());
}
diff --git a/pdf/framework/libs/pdfClient/image_object.h b/pdf/framework/libs/pdfClient/image_object.h
index 2e3aa9541..e3563ba8e 100644
--- a/pdf/framework/libs/pdfClient/image_object.h
+++ b/pdf/framework/libs/pdfClient/image_object.h
@@ -27,6 +27,13 @@ typedef unsigned int uint;
namespace pdfClient {
+enum class BitmapFormat {
+ Unknown = -1,
+ BGR,
+ BGRA,
+ BGRx,
+};
+
class ImageObject : public PageObject {
public:
ImageObject();
@@ -35,12 +42,13 @@ class ImageObject : public PageObject {
bool UpdateFPDFInstance(FPDF_PAGEOBJECT image_object, FPDF_PAGE page) override;
bool PopulateFromFPDFInstance(FPDF_PAGEOBJECT image_object, FPDF_PAGE page) override;
- void* GetBitmapReadableBuffer() const;
+ void* GetBitmapBuffer() const;
~ImageObject();
- int width_ = 0;
- int height_ = 0;
+ size_t width_ = 0;
+ size_t height_ = 0;
+ BitmapFormat bitmap_format_ = BitmapFormat::Unknown;
ScopedFPDFBitmap bitmap_;
};
diff --git a/pdf/framework/libs/pdfClient/jni_conversion.cc b/pdf/framework/libs/pdfClient/jni_conversion.cc
index c132b6698..46d5b2d9e 100644
--- a/pdf/framework/libs/pdfClient/jni_conversion.cc
+++ b/pdf/framework/libs/pdfClient/jni_conversion.cc
@@ -25,6 +25,7 @@
#include "text_object.h"
using pdfClient::Annotation;
+using pdfClient::BitmapFormat;
using pdfClient::Color;
using pdfClient::Document;
using pdfClient::Font;
@@ -450,7 +451,41 @@ jobject ToJavaGotoLinks(JNIEnv* env, const vector<GotoLink>& links) {
return ToJavaList(env, links, &ToJavaGotoLink);
}
-jobject ToJavaBitmap(JNIEnv* env, void* buffer, int width, int height) {
+void ConvertBgrToRgba(uint32_t* rgba_pixel_array, uint8_t* bgr_pixel_array, size_t rgba_stride,
+ size_t bgr_stride, size_t width, size_t height) {
+ for (size_t y = 0; y < height; y++) {
+ uint32_t* rgba_row_ptr = rgba_pixel_array + y * (rgba_stride / 4);
+ uint8_t* bgr_row_ptr = bgr_pixel_array + y * (bgr_stride);
+ for (size_t x = 0; x < width; x++) {
+ // Extract BGR components stored.
+ uint8_t blue = bgr_row_ptr[x * 3];
+ uint8_t green = bgr_row_ptr[x * 3 + 1];
+ uint8_t red = bgr_row_ptr[x * 3 + 2];
+ // Storing java bitmap components RGBA in little-endian.
+ rgba_row_ptr[x] = (0xFF << 24) | (blue << 16) | (green << 8) | red;
+ }
+ }
+}
+
+void ConvertBgraToRgba(uint32_t* rgba_pixel_array, uint8_t* bgra_pixel_array, size_t rgba_stride,
+ size_t bgra_stride, size_t width, size_t height, bool ignore_alpha) {
+ for (size_t y = 0; y < height; y++) {
+ uint32_t* rgba_row_ptr = rgba_pixel_array + y * (rgba_stride / 4);
+ uint8_t* bgra_row_ptr = bgra_pixel_array + y * (bgra_stride);
+ for (size_t x = 0; x < width; x++) {
+ // Extract BGR components and determine alpha based on ignore_alpha flag.
+ uint8_t blue = bgra_row_ptr[x * 4];
+ uint8_t green = bgra_row_ptr[x * 4 + 1];
+ uint8_t red = bgra_row_ptr[x * 4 + 2];
+ uint8_t alpha = ignore_alpha ? 0xFF : bgra_row_ptr[x * 4 + 3];
+ // Storing java bitmap components RGBA in little-endian.
+ rgba_row_ptr[x] = (alpha << 24) | (blue << 16) | (green << 8) | red;
+ }
+ }
+}
+
+jobject ToJavaBitmap(JNIEnv* env, void* buffer, BitmapFormat bitmap_format, size_t width,
+ size_t height, size_t native_stride) {
// Find Java Bitmap class
static jclass bitmap_class = GetPermClassRef(env, kBitmap);
@@ -469,16 +504,41 @@ jobject ToJavaBitmap(JNIEnv* env, void* buffer, int width, int height) {
jobject java_bitmap =
env->CallStaticObjectMethod(bitmap_class, create_bitmap, width, height, argb8888);
- // Lock the Bitmap pixels for copying
+ // Copy the buffer data into java bitmap.
+ AndroidBitmapInfo bitmap_info;
+ AndroidBitmap_getInfo(env, java_bitmap, &bitmap_info);
+ size_t java_stride = bitmap_info.stride;
+
void* bitmap_pixels;
if (AndroidBitmap_lockPixels(env, java_bitmap, &bitmap_pixels) < 0) {
return NULL;
}
- // Copy the buffer data into java Bitmap.
- std::memcpy(bitmap_pixels, buffer, width * height); // 4 bytes per pixel (ARGB_8888)
+ uint32_t* java_pixel_array = static_cast<uint32_t*>(bitmap_pixels);
+ uint8_t* native_pixel_array = static_cast<uint8_t*>(buffer);
+ switch (bitmap_format) {
+ case BitmapFormat::BGR: {
+ ConvertBgrToRgba(java_pixel_array, native_pixel_array, java_stride, native_stride,
+ width, height);
+ break;
+ }
+ case BitmapFormat::BGRA: {
+ ConvertBgraToRgba(java_pixel_array, native_pixel_array, java_stride, native_stride,
+ width, height, false);
+ break;
+ }
+ case BitmapFormat::BGRx: {
+ ConvertBgraToRgba(java_pixel_array, native_pixel_array, java_stride, native_stride,
+ width, height, true);
+ break;
+ }
+ default: {
+ LOGE("Bitmap format unknown!");
+ AndroidBitmap_unlockPixels(env, java_bitmap);
+ return NULL;
+ }
+ }
- // Unlock the Bitmap pixels
AndroidBitmap_unlockPixels(env, java_bitmap);
return java_bitmap;
@@ -682,11 +742,17 @@ jobject ToJavaPdfImageObject(JNIEnv* env, const ImageObject* image_object) {
static jmethodID init_image =
env->GetMethodID(image_object_class, "<init>", funcsig("V", kBitmap).c_str());
- // Get Bitmap readable buffer from ImageObject Data.
- void* buffer = image_object->GetBitmapReadableBuffer();
-
// Create Java Bitmap from Native Bitmap Buffer.
- jobject java_bitmap = ToJavaBitmap(env, buffer, image_object->width_, image_object->height_);
+ void* buffer = image_object->GetBitmapBuffer();
+ BitmapFormat bitmap_format = image_object->bitmap_format_;
+ size_t width = image_object->width_;
+ size_t height = image_object->height_;
+ int stride = FPDFBitmap_GetStride(image_object->bitmap_.get());
+ jobject java_bitmap = ToJavaBitmap(env, buffer, bitmap_format, width, height, stride);
+ if (java_bitmap == NULL) {
+ LOGE("To java bitmap conversion failed!");
+ return NULL;
+ }
// Create Java PdfImageObject Instance.
jobject java_image_object = env->NewObject(image_object_class, init_image, java_bitmap);
@@ -934,6 +1000,23 @@ std::unique_ptr<PathObject> ToNativePathObject(JNIEnv* env, jobject java_path_ob
return path_object;
}
+void CopyRgbaToBgra(uint8_t* rgba_pixel_array, size_t rgba_stride, uint32_t* bgra_pixel_array,
+ size_t bgra_stride, size_t width, size_t height) {
+ for (size_t y = 0; y < height; y++) {
+ uint8_t* rgba_row_ptr = rgba_pixel_array + y * rgba_stride;
+ uint32_t* bgra_row_ptr = bgra_pixel_array + y * (bgra_stride / 4);
+ for (size_t x = 0; x < width; x++) {
+ // Extract RGBA components stored.
+ uint8_t red = rgba_row_ptr[x * 4];
+ uint8_t green = rgba_row_ptr[x * 4 + 1];
+ uint8_t blue = rgba_row_ptr[x * 4 + 2];
+ uint8_t alpha = rgba_row_ptr[x * 4 + 3];
+ // Storing native bitmap components BGRA in little-endian.
+ bgra_row_ptr[x] = (alpha << 24) | (red << 16) | (green << 8) | blue;
+ }
+ }
+}
+
std::unique_ptr<ImageObject> ToNativeImageObject(JNIEnv* env, jobject java_image_object) {
// Create ImageObject Data Instance.
auto image_object = std::make_unique<ImageObject>();
@@ -946,21 +1029,34 @@ std::unique_ptr<ImageObject> ToNativeImageObject(JNIEnv* env, jobject java_image
env->GetMethodID(image_object_class, "getBitmap", funcsig(kBitmap).c_str());
jobject java_bitmap = env->CallObjectMethod(java_image_object, get_bitmap);
- // Create an FPDF_BITMAP from the Android Bitmap.
+ // Get android bitmap info.
+ AndroidBitmapInfo bitmap_info;
+ AndroidBitmap_getInfo(env, java_bitmap, &bitmap_info);
+ if (bitmap_info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
+ LOGE("Android bitmap is not in RGBA_8888 format");
+ return nullptr;
+ }
+ size_t bitmap_width = bitmap_info.width;
+ size_t bitmap_height = bitmap_info.height;
+ size_t java_stride = bitmap_info.stride;
+
+ // Create ImageObject data bitmap.
+ image_object->bitmap_ = ScopedFPDFBitmap(FPDFBitmap_Create(bitmap_width, bitmap_height, 1));
+ size_t native_stride = FPDFBitmap_GetStride(image_object->bitmap_.get());
+
+ // Copy pixels from android bitmap.
void* bitmap_pixels;
if (AndroidBitmap_lockPixels(env, java_bitmap, &bitmap_pixels) < 0) {
+ LOGE("Android bitmap lock pixels failed!");
return nullptr;
}
- AndroidBitmapInfo bitmap_info;
- AndroidBitmap_getInfo(env, java_bitmap, &bitmap_info);
- const int stride = bitmap_info.width * 4;
+ uint8_t* java_pixel_array = static_cast<uint8_t*>(bitmap_pixels);
+ uint32_t* native_pixel_array = static_cast<uint32_t*>(image_object->GetBitmapBuffer());
- // Set ImageObject Data Bitmap
- image_object->bitmap_ = ScopedFPDFBitmap(FPDFBitmap_CreateEx(
- bitmap_info.width, bitmap_info.height, FPDFBitmap_BGRA, bitmap_pixels, stride));
+ CopyRgbaToBgra(java_pixel_array, java_stride, native_pixel_array, native_stride, bitmap_width,
+ bitmap_height);
- // Unlock the Android Bitmap
AndroidBitmap_unlockPixels(env, java_bitmap);
return image_object;
diff --git a/pdf/framework/libs/pdfClient/jni_conversion.h b/pdf/framework/libs/pdfClient/jni_conversion.h
index 2b679405b..30b784250 100644
--- a/pdf/framework/libs/pdfClient/jni_conversion.h
+++ b/pdf/framework/libs/pdfClient/jni_conversion.h
@@ -117,8 +117,6 @@ jobject ToJavaGotoLink(JNIEnv* env, const GotoLink link);
jobject ToJavaGotoLinks(JNIEnv* env, const vector<GotoLink>& links);
-jobject ToJavaBitmap(JNIEnv* env, void* buffer, int width, int height);
-
jobject ToJavaColor(JNIEnv* env, Color color);
jfloatArray ToJavaFloatArray(JNIEnv* env, const float arr[], size_t length);