blob: a9df2eeeeddf5bce3da398f0eaa51edd1166113d [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "skia/public/mojom/bitmap_skbitmap_mojom_traits.h"
#include "base/ranges/algorithm.h"
#include "third_party/skia/include/core/SkPixelRef.h"
namespace mojo {
namespace {
// Maximum reasonable width and height. We don't try to deserialize bitmaps
// bigger than these dimensions.
// These limits are fairly large to accommodate images from the largest possible
// canvas.
constexpr int kMaxWidth = 64 * 1024;
constexpr int kMaxHeight = 64 * 1024;
// A custom SkPixelRef subclass to wrap a BigBuffer storing the pixel data.
class BigBufferPixelRef final : public SkPixelRef {
public:
BigBufferPixelRef(mojo_base::BigBuffer buffer,
int width,
int height,
int row_bytes)
: SkPixelRef(width, height, buffer.data(), row_bytes),
buffer_(std::move(buffer)) {}
~BigBufferPixelRef() override = default;
private:
mojo_base::BigBuffer buffer_;
};
bool CreateSkBitmapForPixelData(SkBitmap* b,
const SkImageInfo& image_info,
base::span<const uint8_t> pixel_data) {
// Ensure width and height are reasonable.
if (image_info.width() > kMaxWidth || image_info.height() > kMaxHeight)
return false;
// We require incoming bitmaps to be tightly packed by specifying the
// rowBytes() as minRowBytes(). Then we compare the number of bytes against
// `pixel_data.size()` later to verify the actual data is tightly packed.
if (!b->tryAllocPixels(image_info, image_info.minRowBytes()))
return false;
// If the image is empty, return success after setting the image info.
if (image_info.width() == 0 || image_info.height() == 0)
return true;
// If these don't match then the number of bytes sent does not match what the
// rest of the mojom said there should be.
if (pixel_data.size() != b->computeByteSize())
return false;
// Implementation note: This copy is important from a security perspective as
// it provides the recipient of the SkBitmap with a stable copy of the data.
// The sender could otherwise continue modifying the shared memory buffer
// underlying the BigBuffer instance.
base::ranges::copy(pixel_data, static_cast<uint8_t*>(b->getPixels()));
b->notifyPixelsChanged();
return true;
}
} // namespace
// static
mojo_base::BigBufferView StructTraits<skia::mojom::BitmapN32DataView,
SkBitmap>::pixel_data(const SkBitmap& b) {
CHECK_EQ(b.rowBytes(), b.info().minRowBytes());
return mojo_base::BigBufferView(base::make_span(
static_cast<uint8_t*>(b.getPixels()), b.computeByteSize()));
}
// static
bool StructTraits<skia::mojom::BitmapN32DataView, SkBitmap>::Read(
skia::mojom::BitmapN32DataView data,
SkBitmap* b) {
SkImageInfo image_info;
if (!data.ReadImageInfo(&image_info))
return false;
mojo_base::BigBufferView pixel_data_view;
if (!data.ReadPixelData(&pixel_data_view))
return false;
return CreateSkBitmapForPixelData(b, std::move(image_info),
pixel_data_view.data());
}
// static
mojo_base::BigBufferView
StructTraits<skia::mojom::BitmapWithArbitraryBppDataView, SkBitmap>::pixel_data(
const SkBitmap& b) {
CHECK_EQ(b.rowBytes(), b.info().minRowBytes());
return mojo_base::BigBufferView(base::make_span(
static_cast<uint8_t*>(b.getPixels()), b.computeByteSize()));
}
// static
bool StructTraits<skia::mojom::BitmapWithArbitraryBppDataView, SkBitmap>::Read(
skia::mojom::BitmapWithArbitraryBppDataView data,
SkBitmap* b) {
SkImageInfo image_info;
if (!data.ReadImageInfo(&image_info))
return false;
mojo_base::BigBufferView pixel_data_view;
if (!data.ReadPixelData(&pixel_data_view))
return false;
return CreateSkBitmapForPixelData(b, std::move(image_info),
pixel_data_view.data());
}
// static
mojo_base::BigBufferView
StructTraits<skia::mojom::BitmapMappedFromTrustedProcessDataView,
SkBitmap>::pixel_data(const SkBitmap& b) {
CHECK_EQ(b.rowBytes(), b.info().minRowBytes());
return mojo_base::BigBufferView(base::make_span(
static_cast<uint8_t*>(b.getPixels()), b.computeByteSize()));
}
// static
bool StructTraits<
skia::mojom::BitmapMappedFromTrustedProcessDataView,
SkBitmap>::Read(skia::mojom::BitmapMappedFromTrustedProcessDataView data,
SkBitmap* b) {
SkImageInfo image_info;
if (!data.ReadImageInfo(&image_info))
return false;
// Ensure width and height are reasonable.
if (image_info.width() > kMaxWidth || image_info.height() > kMaxHeight)
return false;
// If the image is empty, return success after setting the image info.
if (image_info.width() == 0 || image_info.height() == 0)
return b->tryAllocPixels(image_info);
// Otherwise, set a custom PixelRef to retain the BigBuffer. This avoids
// making another copy of the pixel data.
mojo_base::BigBufferView pixel_data_view;
if (!data.ReadPixelData(&pixel_data_view))
return false;
// We require incoming bitmaps to be tightly packed by specifying the
// rowBytes() as minRowBytes(). Then we compare the number of bytes against
// `pixel_data_view.data().size()` later to verify the actual data is tightly
// packed.
if (!b->setInfo(image_info, image_info.minRowBytes()))
return false;
// If these don't match then the number of bytes sent does not match what the
// rest of the mojom said there should be.
if (b->computeByteSize() != pixel_data_view.data().size())
return false;
// Allow the resultant SkBitmap to refer to the given BigBuffer. Note, the
// sender could continue modifying the pixels of the buffer, which could be a
// security concern for some applications. The trade-off is performance.
b->setPixelRef(
sk_make_sp<BigBufferPixelRef>(
mojo_base::BigBufferView::ToBigBuffer(std::move(pixel_data_view)),
image_info.width(), image_info.height(), image_info.minRowBytes()),
0, 0);
return true;
}
// static
base::span<const uint8_t>
StructTraits<skia::mojom::InlineBitmapDataView, SkBitmap>::pixel_data(
const SkBitmap& b) {
CHECK_EQ(b.rowBytes(), b.info().minRowBytes());
return base::make_span(static_cast<uint8_t*>(b.getPixels()),
b.computeByteSize());
}
// static
bool StructTraits<skia::mojom::InlineBitmapDataView, SkBitmap>::Read(
skia::mojom::InlineBitmapDataView data,
SkBitmap* b) {
SkImageInfo image_info;
if (!data.ReadImageInfo(&image_info))
return false;
mojo::ArrayDataView<uint8_t> pixel_data_view;
data.GetPixelDataDataView(&pixel_data_view);
base::span<const uint8_t> pixel_data_bytes(pixel_data_view.data(),
pixel_data_view.size());
return CreateSkBitmapForPixelData(b, std::move(image_info),
std::move(pixel_data_bytes));
}
} // namespace mojo