blob: 4432b3e9a6c88568fe60ea4165f99b42816cd981 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gfx/buffer_format_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/numerics/safe_math.h"
namespace gfx {
namespace {
const BufferFormat kBufferFormats[] = {BufferFormat::ATC,
BufferFormat::ATCIA,
BufferFormat::DXT1,
BufferFormat::DXT5,
BufferFormat::ETC1,
BufferFormat::R_8,
BufferFormat::RG_88,
BufferFormat::BGR_565,
BufferFormat::RGBA_4444,
BufferFormat::RGBX_8888,
BufferFormat::RGBA_8888,
BufferFormat::BGRX_8888,
BufferFormat::BGRA_8888,
BufferFormat::UYVY_422,
BufferFormat::YUV_420_BIPLANAR,
BufferFormat::YVU_420};
static_assert(arraysize(kBufferFormats) ==
(static_cast<int>(BufferFormat::LAST) + 1),
"BufferFormat::LAST must be last value of kBufferFormats");
bool RowSizeForBufferFormatChecked(
size_t width, BufferFormat format, size_t plane, size_t* size_in_bytes) {
base::CheckedNumeric<size_t> checked_size = width;
switch (format) {
case BufferFormat::ATCIA:
case BufferFormat::DXT5:
DCHECK_EQ(0u, plane);
*size_in_bytes = width;
return true;
case BufferFormat::ATC:
case BufferFormat::DXT1:
case BufferFormat::ETC1:
DCHECK_EQ(0u, plane);
DCHECK_EQ(0u, width % 2);
*size_in_bytes = width / 2;
return true;
case BufferFormat::R_8:
checked_size += 3;
if (!checked_size.IsValid())
return false;
*size_in_bytes = (checked_size & ~0x3).ValueOrDie();
return true;
case BufferFormat::RG_88:
case BufferFormat::BGR_565:
case BufferFormat::RGBA_4444:
case BufferFormat::UYVY_422:
checked_size *= 2;
checked_size += 3;
if (!checked_size.IsValid())
return false;
*size_in_bytes = (checked_size & ~0x3).ValueOrDie();
return true;
case BufferFormat::BGRX_8888:
case BufferFormat::RGBX_8888:
case BufferFormat::RGBA_8888:
case BufferFormat::BGRA_8888:
checked_size *= 4;
if (!checked_size.IsValid())
return false;
*size_in_bytes = checked_size.ValueOrDie();
return true;
case BufferFormat::YVU_420:
DCHECK_EQ(0u, width % 2);
*size_in_bytes = width / SubsamplingFactorForBufferFormat(format, plane);
return true;
case BufferFormat::YUV_420_BIPLANAR:
DCHECK_EQ(width % 2, 0u);
*size_in_bytes = width;
return true;
}
NOTREACHED();
return false;
}
} // namespace
std::vector<BufferFormat> GetBufferFormatsForTesting() {
return std::vector<BufferFormat>(kBufferFormats,
kBufferFormats + arraysize(kBufferFormats));
}
size_t NumberOfPlanesForBufferFormat(BufferFormat format) {
switch (format) {
case BufferFormat::ATC:
case BufferFormat::ATCIA:
case BufferFormat::DXT1:
case BufferFormat::DXT5:
case BufferFormat::ETC1:
case BufferFormat::R_8:
case BufferFormat::RG_88:
case BufferFormat::BGR_565:
case BufferFormat::RGBA_4444:
case BufferFormat::RGBX_8888:
case BufferFormat::RGBA_8888:
case BufferFormat::BGRX_8888:
case BufferFormat::BGRA_8888:
case BufferFormat::UYVY_422:
return 1;
case BufferFormat::YUV_420_BIPLANAR:
return 2;
case BufferFormat::YVU_420:
return 3;
}
NOTREACHED();
return 0;
}
size_t SubsamplingFactorForBufferFormat(BufferFormat format, size_t plane) {
switch (format) {
case BufferFormat::ATC:
case BufferFormat::ATCIA:
case BufferFormat::DXT1:
case BufferFormat::DXT5:
case BufferFormat::ETC1:
case BufferFormat::R_8:
case BufferFormat::RG_88:
case BufferFormat::BGR_565:
case BufferFormat::RGBA_4444:
case BufferFormat::RGBX_8888:
case BufferFormat::RGBA_8888:
case BufferFormat::BGRX_8888:
case BufferFormat::BGRA_8888:
case BufferFormat::UYVY_422:
return 1;
case BufferFormat::YVU_420: {
static size_t factor[] = {1, 2, 2};
DCHECK_LT(static_cast<size_t>(plane), arraysize(factor));
return factor[plane];
}
case BufferFormat::YUV_420_BIPLANAR: {
static size_t factor[] = {1, 2};
DCHECK_LT(static_cast<size_t>(plane), arraysize(factor));
return factor[plane];
}
}
NOTREACHED();
return 0;
}
size_t RowSizeForBufferFormat(size_t width, BufferFormat format, size_t plane) {
size_t row_size = 0;
bool valid = RowSizeForBufferFormatChecked(width, format, plane, &row_size);
DCHECK(valid);
return row_size;
}
size_t BufferSizeForBufferFormat(const Size& size, BufferFormat format) {
size_t buffer_size = 0;
bool valid = BufferSizeForBufferFormatChecked(size, format, &buffer_size);
DCHECK(valid);
return buffer_size;
}
bool BufferSizeForBufferFormatChecked(const Size& size,
BufferFormat format,
size_t* size_in_bytes) {
base::CheckedNumeric<size_t> checked_size = 0;
size_t num_planes = NumberOfPlanesForBufferFormat(format);
for (size_t i = 0; i < num_planes; ++i) {
size_t row_size = 0;
if (!RowSizeForBufferFormatChecked(size.width(), format, i, &row_size))
return false;
base::CheckedNumeric<size_t> checked_plane_size = row_size;
checked_plane_size *= size.height() /
SubsamplingFactorForBufferFormat(format, i);
if (!checked_plane_size.IsValid())
return false;
checked_size += checked_plane_size.ValueOrDie();
if (!checked_size.IsValid())
return false;
}
*size_in_bytes = checked_size.ValueOrDie();
return true;
}
size_t BufferOffsetForBufferFormat(const Size& size,
BufferFormat format,
size_t plane) {
DCHECK_LT(plane, gfx::NumberOfPlanesForBufferFormat(format));
switch (format) {
case BufferFormat::ATC:
case BufferFormat::ATCIA:
case BufferFormat::DXT1:
case BufferFormat::DXT5:
case BufferFormat::ETC1:
case BufferFormat::R_8:
case BufferFormat::RG_88:
case BufferFormat::BGR_565:
case BufferFormat::RGBA_4444:
case BufferFormat::RGBX_8888:
case BufferFormat::RGBA_8888:
case BufferFormat::BGRX_8888:
case BufferFormat::BGRA_8888:
case BufferFormat::UYVY_422:
return 0;
case BufferFormat::YVU_420: {
static size_t offset_in_2x2_sub_sampling_sizes[] = {0, 4, 5};
DCHECK_LT(plane, arraysize(offset_in_2x2_sub_sampling_sizes));
return offset_in_2x2_sub_sampling_sizes[plane] *
(size.width() / 2 + size.height() / 2);
}
case gfx::BufferFormat::YUV_420_BIPLANAR: {
static size_t offset_in_2x2_sub_sampling_sizes[] = {0, 4};
DCHECK_LT(plane, arraysize(offset_in_2x2_sub_sampling_sizes));
return offset_in_2x2_sub_sampling_sizes[plane] *
(size.width() / 2 + size.height() / 2);
}
}
NOTREACHED();
return 0;
}
} // namespace gfx