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
|