File: thumbnail.cc

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (136 lines) | stat: -rw-r--r-- 5,026 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "pdf/ui/thumbnail.h"

#include <stddef.h>

#include <algorithm>
#include <cmath>
#include <utility>

#include "base/check.h"
#include "base/check_op.h"
#include "base/numerics/checked_math.h"
#include "base/values.h"
#include "ui/gfx/geometry/size.h"

namespace chrome_pdf {

namespace {

constexpr float kMinDevicePixelRatio = 0.25;
constexpr float kMaxDevicePixelRatio = 2;

constexpr int kImageColorChannels = 4;

// TODO(crbug.com/702993): Reevaluate the thumbnail size cap when the PDF
// component migrates off of PPAPI.
// The maximum thumbnail area is essentially arbitrary, but the value was chosen
// considering the fact that when sending array buffers through PPAPI, if the
// size of the data is over 256KiB, it gets sent using shared memory instead of
// IPC. Thumbnail sizes are capped at 255KiB to avoid the 256KiB threshold for
// images and their metadata, such as size.
constexpr int kMaxThumbnailPixels = 255 * 1024 / kImageColorChannels;

// Maximum CSS dimensions are set to match UX specifications.
// These constants should be kept in sync with `PORTRAIT_WIDTH` and
// `LANDSCAPE_WIDTH` in
// chrome/browser/resources/pdf/elements/viewer-thumbnail.ts.
constexpr int kMaxWidthPortraitPx = 108;
constexpr int kMaxWidthLandscapePx = 140;

// PDF page size limits in default user space units, as defined by PDF 1.7 Annex
// C.2, "Architectural limits".
constexpr int kPdfPageMinDimension = 3;
constexpr int kPdfPageMaxDimension = 14400;
constexpr int kPdfMaxAspectRatio = kPdfPageMaxDimension / kPdfPageMinDimension;

// Limit the proportions within PDF limits to handle pathological PDF pages.
gfx::Size LimitAspectRatio(gfx::Size page_size) {
  // Bump up any lengths of 0 to 1.
  page_size.SetToMax(gfx::Size(1, 1));

  if (page_size.height() / page_size.width() > kPdfMaxAspectRatio)
    return gfx::Size(kPdfPageMinDimension, kPdfPageMaxDimension);
  if (page_size.width() / page_size.height() > kPdfMaxAspectRatio)
    return gfx::Size(kPdfPageMaxDimension, kPdfPageMinDimension);

  return page_size;
}

// Calculate the size of a thumbnail image in device pixels using `page_size` in
// any units and `device_pixel_ratio`.
gfx::Size CalculateBestFitSize(const gfx::Size& page_size,
                               float device_pixel_ratio) {
  gfx::Size safe_page_size = LimitAspectRatio(page_size);

  // Return the larger of the unrotated and rotated sizes to over-sample the PDF
  // page so that the thumbnail looks good in different orientations.
  float scale_portrait =
      static_cast<float>(kMaxWidthPortraitPx) /
      std::min(safe_page_size.width(), safe_page_size.height());
  float scale_landscape =
      static_cast<float>(kMaxWidthLandscapePx) /
      std::max(safe_page_size.width(), safe_page_size.height());
  float scale = std::max(scale_portrait, scale_landscape) * device_pixel_ratio;

  // Using gfx::ScaleToFlooredSize() is fine because `scale` will not yield an
  // empty size unless `device_pixel_ratio` is very small (close to 0).
  // However, `device_pixel_ratio` support is limited to between 0.25 and 2.
  gfx::Size scaled_size = gfx::ScaleToFlooredSize(safe_page_size, scale);
  if (scaled_size.GetCheckedArea().ValueOrDefault(kMaxThumbnailPixels + 1) >
      kMaxThumbnailPixels) {
    // Recalculate `scale` to accommodate pixel size limit such that:
    // (scale * safe_page_size.width()) * (scale * safe_page_size.height()) ==
    //     kMaxThumbnailPixels;
    scale = std::sqrt(static_cast<float>(kMaxThumbnailPixels) /
                      safe_page_size.width() / safe_page_size.height());
    return gfx::ScaleToFlooredSize(safe_page_size, scale);
  }

  return scaled_size;
}

int CalculateStride(int width) {
  base::CheckedNumeric<size_t> stride = kImageColorChannels;
  stride *= width;
  return stride.ValueOrDie<int>();
}

size_t CalculateImageDataSize(int stride, int height) {
  base::CheckedNumeric<int> size = stride;
  size *= height;
  return size.ValueOrDie<size_t>();
}

}  // namespace

Thumbnail::Thumbnail(const gfx::Size& page_size, float device_pixel_ratio)
    : device_pixel_ratio_(std::clamp(device_pixel_ratio,
                                     kMinDevicePixelRatio,
                                     kMaxDevicePixelRatio)),
      image_size_(CalculateBestFitSize(page_size, device_pixel_ratio_)),
      stride_(CalculateStride(image_size_.width())),
      image_data_(CalculateImageDataSize(stride(), image_size().height())) {
  DCHECK(!image_data_.empty());
}

Thumbnail::Thumbnail(Thumbnail&& other) = default;

Thumbnail& Thumbnail::operator=(Thumbnail&& other) = default;

Thumbnail::~Thumbnail() = default;

base::Value::BlobStorage& Thumbnail::GetImageData() {
  DCHECK(!image_data_.empty());
  return image_data_;
}

base::Value::BlobStorage Thumbnail::TakeData() {
  DCHECK(!image_data_.empty());
  return std::move(image_data_);
}

}  // namespace chrome_pdf