File: jpeg_codec.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (168 lines) | stat: -rw-r--r-- 5,425 bytes parent folder | download | duplicates (5)
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/codec/jpeg_codec.h"

#include <climits>
#include <memory>
#include <optional>

#include "base/notreached.h"
#include "third_party/skia/include/codec/SkJpegDecoder.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/encode/SkJpegEncoder.h"
#include "ui/gfx/codec/vector_wstream.h"

namespace gfx {

// Encoder ---------------------------------------------------------------------

std::optional<std::vector<uint8_t>> JPEGCodec::Encode(
    const SkPixmap& input,
    int quality,
    SkJpegEncoder::Downsample downsample,
    const SkData* xmp_metadata) {
  std::vector<uint8_t> output;
  VectorWStream dst(&output);

  SkJpegEncoder::Options options;
  options.fQuality = quality;
  options.fDownsample = downsample;
  if (xmp_metadata) {
    options.xmpMetadata = xmp_metadata;
  }

  if (!SkJpegEncoder::Encode(&dst, input, options)) {
    return std::nullopt;
  }

  return output;
}

std::optional<std::vector<uint8_t>> JPEGCodec::Encode(const SkPixmap& input,
                                                      int quality) {
  return Encode(input, quality, SkJpegEncoder::Downsample::k420);
}

std::optional<std::vector<uint8_t>> JPEGCodec::Encode(const SkBitmap& src,
                                                      int quality) {
  SkPixmap pixmap;
  if (!src.peekPixels(&pixmap)) {
    return std::nullopt;
  }

  return JPEGCodec::Encode(pixmap, quality);
}

// Decoder --------------------------------------------------------------------

namespace {

struct PreparationOutput {
  std::unique_ptr<SkCodec> codec;
  SkImageInfo image_info;
};

std::optional<PreparationOutput> PrepareForJPEGDecode(
    base::span<const uint8_t> input,
    SkColorType color_type) {
  PreparationOutput output;

  // We only support 8-bit RGBA and BGRA color types.
  CHECK(color_type == kRGBA_8888_SkColorType ||
        color_type == kBGRA_8888_SkColorType)
      << "Invalid pixel format " << color_type;

  // Parse the input stream with the JPEG decoder, yielding a SkCodec.
  auto stream = std::make_unique<SkMemoryStream>(input.data(), input.size(),
                                                 /*copyData=*/false);
  SkCodec::Result result;
  output.codec = SkJpegDecoder::Decode(std::move(stream), &result);
  if (!output.codec || result != SkCodec::kSuccess) {
    return std::nullopt;
  }

  // Reject images that would exceed INT_MAX bytes.
  SkISize size = output.codec->dimensions();
  constexpr int kBytesPerPixel = 4;
  if (size.area() >= (INT_MAX / kBytesPerPixel)) {
    return std::nullopt;
  }

  // The fuzzer is able to make astronomically large bitmaps (30000x30000) from
  // very small inputs. Images this large can take several seconds to decode. In
  // a build instrumented for fuzzing, this time can balloon to over a minute.
  // To avoid timeouts, we limit the fuzzer to 16 million pixels. We don't
  // reject very wide or very tall images, as long as the image is reasonably
  // small on the other axis.
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
  constexpr int kFuzzerPixelLimit = 4000 * 4000;
  if (size.area() >= kFuzzerPixelLimit) {
    return std::nullopt;
  }
#endif

  // Create an SkImageInfo matching our JPEG's dimensions and color type.
  output.image_info = SkImageInfo::Make(size, color_type, kOpaque_SkAlphaType);

  return output;
}

}  // namespace

bool JPEGCodec::Decode(const uint8_t* input,
                       size_t input_size,
                       SkColorType color_type,
                       std::vector<uint8_t>* output,
                       int* w,
                       int* h) {
  std::optional<PreparationOutput> preparation_output = PrepareForJPEGDecode(
      UNSAFE_BUFFERS(base::span(input, input_size)), color_type);
  if (!preparation_output) {
    return false;
  }

  *w = preparation_output->image_info.width();
  *h = preparation_output->image_info.height();

  // Decode the pixels into the `output` vector.
  output->resize(preparation_output->image_info.computeMinByteSize());
  SkCodec::Result result = preparation_output->codec->getPixels(
      preparation_output->image_info, &output->front(),
      preparation_output->image_info.minRowBytes());
  return result == SkCodec::kSuccess;
}

// static
SkBitmap JPEGCodec::Decode(base::span<const uint8_t> input) {
  constexpr SkColorType kFormat =  // Parens around (0) solve dead-code warning.
      (SK_R32_SHIFT == (0))   ? kRGBA_8888_SkColorType
      : (SK_B32_SHIFT == (0)) ? kBGRA_8888_SkColorType
                              : kUnknown_SkColorType;

  std::optional<PreparationOutput> preparation_output =
      PrepareForJPEGDecode(input, kFormat);
  if (!preparation_output) {
    return SkBitmap();
  }

  // Allocate pixel storage for the decoded JPEG.
  SkBitmap bitmap;
  if (!bitmap.tryAllocN32Pixels(preparation_output->image_info.width(),
                                preparation_output->image_info.height())) {
    return SkBitmap();
  }

  // Decode the image pixels directly onto an SkBitmap.
  SkCodec::Result result =
      preparation_output->codec->getPixels(bitmap.pixmap());
  if (result == SkCodec::kSuccess) {
    return bitmap;
  } else {
    return SkBitmap();
  }
}

}  // namespace gfx