thumbnail.cc 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "pdf/ui/thumbnail.h"
  5. #include <stddef.h>
  6. #include <algorithm>
  7. #include <cmath>
  8. #include <utility>
  9. #include "base/check.h"
  10. #include "base/check_op.h"
  11. #include "base/cxx17_backports.h"
  12. #include "base/numerics/checked_math.h"
  13. #include "base/values.h"
  14. #include "ui/gfx/geometry/size.h"
  15. namespace chrome_pdf {
  16. namespace {
  17. constexpr float kMinDevicePixelRatio = 0.25;
  18. constexpr float kMaxDevicePixelRatio = 2;
  19. constexpr int kImageColorChannels = 4;
  20. // TODO(crbug.com/702993): Reevaluate the thumbnail size cap when the PDF
  21. // component migrates off of PPAPI.
  22. // The maximum thumbnail area is essentially arbitrary, but the value was chosen
  23. // considering the fact that when sending array buffers through PPAPI, if the
  24. // size of the data is over 256KiB, it gets sent using shared memory instead of
  25. // IPC. Thumbnail sizes are capped at 255KiB to avoid the 256KiB threshold for
  26. // images and their metadata, such as size.
  27. constexpr int kMaxThumbnailPixels = 255 * 1024 / kImageColorChannels;
  28. // Maximum CSS dimensions are set to match UX specifications.
  29. // These constants should be kept in sync with `PORTRAIT_WIDTH` and
  30. // `LANDSCAPE_WIDTH` in
  31. // chrome/browser/resources/pdf/elements/viewer-thumbnail.ts.
  32. constexpr int kMaxWidthPortraitPx = 108;
  33. constexpr int kMaxWidthLandscapePx = 140;
  34. // PDF page size limits in default user space units, as defined by PDF 1.7 Annex
  35. // C.2, "Architectural limits".
  36. constexpr int kPdfPageMinDimension = 3;
  37. constexpr int kPdfPageMaxDimension = 14400;
  38. constexpr int kPdfMaxAspectRatio = kPdfPageMaxDimension / kPdfPageMinDimension;
  39. // Limit the proportions within PDF limits to handle pathological PDF pages.
  40. gfx::Size LimitAspectRatio(gfx::Size page_size) {
  41. // Bump up any lengths of 0 to 1.
  42. page_size.SetToMax(gfx::Size(1, 1));
  43. if (page_size.height() / page_size.width() > kPdfMaxAspectRatio)
  44. return gfx::Size(kPdfPageMinDimension, kPdfPageMaxDimension);
  45. if (page_size.width() / page_size.height() > kPdfMaxAspectRatio)
  46. return gfx::Size(kPdfPageMaxDimension, kPdfPageMinDimension);
  47. return page_size;
  48. }
  49. // Calculate the size of a thumbnail image in device pixels using `page_size` in
  50. // any units and `device_pixel_ratio`.
  51. gfx::Size CalculateBestFitSize(const gfx::Size& page_size,
  52. float device_pixel_ratio) {
  53. gfx::Size safe_page_size = LimitAspectRatio(page_size);
  54. // Return the larger of the unrotated and rotated sizes to over-sample the PDF
  55. // page so that the thumbnail looks good in different orientations.
  56. float scale_portrait =
  57. static_cast<float>(kMaxWidthPortraitPx) /
  58. std::min(safe_page_size.width(), safe_page_size.height());
  59. float scale_landscape =
  60. static_cast<float>(kMaxWidthLandscapePx) /
  61. std::max(safe_page_size.width(), safe_page_size.height());
  62. float scale = std::max(scale_portrait, scale_landscape) * device_pixel_ratio;
  63. // Using gfx::ScaleToFlooredSize() is fine because `scale` will not yield an
  64. // empty size unless `device_pixel_ratio` is very small (close to 0).
  65. // However, `device_pixel_ratio` support is limited to between 0.25 and 2.
  66. gfx::Size scaled_size = gfx::ScaleToFlooredSize(safe_page_size, scale);
  67. if (scaled_size.GetCheckedArea().ValueOrDefault(kMaxThumbnailPixels + 1) >
  68. kMaxThumbnailPixels) {
  69. // Recalculate `scale` to accommodate pixel size limit such that:
  70. // (scale * safe_page_size.width()) * (scale * safe_page_size.height()) ==
  71. // kMaxThumbnailPixels;
  72. scale = std::sqrt(static_cast<float>(kMaxThumbnailPixels) /
  73. safe_page_size.width() / safe_page_size.height());
  74. return gfx::ScaleToFlooredSize(safe_page_size, scale);
  75. }
  76. return scaled_size;
  77. }
  78. int CalculateStride(int width) {
  79. base::CheckedNumeric<size_t> stride = kImageColorChannels;
  80. stride *= width;
  81. return stride.ValueOrDie<int>();
  82. }
  83. size_t CalculateImageDataSize(int stride, int height) {
  84. base::CheckedNumeric<int> size = stride;
  85. size *= height;
  86. return size.ValueOrDie<size_t>();
  87. }
  88. } // namespace
  89. Thumbnail::Thumbnail(const gfx::Size& page_size, float device_pixel_ratio)
  90. : device_pixel_ratio_(base::clamp(device_pixel_ratio,
  91. kMinDevicePixelRatio,
  92. kMaxDevicePixelRatio)),
  93. image_size_(CalculateBestFitSize(page_size, device_pixel_ratio_)),
  94. stride_(CalculateStride(image_size_.width())),
  95. image_data_(CalculateImageDataSize(stride(), image_size().height())) {
  96. DCHECK(!image_data_.empty());
  97. }
  98. Thumbnail::Thumbnail(Thumbnail&& other) = default;
  99. Thumbnail& Thumbnail::operator=(Thumbnail&& other) = default;
  100. Thumbnail::~Thumbnail() = default;
  101. base::Value::BlobStorage& Thumbnail::GetImageData() {
  102. DCHECK(!image_data_.empty());
  103. return image_data_;
  104. }
  105. base::Value::BlobStorage Thumbnail::TakeData() {
  106. DCHECK(!image_data_.empty());
  107. return std::move(image_data_);
  108. }
  109. } // namespace chrome_pdf