blob: edea91610d84d275db01498377a1989a7bde8e02 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "lib/image-format/image_format.h"
#include <fbl/algorithm.h>
#include <zircon/assert.h>
#include <map>
#include <set>
namespace {
// There are two aspects of the ColorSpace and PixelFormat that we care about:
// * bits-per-sample - bits per primary sample (R, G, B, or Y)
// * RGB vs. YUV - whether the system supports the ColorSpace or PixelFormat
// representing RGB data or YUV data. Any given ColorSpace only supports
// one or the other. Currently any given PixelFormat only supports one or
// the other and this isn't likely to change.
// While we could just list all the ColorSpace(s) that each PixelFormat could
// plausibly support, expressing in terms of bits-per-sample and RGB vs. YUV is
// perhaps easier to grok.
enum ColorType {
kColorType_NONE,
kColorType_RGB,
kColorType_YUV
};
struct SamplingInfo {
std::set<uint32_t> possible_bits_per_sample;
ColorType color_type;
};
const std::map<fuchsia_sysmem_ColorSpaceType, SamplingInfo> kColorSpaceSamplingInfo = {
{fuchsia_sysmem_ColorSpaceType_SRGB, {{8, 10, 12, 16}, kColorType_RGB}},
{fuchsia_sysmem_ColorSpaceType_REC601_NTSC, {{8, 10}, kColorType_YUV}},
{fuchsia_sysmem_ColorSpaceType_REC601_NTSC_FULL_RANGE, {{8, 10}, kColorType_YUV}},
{fuchsia_sysmem_ColorSpaceType_REC601_PAL, {{8, 10}, kColorType_YUV}},
{fuchsia_sysmem_ColorSpaceType_REC601_PAL_FULL_RANGE, {{8, 10}, kColorType_YUV}},
{fuchsia_sysmem_ColorSpaceType_REC709, {{8, 10}, kColorType_YUV}},
{fuchsia_sysmem_ColorSpaceType_REC2020, {{10, 12}, kColorType_YUV}},
{fuchsia_sysmem_ColorSpaceType_REC2100, {{10, 12}, kColorType_YUV}},
};
const std::map<fuchsia_sysmem_PixelFormatType, SamplingInfo> kPixelFormatSamplingInfo = {
{fuchsia_sysmem_PixelFormatType_R8G8B8A8, {{8}, kColorType_RGB}},
{fuchsia_sysmem_PixelFormatType_BGRA32, {{8}, kColorType_RGB}},
{fuchsia_sysmem_PixelFormatType_I420, {{8}, kColorType_YUV}},
{fuchsia_sysmem_PixelFormatType_M420, {{8}, kColorType_YUV}},
{fuchsia_sysmem_PixelFormatType_NV12, {{8}, kColorType_YUV}},
{fuchsia_sysmem_PixelFormatType_YUY2, {{8}, kColorType_YUV}},
// 8 bits RGB when uncompressed - in this context, MJPEG is essentially
// pretending to be uncompressed.
{fuchsia_sysmem_PixelFormatType_MJPEG, {{8}, kColorType_RGB}},
{fuchsia_sysmem_PixelFormatType_YV12, {{8}, kColorType_YUV}},
};
class ImageFormatSet {
public:
virtual bool IsSupported(const fuchsia_sysmem_PixelFormat* pixel_format) const = 0;
virtual uint64_t
ImageFormatImageSize(const fuchsia_sysmem_ImageFormat_2* image_format) const = 0;
};
class IntelTiledFormats : public ImageFormatSet {
public:
bool IsSupported(const fuchsia_sysmem_PixelFormat* pixel_format) const override {
if (!pixel_format->has_format_modifier)
return false;
if (pixel_format->type != fuchsia_sysmem_PixelFormatType_R8G8B8A8 &&
pixel_format->type != fuchsia_sysmem_PixelFormatType_BGRA32) {
return false;
}
switch (pixel_format->format_modifier.value) {
case fuchsia_sysmem_FORMAT_MODIFIER_INTEL_I915_X_TILED:
case fuchsia_sysmem_FORMAT_MODIFIER_INTEL_I915_Y_TILED:
case fuchsia_sysmem_FORMAT_MODIFIER_INTEL_I915_YF_TILED:
return true;
default:
return false;
}
}
uint64_t ImageFormatImageSize(const fuchsia_sysmem_ImageFormat_2* image_format) const override {
// See
// https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-skl-vol05-memory_views.pdf
constexpr uint32_t kIntelTileByteSize = 4096;
constexpr uint32_t kIntelYTilePixelWidth = 32;
constexpr uint32_t kIntelYTileHeight = 4096 / (kIntelYTilePixelWidth * 4);
constexpr uint32_t kIntelXTilePixelWidth = 128;
constexpr uint32_t kIntelXTileHeight = 4096 / (kIntelXTilePixelWidth * 4);
constexpr uint32_t kIntelYFTilePixelWidth = 32; // For a 4 byte per component format
constexpr uint32_t kIntelYFTileHeight = 4096 / (kIntelYFTilePixelWidth * 4);
ZX_DEBUG_ASSERT(IsSupported(&image_format->pixel_format));
switch (image_format->pixel_format.format_modifier.value) {
case fuchsia_sysmem_FORMAT_MODIFIER_INTEL_I915_X_TILED:
return fbl::round_up(image_format->coded_width, kIntelXTilePixelWidth) /
kIntelXTilePixelWidth *
fbl::round_up(image_format->coded_height, kIntelXTileHeight) /
kIntelXTileHeight * kIntelTileByteSize;
case fuchsia_sysmem_FORMAT_MODIFIER_INTEL_I915_Y_TILED:
return fbl::round_up(image_format->coded_width, kIntelYTilePixelWidth) /
kIntelYTilePixelWidth *
fbl::round_up(image_format->coded_height, kIntelYTileHeight) /
kIntelYTileHeight * kIntelTileByteSize;
case fuchsia_sysmem_FORMAT_MODIFIER_INTEL_I915_YF_TILED:
return fbl::round_up(image_format->coded_width, kIntelYFTilePixelWidth) /
kIntelYFTilePixelWidth *
fbl::round_up(image_format->coded_height, kIntelYFTileHeight) /
kIntelYFTileHeight * kIntelTileByteSize;
default:
return 0u;
}
}
};
class AfbcFormats : public ImageFormatSet {
public:
bool IsSupported(const fuchsia_sysmem_PixelFormat* pixel_format) const override {
if (!pixel_format->has_format_modifier)
return false;
if (pixel_format->type != fuchsia_sysmem_PixelFormatType_R8G8B8A8 &&
pixel_format->type != fuchsia_sysmem_PixelFormatType_BGRA32) {
return false;
}
switch (pixel_format->format_modifier.value) {
case fuchsia_sysmem_FORMAT_MODIFIER_ARM_AFBC_16x16:
case fuchsia_sysmem_FORMAT_MODIFIER_ARM_AFBC_32x8:
return true;
default:
return false;
}
}
uint64_t ImageFormatImageSize(const fuchsia_sysmem_ImageFormat_2* image_format) const override {
// See
// https://android.googlesource.com/device/linaro/hikey/+/android-o-preview-3/gralloc960/alloc_device.cpp
constexpr uint32_t kAfbcBodyAlignment = 1024u;
ZX_DEBUG_ASSERT(IsSupported(&image_format->pixel_format));
uint32_t block_width;
uint32_t block_height;
switch (image_format->pixel_format.format_modifier.value) {
case fuchsia_sysmem_FORMAT_MODIFIER_ARM_AFBC_16x16:
block_width = 16;
block_height = 16;
break;
case fuchsia_sysmem_FORMAT_MODIFIER_ARM_AFBC_32x8:
block_width = 32;
block_height = 8;
break;
default:
return 0;
}
ZX_DEBUG_ASSERT(image_format->pixel_format.type ==
fuchsia_sysmem_PixelFormatType_R8G8B8A8 ||
image_format->pixel_format.type == fuchsia_sysmem_PixelFormatType_BGRA32);
constexpr uint32_t kBytesPerPixel = 4;
constexpr uint32_t kBytesPerBlockHeader = 16;
uint64_t block_count = fbl::round_up(image_format->coded_width, block_width) / block_width *
fbl::round_up(image_format->coded_height, block_height) /
block_height;
return block_count * block_width * block_height * kBytesPerPixel +
fbl::round_up(block_count * kBytesPerBlockHeader, kAfbcBodyAlignment);
}
};
class LinearFormats : public ImageFormatSet {
bool IsSupported(const fuchsia_sysmem_PixelFormat* pixel_format) const override {
if (pixel_format->has_format_modifier)
return false;
switch (pixel_format->type) {
case fuchsia_sysmem_PixelFormatType_INVALID:
case fuchsia_sysmem_PixelFormatType_MJPEG:
return false;
case fuchsia_sysmem_PixelFormatType_R8G8B8A8:
case fuchsia_sysmem_PixelFormatType_BGRA32:
case fuchsia_sysmem_PixelFormatType_I420:
case fuchsia_sysmem_PixelFormatType_M420:
case fuchsia_sysmem_PixelFormatType_NV12:
case fuchsia_sysmem_PixelFormatType_YUY2:
case fuchsia_sysmem_PixelFormatType_YV12:
return true;
}
return false;
}
uint64_t ImageFormatImageSize(const fuchsia_sysmem_ImageFormat_2* image_format) const override {
ZX_DEBUG_ASSERT(IsSupported(&image_format->pixel_format));
uint64_t coded_height = image_format->coded_height;
uint64_t bytes_per_row = image_format->bytes_per_row;
switch (image_format->pixel_format.type) {
case fuchsia_sysmem_PixelFormatType_R8G8B8A8:
case fuchsia_sysmem_PixelFormatType_BGRA32:
return coded_height * bytes_per_row;
case fuchsia_sysmem_PixelFormatType_I420:
return coded_height * bytes_per_row * 3 / 2;
case fuchsia_sysmem_PixelFormatType_M420:
return coded_height * bytes_per_row * 3 / 2;
case fuchsia_sysmem_PixelFormatType_NV12:
return coded_height * bytes_per_row * 3 / 2;
case fuchsia_sysmem_PixelFormatType_YUY2:
return coded_height * bytes_per_row;
case fuchsia_sysmem_PixelFormatType_YV12:
return coded_height * bytes_per_row * 3 / 2;
default:
return 0u;
}
}
};
constexpr LinearFormats kLinearFormats;
constexpr IntelTiledFormats kIntelFormats;
constexpr AfbcFormats kAfbcFormats;
constexpr const ImageFormatSet* kImageFormats[] = {
&kLinearFormats, &kIntelFormats, &kAfbcFormats,
};
} // namespace
bool ImageFormatIsPixelFormatEqual(const fuchsia_sysmem_PixelFormat& a, const fuchsia_sysmem_PixelFormat& b) {
return
a.type == b.type &&
// !has_format_modifier is for consistency with making format_modifier
// optional in future.
a.has_format_modifier == b.has_format_modifier &&
// Must be 0 if !has_format_modifier.
a.format_modifier.value == b.format_modifier.value;
}
bool ImageFormatIsSupportedColorSpaceForPixelFormat(const fuchsia_sysmem_ColorSpace& color_space, const fuchsia_sysmem_PixelFormat& pixel_format) {
// Ignore pixel format modifier - assume it has already been checked.
auto color_space_sampling_info_iter = kColorSpaceSamplingInfo.find(color_space.type);
if (color_space_sampling_info_iter == kColorSpaceSamplingInfo.end()) {
return false;
}
auto pixel_format_sampling_info_iter = kPixelFormatSamplingInfo.find(pixel_format.type);
if (pixel_format_sampling_info_iter == kPixelFormatSamplingInfo.end()) {
return false;
}
const SamplingInfo& color_space_sampling_info = color_space_sampling_info_iter->second;
const SamplingInfo& pixel_format_sampling_info = pixel_format_sampling_info_iter->second;
if (color_space_sampling_info.color_type != pixel_format_sampling_info.color_type) {
return false;
}
bool is_bits_per_sample_match_found = false;
for (uint32_t bits_per_sample : color_space_sampling_info.possible_bits_per_sample) {
auto pixel_format_bits_per_sample_iter = pixel_format_sampling_info.possible_bits_per_sample.find(bits_per_sample);
if (pixel_format_bits_per_sample_iter != pixel_format_sampling_info.possible_bits_per_sample.end()) {
is_bits_per_sample_match_found = true;
break;
}
}
if (!is_bits_per_sample_match_found) {
return false;
}
return true;
}
bool ImageFormatIsSupported(const fuchsia_sysmem_PixelFormat* pixel_format) {
for (auto& format_set : kImageFormats) {
if (format_set->IsSupported(pixel_format))
return true;
}
return false;
}
// Overall bits per pixel, across all pixel data in the whole image.
uint32_t ImageFormatBitsPerPixel(const fuchsia_sysmem_PixelFormat* pixel_format) {
ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format));
switch (pixel_format->type) {
case fuchsia_sysmem_PixelFormatType_INVALID:
case fuchsia_sysmem_PixelFormatType_MJPEG:
// impossible; checked previously.
ZX_DEBUG_ASSERT(false);
return 0u;
case fuchsia_sysmem_PixelFormatType_R8G8B8A8:
return 4u * 8u;
case fuchsia_sysmem_PixelFormatType_BGRA32:
return 4u * 8u;
case fuchsia_sysmem_PixelFormatType_I420:
return 12u;
case fuchsia_sysmem_PixelFormatType_M420:
return 12u;
case fuchsia_sysmem_PixelFormatType_NV12:
return 12u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u * 8u;
case fuchsia_sysmem_PixelFormatType_YV12:
return 12u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;
}
uint32_t ImageFormatStrideBytesPerWidthPixel(
const fuchsia_sysmem_PixelFormat* pixel_format) {
ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format));
switch (pixel_format->type) {
case fuchsia_sysmem_PixelFormatType_INVALID:
case fuchsia_sysmem_PixelFormatType_MJPEG:
// impossible; checked previously.
ZX_DEBUG_ASSERT(false);
return 0u;
case fuchsia_sysmem_PixelFormatType_R8G8B8A8:
return 4u;
case fuchsia_sysmem_PixelFormatType_BGRA32:
return 4u;
case fuchsia_sysmem_PixelFormatType_I420:
return 1u;
case fuchsia_sysmem_PixelFormatType_M420:
return 1u;
case fuchsia_sysmem_PixelFormatType_NV12:
return 1u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u;
case fuchsia_sysmem_PixelFormatType_YV12:
return 1u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;
}
uint64_t ImageFormatImageSize(const fuchsia_sysmem_ImageFormat_2* image_format) {
for (auto& format_set : kImageFormats) {
if (format_set->IsSupported(&image_format->pixel_format))
return format_set->ImageFormatImageSize(image_format);
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(image_format->pixel_format.type));
return 0;
}
uint32_t ImageFormatCodedWidthMinDivisor(
const fuchsia_sysmem_PixelFormat* pixel_format) {
ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format));
switch (pixel_format->type) {
case fuchsia_sysmem_PixelFormatType_INVALID:
case fuchsia_sysmem_PixelFormatType_MJPEG:
// impossible; checked previously.
ZX_DEBUG_ASSERT(false);
return 0u;
case fuchsia_sysmem_PixelFormatType_R8G8B8A8:
return 1u;
case fuchsia_sysmem_PixelFormatType_BGRA32:
return 1u;
case fuchsia_sysmem_PixelFormatType_I420:
return 2u;
case fuchsia_sysmem_PixelFormatType_M420:
return 2u;
case fuchsia_sysmem_PixelFormatType_NV12:
return 2u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u;
case fuchsia_sysmem_PixelFormatType_YV12:
return 2u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;
}
uint32_t ImageFormatCodedHeightMinDivisor(
const fuchsia_sysmem_PixelFormat* pixel_format) {
ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format));
switch (pixel_format->type) {
case fuchsia_sysmem_PixelFormatType_INVALID:
case fuchsia_sysmem_PixelFormatType_MJPEG:
// impossible; checked previously.
ZX_DEBUG_ASSERT(false);
return 0u;
case fuchsia_sysmem_PixelFormatType_R8G8B8A8:
return 1u;
case fuchsia_sysmem_PixelFormatType_BGRA32:
return 1u;
case fuchsia_sysmem_PixelFormatType_I420:
return 2u;
case fuchsia_sysmem_PixelFormatType_M420:
return 2u;
case fuchsia_sysmem_PixelFormatType_NV12:
return 2u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u;
case fuchsia_sysmem_PixelFormatType_YV12:
return 2u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;
}
uint32_t ImageFormatSampleAlignment(
const fuchsia_sysmem_PixelFormat* pixel_format) {
ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format));
switch (pixel_format->type) {
case fuchsia_sysmem_PixelFormatType_INVALID:
case fuchsia_sysmem_PixelFormatType_MJPEG:
// impossible; checked previously.
ZX_DEBUG_ASSERT(false);
return 0u;
case fuchsia_sysmem_PixelFormatType_R8G8B8A8:
return 4u;
case fuchsia_sysmem_PixelFormatType_BGRA32:
return 4u;
case fuchsia_sysmem_PixelFormatType_I420:
return 2u;
case fuchsia_sysmem_PixelFormatType_M420:
return 2u;
case fuchsia_sysmem_PixelFormatType_NV12:
return 2u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u;
case fuchsia_sysmem_PixelFormatType_YV12:
return 2u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;
}
bool ImageFormatMinimumRowBytes(const fuchsia_sysmem_ImageFormatConstraints* constraints,
uint32_t width, uint32_t* minimum_row_bytes_out) {
// Bytes per row is not well-defined for tiled types.
ZX_DEBUG_ASSERT(!constraints->pixel_format.has_format_modifier);
if (width < constraints->min_coded_width || width > constraints->max_coded_width) {
return false;
}
*minimum_row_bytes_out = fbl::round_up(
fbl::max(ImageFormatStrideBytesPerWidthPixel(&constraints->pixel_format) * width,
constraints->min_bytes_per_row),
constraints->bytes_per_row_divisor);
ZX_ASSERT(*minimum_row_bytes_out <= constraints->max_bytes_per_row);
return true;
}