| // 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 <fuchsia/sysmem/c/fidl.h> |
| #include <fuchsia/sysmem2/llcpp/fidl.h> |
| #include <lib/sysmem-version/sysmem-version.h> |
| #include <zircon/assert.h> |
| #include <zircon/pixelformat.h> |
| |
| #include <algorithm> |
| #include <map> |
| #include <set> |
| |
| #include <fbl/algorithm.h> |
| |
| namespace { |
| |
| using ColorSpace = fuchsia_sysmem2::wire::ColorSpace; |
| using ColorSpaceType = fuchsia_sysmem2::wire::ColorSpaceType; |
| using ImageFormat = fuchsia_sysmem2::wire::ImageFormat; |
| using ImageFormatConstraints = fuchsia_sysmem2::wire::ImageFormatConstraints; |
| using PixelFormat = fuchsia_sysmem2::wire::PixelFormat; |
| using PixelFormatType = fuchsia_sysmem2::wire::PixelFormatType; |
| |
| // 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<ColorSpaceType, SamplingInfo> kColorSpaceSamplingInfo = { |
| {ColorSpaceType::kSrgb, {{8, 10, 12, 16}, kColorType_RGB}}, |
| {ColorSpaceType::kRec601Ntsc, {{8, 10}, kColorType_YUV}}, |
| {ColorSpaceType::kRec601NtscFullRange, {{8, 10}, kColorType_YUV}}, |
| {ColorSpaceType::kRec601Pal, {{8, 10}, kColorType_YUV}}, |
| {ColorSpaceType::kRec601PalFullRange, {{8, 10}, kColorType_YUV}}, |
| {ColorSpaceType::kRec709, {{8, 10}, kColorType_YUV}}, |
| {ColorSpaceType::kRec2020, {{10, 12}, kColorType_YUV}}, |
| {ColorSpaceType::kRec2100, {{10, 12}, kColorType_YUV}}, |
| }; |
| const std::map<PixelFormatType, SamplingInfo> kPixelFormatSamplingInfo = { |
| {PixelFormatType::kR8G8B8A8, {{8}, kColorType_RGB}}, |
| {PixelFormatType::kBgra32, {{8}, kColorType_RGB}}, |
| {PixelFormatType::kI420, {{8}, kColorType_YUV}}, |
| {PixelFormatType::kM420, {{8}, kColorType_YUV}}, |
| {PixelFormatType::kNv12, {{8}, kColorType_YUV}}, |
| {PixelFormatType::kYuy2, {{8}, kColorType_YUV}}, |
| // 8 bits RGB when uncompressed - in this context, MJPEG is essentially |
| // pretending to be uncompressed. |
| {PixelFormatType::kMjpeg, {{8}, kColorType_RGB}}, |
| {PixelFormatType::kYv12, {{8}, kColorType_YUV}}, |
| {PixelFormatType::kBgr24, {{8}, kColorType_RGB}}, |
| |
| // These use the same colorspaces as regular 8-bit-per-component formats |
| {PixelFormatType::kRgb565, {{8}, kColorType_RGB}}, |
| {PixelFormatType::kRgb332, {{8}, kColorType_RGB}}, |
| {PixelFormatType::kRgb2220, {{8}, kColorType_RGB}}, |
| // Expands to RGB |
| {PixelFormatType::kL8, {{8}, kColorType_RGB}}, |
| {PixelFormatType::kR8, {{8}, kColorType_RGB}}, |
| {PixelFormatType::kR8G8, {{8}, kColorType_RGB}}, |
| {PixelFormatType::kA2B10G10R10, {{8}, kColorType_RGB}}, |
| {PixelFormatType::kA2R10G10B10, {{8}, kColorType_RGB}}, |
| }; |
| |
| constexpr uint32_t kTransactionEliminationAlignment = 64; |
| // The transaction elimination buffer is always reported as plane 3. |
| constexpr uint32_t kTransactionEliminationPlane = 3; |
| |
| static uint64_t arm_transaction_elimination_row_size(uint32_t width) { |
| uint32_t kTileSize = 32; |
| uint32_t kBytesPerTilePerRow = 16; |
| uint32_t width_in_tiles = fbl::round_up(width, kTileSize) / kTileSize; |
| return fbl::round_up(width_in_tiles * kBytesPerTilePerRow, kTransactionEliminationAlignment); |
| } |
| |
| static uint64_t arm_transaction_elimination_buffer_size(uint64_t start, uint32_t width, |
| uint32_t height) { |
| uint32_t kTileSize = 32; |
| uint32_t end = start; |
| end = fbl::round_up(end, kTransactionEliminationAlignment); |
| uint32_t kHeaderSize = kTransactionEliminationAlignment; |
| end += kHeaderSize; |
| uint32_t height_in_tiles = fbl::round_up(height, kTileSize) / kTileSize; |
| end += arm_transaction_elimination_row_size(width) * 2 * height_in_tiles; |
| return end - start; |
| } |
| |
| class ImageFormatSet { |
| public: |
| virtual const char* Name() const = 0; |
| virtual bool IsSupported(const PixelFormat& pixel_format) const = 0; |
| virtual uint64_t ImageFormatImageSize(const ImageFormat& image_format) const = 0; |
| virtual bool ImageFormatPlaneByteOffset(const ImageFormat& image_format, uint32_t plane, |
| uint64_t* offset_out) const = 0; |
| virtual bool ImageFormatPlaneRowBytes(const ImageFormat& image_format, uint32_t plane, |
| uint32_t* row_bytes_out) const = 0; |
| }; |
| |
| class IntelTiledFormats : public ImageFormatSet { |
| public: |
| const char* Name() const override { return "IntelTiledFormats"; } |
| |
| bool IsSupported(const PixelFormat& pixel_format) const override { |
| if (!pixel_format.has_type()) |
| return false; |
| if (!pixel_format.has_format_modifier_value()) |
| return false; |
| if (pixel_format.type() != PixelFormatType::kR8G8B8A8 && |
| pixel_format.type() != PixelFormatType::kBgra32) { |
| return false; |
| } |
| switch (pixel_format.format_modifier_value()) { |
| case fuchsia_sysmem2::wire::kFormatModifierIntelI915XTiled: |
| case fuchsia_sysmem2::wire::kFormatModifierIntelI915YTiled: |
| case fuchsia_sysmem2::wire::kFormatModifierIntelI915YfTiled: |
| return true; |
| default: |
| return false; |
| } |
| } |
| uint64_t ImageFormatImageSize(const ImageFormat& 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_sysmem2::wire::kFormatModifierIntelI915XTiled: |
| return fbl::round_up(image_format.coded_width(), kIntelXTilePixelWidth) / |
| kIntelXTilePixelWidth * |
| fbl::round_up(image_format.coded_height(), kIntelXTileHeight) / kIntelXTileHeight * |
| kIntelTileByteSize; |
| |
| case fuchsia_sysmem2::wire::kFormatModifierIntelI915YTiled: |
| return fbl::round_up(image_format.coded_width(), kIntelYTilePixelWidth) / |
| kIntelYTilePixelWidth * |
| fbl::round_up(image_format.coded_height(), kIntelYTileHeight) / kIntelYTileHeight * |
| kIntelTileByteSize; |
| |
| case fuchsia_sysmem2::wire::kFormatModifierIntelI915YfTiled: |
| return fbl::round_up(image_format.coded_width(), kIntelYFTilePixelWidth) / |
| kIntelYFTilePixelWidth * |
| fbl::round_up(image_format.coded_height(), kIntelYFTileHeight) / kIntelYFTileHeight * |
| kIntelTileByteSize; |
| default: |
| return 0u; |
| } |
| } |
| bool ImageFormatPlaneByteOffset(const ImageFormat& image_format, uint32_t plane, |
| uint64_t* offset_out) const override { |
| ZX_DEBUG_ASSERT(IsSupported(image_format.pixel_format())); |
| if (plane == 0) { |
| *offset_out = 0; |
| return true; |
| } |
| return false; |
| } |
| bool ImageFormatPlaneRowBytes(const ImageFormat& image_format, uint32_t plane, |
| uint32_t* row_bytes_out) const override { |
| if (plane == 0) { |
| *row_bytes_out = 0; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| }; |
| class AfbcFormats : public ImageFormatSet { |
| public: |
| const char* Name() const override { return "AfbcFormats"; } |
| |
| static constexpr uint64_t kAfbcModifierMask = |
| fuchsia_sysmem2::wire::kFormatModifierArmTeBit | |
| fuchsia_sysmem2::wire::kFormatModifierArmSplitBlockBit | |
| fuchsia_sysmem2::wire::kFormatModifierArmSparseBit | |
| fuchsia_sysmem2::wire::kFormatModifierArmYuvBit | |
| fuchsia_sysmem2::wire::kFormatModifierArmBchBit | |
| fuchsia_sysmem2::wire::kFormatModifierArmTiledHeaderBit; |
| bool IsSupported(const PixelFormat& pixel_format) const override { |
| if (!pixel_format.has_format_modifier_value()) |
| return false; |
| if (!pixel_format.has_type()) |
| return false; |
| if (pixel_format.type() != PixelFormatType::kR8G8B8A8 && |
| pixel_format.type() != PixelFormatType::kBgra32) { |
| return false; |
| } |
| switch (pixel_format.format_modifier_value() & ~kAfbcModifierMask) { |
| case fuchsia_sysmem2::wire::kFormatModifierArmAfbc16X16: |
| case fuchsia_sysmem2::wire::kFormatModifierArmAfbc32X8: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // Calculate the size of the Raw AFBC image without a transaction elimination buffer. |
| uint64_t NonTESize(const ImageFormat& image_format) const { |
| // See |
| // https://android.googlesource.com/device/linaro/hikey/+/android-o-preview-3/gralloc960/alloc_device.cpp |
| constexpr uint32_t kAfbcBodyAlignment = 1024u; |
| constexpr uint32_t kTiledAfbcBodyAlignment = 4096u; |
| |
| ZX_DEBUG_ASSERT(image_format.has_pixel_format()); |
| ZX_DEBUG_ASSERT(IsSupported(image_format.pixel_format())); |
| uint32_t block_width; |
| uint32_t block_height; |
| uint32_t width_alignment; |
| uint32_t height_alignment; |
| bool tiled_header = image_format.pixel_format().format_modifier_value() & |
| fuchsia_sysmem2::wire::kFormatModifierArmTiledHeaderBit; |
| |
| switch (image_format.pixel_format().format_modifier_value() & ~kAfbcModifierMask) { |
| case fuchsia_sysmem2::wire::kFormatModifierArmAfbc16X16: |
| block_width = 16; |
| block_height = 16; |
| if (!tiled_header) { |
| width_alignment = block_width; |
| height_alignment = block_height; |
| } else { |
| width_alignment = 128; |
| height_alignment = 128; |
| } |
| break; |
| |
| case fuchsia_sysmem2::wire::kFormatModifierArmAfbc32X8: |
| block_width = 32; |
| block_height = 8; |
| if (!tiled_header) { |
| width_alignment = block_width; |
| height_alignment = block_height; |
| } else { |
| width_alignment = 256; |
| height_alignment = 64; |
| } |
| break; |
| default: |
| return 0; |
| } |
| |
| uint32_t body_alignment = tiled_header ? kTiledAfbcBodyAlignment : kAfbcBodyAlignment; |
| |
| ZX_DEBUG_ASSERT(image_format.pixel_format().has_type()); |
| ZX_DEBUG_ASSERT(image_format.pixel_format().type() == PixelFormatType::kR8G8B8A8 || |
| image_format.pixel_format().type() == PixelFormatType::kBgra32); |
| constexpr uint32_t kBytesPerPixel = 4; |
| constexpr uint32_t kBytesPerBlockHeader = 16; |
| |
| ZX_DEBUG_ASSERT(image_format.has_coded_width()); |
| ZX_DEBUG_ASSERT(image_format.has_coded_height()); |
| uint64_t block_count = |
| fbl::round_up(image_format.coded_width(), width_alignment) / block_width * |
| fbl::round_up(image_format.coded_height(), height_alignment) / block_height; |
| return block_count * block_width * block_height * kBytesPerPixel + |
| fbl::round_up(block_count * kBytesPerBlockHeader, body_alignment); |
| } |
| |
| uint64_t ImageFormatImageSize(const ImageFormat& image_format) const override { |
| uint64_t size = NonTESize(image_format); |
| if (image_format.pixel_format().format_modifier_value() & |
| fuchsia_sysmem2::wire::kFormatModifierArmTeBit) { |
| size += arm_transaction_elimination_buffer_size(size, image_format.coded_width(), |
| image_format.coded_height()); |
| } |
| |
| return size; |
| } |
| |
| bool ImageFormatPlaneByteOffset(const ImageFormat& image_format, uint32_t plane, |
| uint64_t* offset_out) const override { |
| ZX_DEBUG_ASSERT(IsSupported(image_format.pixel_format())); |
| if (plane == 0) { |
| *offset_out = 0; |
| return true; |
| } else if (plane == kTransactionEliminationPlane) { |
| *offset_out = fbl::round_up(NonTESize(image_format), kTransactionEliminationAlignment); |
| return true; |
| } |
| return false; |
| } |
| bool ImageFormatPlaneRowBytes(const ImageFormat& image_format, uint32_t plane, |
| uint32_t* row_bytes_out) const override { |
| if (plane == 0) { |
| *row_bytes_out = 0; |
| return true; |
| } else if (plane == kTransactionEliminationPlane) { |
| *row_bytes_out = arm_transaction_elimination_row_size(image_format.coded_width()); |
| return true; |
| } |
| return false; |
| } |
| }; |
| |
| static uint64_t linear_size(uint32_t coded_height, uint32_t bytes_per_row, PixelFormatType type) { |
| switch (type) { |
| case PixelFormatType::kR8G8B8A8: |
| case PixelFormatType::kBgra32: |
| case PixelFormatType::kBgr24: |
| case PixelFormatType::kRgb565: |
| case PixelFormatType::kRgb332: |
| case PixelFormatType::kRgb2220: |
| case PixelFormatType::kL8: |
| case PixelFormatType::kR8: |
| case PixelFormatType::kR8G8: |
| case PixelFormatType::kA2B10G10R10: |
| case PixelFormatType::kA2R10G10B10: |
| return coded_height * bytes_per_row; |
| case PixelFormatType::kI420: |
| return coded_height * bytes_per_row * 3 / 2; |
| case PixelFormatType::kM420: |
| return coded_height * bytes_per_row * 3 / 2; |
| case PixelFormatType::kNv12: |
| return coded_height * bytes_per_row * 3 / 2; |
| case PixelFormatType::kYuy2: |
| return coded_height * bytes_per_row; |
| case PixelFormatType::kYv12: |
| return coded_height * bytes_per_row * 3 / 2; |
| default: |
| return 0u; |
| } |
| } |
| |
| class LinearFormats : public ImageFormatSet { |
| public: |
| const char* Name() const override { return "LinearFormats"; } |
| |
| bool IsSupported(const PixelFormat& pixel_format) const override { |
| if (pixel_format.has_format_modifier_value() && |
| pixel_format.format_modifier_value() != fuchsia_sysmem2::wire::kFormatModifierLinear) { |
| return false; |
| } |
| ZX_DEBUG_ASSERT(pixel_format.has_type()); |
| switch (pixel_format.type()) { |
| case PixelFormatType::kInvalid: |
| case PixelFormatType::kMjpeg: |
| return false; |
| case PixelFormatType::kR8G8B8A8: |
| case PixelFormatType::kBgra32: |
| case PixelFormatType::kBgr24: |
| case PixelFormatType::kI420: |
| case PixelFormatType::kM420: |
| case PixelFormatType::kNv12: |
| case PixelFormatType::kYuy2: |
| case PixelFormatType::kYv12: |
| case PixelFormatType::kRgb565: |
| case PixelFormatType::kRgb332: |
| case PixelFormatType::kRgb2220: |
| case PixelFormatType::kL8: |
| case PixelFormatType::kR8: |
| case PixelFormatType::kR8G8: |
| case PixelFormatType::kA2B10G10R10: |
| case PixelFormatType::kA2R10G10B10: |
| return true; |
| } |
| return false; |
| } |
| |
| uint64_t ImageFormatImageSize(const ImageFormat& image_format) const override { |
| ZX_DEBUG_ASSERT(image_format.has_pixel_format()); |
| ZX_DEBUG_ASSERT(image_format.pixel_format().has_type()); |
| ZX_DEBUG_ASSERT(IsSupported(image_format.pixel_format())); |
| ZX_DEBUG_ASSERT(image_format.has_coded_height()); |
| ZX_DEBUG_ASSERT(image_format.has_bytes_per_row()); |
| uint32_t coded_height = image_format.has_coded_height() ? image_format.coded_height() : 0; |
| uint32_t bytes_per_row = image_format.has_bytes_per_row() ? image_format.bytes_per_row() : 0; |
| return linear_size(coded_height, bytes_per_row, image_format.pixel_format().type()); |
| } |
| |
| bool ImageFormatPlaneByteOffset(const ImageFormat& image_format, uint32_t plane, |
| uint64_t* offset_out) const override { |
| if (plane == 0) { |
| *offset_out = 0; |
| return true; |
| } |
| if (plane == 1) { |
| switch (image_format.pixel_format().type()) { |
| case PixelFormatType::kNv12: |
| case PixelFormatType::kI420: |
| case PixelFormatType::kYv12: |
| *offset_out = image_format.coded_height() * image_format.bytes_per_row(); |
| return true; |
| default: |
| return false; |
| } |
| } |
| if (plane == 2) { |
| switch (image_format.pixel_format().type()) { |
| case PixelFormatType::kI420: |
| case PixelFormatType::kYv12: |
| *offset_out = image_format.coded_height() * image_format.bytes_per_row(); |
| *offset_out += image_format.coded_height() / 2 * image_format.bytes_per_row() / 2; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool ImageFormatPlaneRowBytes(const ImageFormat& image_format, uint32_t plane, |
| uint32_t* row_bytes_out) const override { |
| if (plane == 0) { |
| *row_bytes_out = image_format.bytes_per_row(); |
| return true; |
| } else if (plane == 1) { |
| switch (image_format.pixel_format().type()) { |
| case PixelFormatType::kNv12: |
| *row_bytes_out = image_format.bytes_per_row(); |
| return true; |
| case PixelFormatType::kI420: |
| case PixelFormatType::kYv12: |
| *row_bytes_out = image_format.bytes_per_row() / 2; |
| return true; |
| default: |
| return false; |
| } |
| } else if (plane == 2) { |
| switch (image_format.pixel_format().type()) { |
| case PixelFormatType::kI420: |
| case PixelFormatType::kYv12: |
| *row_bytes_out = image_format.bytes_per_row() / 2; |
| return true; |
| default: |
| return false; |
| } |
| } |
| return false; |
| } |
| }; |
| |
| constexpr LinearFormats kLinearFormats; |
| |
| class GoldfishFormats : public ImageFormatSet { |
| public: |
| const char* Name() const override { return "GoldfishFormats"; } |
| |
| bool IsSupported(const PixelFormat& pixel_format) const override { |
| if (!pixel_format.has_type()) { |
| return false; |
| } |
| if (!pixel_format.has_format_modifier_value()) { |
| return false; |
| } |
| switch (pixel_format.format_modifier_value()) { |
| case fuchsia_sysmem2::wire::kFormatModifierGoogleGoldfishOptimal: |
| return true; |
| default: |
| return false; |
| } |
| } |
| uint64_t ImageFormatImageSize(const ImageFormat& image_format) const override { |
| ZX_DEBUG_ASSERT(image_format.has_pixel_format()); |
| ZX_DEBUG_ASSERT(image_format.pixel_format().has_type()); |
| ZX_DEBUG_ASSERT(IsSupported(image_format.pixel_format())); |
| ZX_DEBUG_ASSERT(image_format.has_coded_height()); |
| ZX_DEBUG_ASSERT(image_format.has_bytes_per_row()); |
| uint32_t coded_height = image_format.has_coded_height() ? image_format.coded_height() : 0; |
| uint32_t bytes_per_row = image_format.has_bytes_per_row() ? image_format.bytes_per_row() : 0; |
| return linear_size(coded_height, bytes_per_row, image_format.pixel_format().type()); |
| } |
| bool ImageFormatPlaneByteOffset(const ImageFormat& image_format, uint32_t plane, |
| uint64_t* offset_out) const override { |
| ZX_DEBUG_ASSERT(IsSupported(image_format.pixel_format())); |
| if (plane == 0) { |
| *offset_out = 0; |
| return true; |
| } |
| return false; |
| } |
| bool ImageFormatPlaneRowBytes(const ImageFormat& image_format, uint32_t plane, |
| uint32_t* row_bytes_out) const override { |
| if (plane == 0) { |
| *row_bytes_out = image_format.bytes_per_row(); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| }; |
| |
| class ArmTELinearFormats : public ImageFormatSet { |
| public: |
| const char* Name() const override { return "ArmTELinearFormats"; } |
| |
| bool IsSupported(const fuchsia_sysmem2::wire::PixelFormat& pixel_format) const override { |
| if (!pixel_format.has_format_modifier_value()) { |
| return false; |
| } |
| if (pixel_format.format_modifier_value() != fuchsia_sysmem2::wire::kFormatModifierArmLinearTe) |
| return false; |
| switch (pixel_format.type()) { |
| case PixelFormatType::kInvalid: |
| case PixelFormatType::kMjpeg: |
| return false; |
| case PixelFormatType::kR8G8B8A8: |
| case PixelFormatType::kBgra32: |
| case PixelFormatType::kBgr24: |
| case PixelFormatType::kI420: |
| case PixelFormatType::kM420: |
| case PixelFormatType::kNv12: |
| case PixelFormatType::kYuy2: |
| case PixelFormatType::kYv12: |
| case PixelFormatType::kRgb565: |
| case PixelFormatType::kRgb332: |
| case PixelFormatType::kRgb2220: |
| case PixelFormatType::kL8: |
| case PixelFormatType::kR8: |
| case PixelFormatType::kR8G8: |
| case PixelFormatType::kA2B10G10R10: |
| case PixelFormatType::kA2R10G10B10: |
| return true; |
| } |
| return false; |
| } |
| |
| uint64_t ImageFormatImageSize( |
| const fuchsia_sysmem2::wire::ImageFormat& image_format) const override { |
| ZX_DEBUG_ASSERT(IsSupported(image_format.pixel_format())); |
| ZX_DEBUG_ASSERT(image_format.has_coded_width()); |
| ZX_DEBUG_ASSERT(image_format.has_coded_height()); |
| ZX_DEBUG_ASSERT(image_format.has_bytes_per_row()); |
| uint32_t coded_width = image_format.has_coded_width() ? image_format.coded_width() : 0; |
| uint32_t coded_height = image_format.has_coded_height() ? image_format.coded_height() : 0; |
| uint32_t bytes_per_row = image_format.has_bytes_per_row() ? image_format.bytes_per_row() : 0; |
| uint64_t size = linear_size(coded_height, bytes_per_row, image_format.pixel_format().type()); |
| uint64_t crc_size = arm_transaction_elimination_buffer_size(size, coded_width, coded_height); |
| return size + crc_size; |
| } |
| |
| bool ImageFormatPlaneByteOffset(const fuchsia_sysmem2::wire::ImageFormat& image_format, |
| |
| uint32_t plane, uint64_t* offset_out) const override { |
| if (plane < kTransactionEliminationPlane) { |
| return kLinearFormats.ImageFormatPlaneByteOffset(image_format, plane, offset_out); |
| } else if (plane == kTransactionEliminationPlane) { |
| ZX_DEBUG_ASSERT(image_format.has_coded_height()); |
| ZX_DEBUG_ASSERT(image_format.has_bytes_per_row()); |
| uint32_t coded_height = image_format.has_coded_height() ? image_format.coded_height() : 0; |
| uint32_t bytes_per_row = image_format.has_bytes_per_row() ? image_format.bytes_per_row() : 0; |
| uint64_t size = linear_size(coded_height, bytes_per_row, image_format.pixel_format().type()); |
| *offset_out = fbl::round_up(size, 64u); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool ImageFormatPlaneRowBytes(const fuchsia_sysmem2::wire::ImageFormat& image_format, |
| uint32_t plane, uint32_t* row_bytes_out) const override { |
| if (plane < kTransactionEliminationPlane) { |
| return kLinearFormats.ImageFormatPlaneRowBytes(image_format, plane, row_bytes_out); |
| } else if (plane == kTransactionEliminationPlane) { |
| *row_bytes_out = arm_transaction_elimination_row_size(image_format.coded_width()); |
| return true; |
| } |
| return false; |
| } |
| }; |
| |
| constexpr IntelTiledFormats kIntelFormats; |
| constexpr AfbcFormats kAfbcFormats; |
| constexpr ArmTELinearFormats kArmTELinearFormats; |
| constexpr GoldfishFormats kGoldfishFormats; |
| |
| constexpr const ImageFormatSet* kImageFormats[] = { |
| &kLinearFormats, &kIntelFormats, &kAfbcFormats, &kArmTELinearFormats, &kGoldfishFormats, |
| }; |
| |
| } // namespace |
| |
| bool ImageFormatIsPixelFormatEqual(const fuchsia_sysmem2::wire::PixelFormat& a, |
| const fuchsia_sysmem2::wire::PixelFormat& b) { |
| if (a.type() != b.type()) { |
| return false; |
| } |
| uint64_t format_modifier_a = a.has_format_modifier_value() |
| ? a.format_modifier_value() |
| : fuchsia_sysmem2::wire::kFormatModifierNone; |
| uint64_t format_modifier_b = b.has_format_modifier_value() |
| ? b.format_modifier_value() |
| : fuchsia_sysmem2::wire::kFormatModifierNone; |
| if (format_modifier_a != format_modifier_b) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool ImageFormatIsPixelFormatEqual(const fuchsia_sysmem::wire::PixelFormat& a_v1, |
| const fuchsia_sysmem::wire::PixelFormat& b_v1) { |
| fidl::FidlAllocator allocator; |
| PixelFormat a = sysmem::V2CopyFromV1PixelFormat(allocator, a_v1); |
| PixelFormat b = sysmem::V2CopyFromV1PixelFormat(allocator, b_v1); |
| return ImageFormatIsPixelFormatEqual(a, b); |
| } |
| |
| bool ImageFormatIsPixelFormatEqual(const fuchsia_sysmem_PixelFormat& a_v1, |
| const fuchsia_sysmem_PixelFormat& b_v1) { |
| fidl::FidlAllocator allocator; |
| PixelFormat a = sysmem::V2CopyFromV1PixelFormat(allocator, a_v1); |
| PixelFormat b = sysmem::V2CopyFromV1PixelFormat(allocator, b_v1); |
| return ImageFormatIsPixelFormatEqual(a, b); |
| } |
| |
| bool ImageFormatIsSupportedColorSpaceForPixelFormat( |
| const fuchsia_sysmem2::wire::ColorSpace& color_space, |
| const fuchsia_sysmem2::wire::PixelFormat& pixel_format) { |
| if (!color_space.has_type()) |
| return false; |
| // 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 ImageFormatIsSupportedColorSpaceForPixelFormat( |
| const fuchsia_sysmem::wire::ColorSpace& color_space_v1, |
| const fuchsia_sysmem::wire::PixelFormat& pixel_format_v1) { |
| fidl::FidlAllocator allocator; |
| ColorSpace color_space = sysmem::V2CopyFromV1ColorSpace(allocator, color_space_v1); |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatIsSupportedColorSpaceForPixelFormat(color_space, pixel_format); |
| } |
| |
| bool ImageFormatIsSupportedColorSpaceForPixelFormat( |
| const fuchsia_sysmem_ColorSpace& color_space_v1, |
| const fuchsia_sysmem_PixelFormat& pixel_format_v1) { |
| fidl::FidlAllocator allocator; |
| ColorSpace color_space = sysmem::V2CopyFromV1ColorSpace(allocator, color_space_v1); |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatIsSupportedColorSpaceForPixelFormat(color_space, pixel_format); |
| } |
| |
| bool ImageFormatIsSupported(const fuchsia_sysmem2::wire::PixelFormat& pixel_format) { |
| for (auto format_set : kImageFormats) { |
| if (format_set->IsSupported(pixel_format)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool ImageFormatIsSupported(const fuchsia_sysmem::wire::PixelFormat& pixel_format_v1) { |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatIsSupported(pixel_format); |
| } |
| |
| bool ImageFormatIsSupported(const fuchsia_sysmem_PixelFormat* pixel_format_v1) { |
| ZX_DEBUG_ASSERT(pixel_format_v1); |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, *pixel_format_v1); |
| return ImageFormatIsSupported(pixel_format); |
| } |
| |
| uint32_t ImageFormatBitsPerPixel(const fuchsia_sysmem2::wire::PixelFormat& pixel_format) { |
| ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format)); |
| switch (pixel_format.type()) { |
| case PixelFormatType::kInvalid: |
| case PixelFormatType::kMjpeg: |
| // impossible; checked previously. |
| ZX_DEBUG_ASSERT(false); |
| return 0u; |
| case PixelFormatType::kR8G8B8A8: |
| return 4u * 8u; |
| case PixelFormatType::kBgra32: |
| return 4u * 8u; |
| case PixelFormatType::kBgr24: |
| return 3u * 8u; |
| case PixelFormatType::kI420: |
| return 12u; |
| case PixelFormatType::kM420: |
| return 12u; |
| case PixelFormatType::kNv12: |
| return 12u; |
| case PixelFormatType::kYuy2: |
| return 2u * 8u; |
| case PixelFormatType::kYv12: |
| return 12u; |
| case PixelFormatType::kRgb565: |
| return 16u; |
| case PixelFormatType::kRgb332: |
| case PixelFormatType::kRgb2220: |
| case PixelFormatType::kL8: |
| case PixelFormatType::kR8: |
| return 8u; |
| case PixelFormatType::kR8G8: |
| return 16u; |
| case PixelFormatType::kA2B10G10R10: |
| case PixelFormatType::kA2R10G10B10: |
| return 2u + 3 * 10u; |
| } |
| ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format.type())); |
| return 0u; |
| } |
| |
| uint32_t ImageFormatBitsPerPixel(const fuchsia_sysmem::wire::PixelFormat& pixel_format_v1) { |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatBitsPerPixel(pixel_format); |
| } |
| |
| // Overall bits per pixel, across all pixel data in the whole image. |
| uint32_t ImageFormatBitsPerPixel(const fuchsia_sysmem_PixelFormat* pixel_format_v1) { |
| ZX_DEBUG_ASSERT(pixel_format_v1); |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, *pixel_format_v1); |
| return ImageFormatBitsPerPixel(pixel_format); |
| } |
| |
| uint32_t ImageFormatStrideBytesPerWidthPixel( |
| const fuchsia_sysmem2::wire::PixelFormat& pixel_format) { |
| ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format)); |
| // This list should match the one in garnet/public/rust/fuchsia-framebuffer/src/sysmem.rs. |
| switch (pixel_format.type()) { |
| case PixelFormatType::kInvalid: |
| case PixelFormatType::kMjpeg: |
| // impossible; checked previously. |
| ZX_DEBUG_ASSERT(false); |
| return 0u; |
| case PixelFormatType::kR8G8B8A8: |
| return 4u; |
| case PixelFormatType::kBgra32: |
| return 4u; |
| case PixelFormatType::kBgr24: |
| return 3u; |
| case PixelFormatType::kI420: |
| return 1u; |
| case PixelFormatType::kM420: |
| return 1u; |
| case PixelFormatType::kNv12: |
| return 1u; |
| case PixelFormatType::kYuy2: |
| return 2u; |
| case PixelFormatType::kYv12: |
| return 1u; |
| case PixelFormatType::kRgb565: |
| return 2u; |
| case PixelFormatType::kRgb332: |
| return 1u; |
| case PixelFormatType::kRgb2220: |
| return 1u; |
| case PixelFormatType::kL8: |
| return 1u; |
| case PixelFormatType::kR8: |
| return 1u; |
| case PixelFormatType::kR8G8: |
| return 2u; |
| case PixelFormatType::kA2B10G10R10: |
| return 4u; |
| case PixelFormatType::kA2R10G10B10: |
| return 4u; |
| } |
| ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format.type())); |
| return 0u; |
| } |
| |
| uint32_t ImageFormatStrideBytesPerWidthPixel( |
| const fuchsia_sysmem::wire::PixelFormat& pixel_format_v1) { |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatStrideBytesPerWidthPixel(pixel_format); |
| } |
| |
| uint32_t ImageFormatStrideBytesPerWidthPixel(const fuchsia_sysmem_PixelFormat* pixel_format_v1) { |
| ZX_DEBUG_ASSERT(pixel_format_v1); |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, *pixel_format_v1); |
| return ImageFormatStrideBytesPerWidthPixel(pixel_format); |
| } |
| |
| uint64_t ImageFormatImageSize(const fuchsia_sysmem2::wire::ImageFormat& image_format) { |
| ZX_DEBUG_ASSERT(image_format.has_pixel_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; |
| } |
| |
| uint64_t ImageFormatImageSize(const fuchsia_sysmem::wire::ImageFormat2& image_format_v1) { |
| fidl::FidlAllocator allocator; |
| ImageFormat image_format = |
| sysmem::V2CopyFromV1ImageFormat(allocator, image_format_v1).take_value(); |
| return ImageFormatImageSize(image_format); |
| } |
| |
| uint64_t ImageFormatImageSize(const fuchsia_sysmem_ImageFormat_2* image_format_v1) { |
| ZX_DEBUG_ASSERT(image_format_v1); |
| fidl::FidlAllocator allocator; |
| ImageFormat image_format = |
| sysmem::V2CopyFromV1ImageFormat(allocator, *image_format_v1).take_value(); |
| return ImageFormatImageSize(image_format); |
| } |
| |
| uint32_t ImageFormatCodedWidthMinDivisor(const fuchsia_sysmem2::wire::PixelFormat& pixel_format) { |
| ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format)); |
| switch (pixel_format.type()) { |
| case PixelFormatType::kInvalid: |
| case PixelFormatType::kMjpeg: |
| // impossible; checked previously. |
| ZX_DEBUG_ASSERT(false); |
| return 0u; |
| case PixelFormatType::kR8G8B8A8: |
| return 1u; |
| case PixelFormatType::kBgra32: |
| return 1u; |
| case PixelFormatType::kBgr24: |
| return 1u; |
| case PixelFormatType::kI420: |
| return 2u; |
| case PixelFormatType::kM420: |
| return 2u; |
| case PixelFormatType::kNv12: |
| return 2u; |
| case PixelFormatType::kYuy2: |
| return 2u; |
| case PixelFormatType::kYv12: |
| return 2u; |
| case PixelFormatType::kRgb565: |
| return 1u; |
| case PixelFormatType::kRgb332: |
| return 1u; |
| case PixelFormatType::kRgb2220: |
| return 1u; |
| case PixelFormatType::kL8: |
| return 1u; |
| case PixelFormatType::kR8: |
| return 1u; |
| case PixelFormatType::kR8G8: |
| return 1u; |
| case PixelFormatType::kA2B10G10R10: |
| return 1u; |
| case PixelFormatType::kA2R10G10B10: |
| return 1u; |
| } |
| ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format.type())); |
| return 0u; |
| } |
| |
| uint32_t ImageFormatCodedWidthMinDivisor(const fuchsia_sysmem::wire::PixelFormat& pixel_format_v1) { |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatCodedWidthMinDivisor(pixel_format); |
| } |
| |
| uint32_t ImageFormatCodedWidthMinDivisor(const fuchsia_sysmem_PixelFormat* pixel_format_v1) { |
| ZX_DEBUG_ASSERT(pixel_format_v1); |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, *pixel_format_v1); |
| return ImageFormatCodedWidthMinDivisor(pixel_format); |
| } |
| |
| uint32_t ImageFormatCodedHeightMinDivisor(const fuchsia_sysmem2::wire::PixelFormat& pixel_format) { |
| ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format)); |
| switch (pixel_format.type()) { |
| case PixelFormatType::kInvalid: |
| case PixelFormatType::kMjpeg: |
| // impossible; checked previously. |
| ZX_DEBUG_ASSERT(false); |
| return 0u; |
| case PixelFormatType::kR8G8B8A8: |
| return 1u; |
| case PixelFormatType::kBgra32: |
| return 1u; |
| case PixelFormatType::kBgr24: |
| return 1u; |
| case PixelFormatType::kI420: |
| return 2u; |
| case PixelFormatType::kM420: |
| return 2u; |
| case PixelFormatType::kNv12: |
| return 2u; |
| case PixelFormatType::kYuy2: |
| return 2u; |
| case PixelFormatType::kYv12: |
| return 2u; |
| case PixelFormatType::kRgb565: |
| return 1u; |
| case PixelFormatType::kRgb332: |
| return 1u; |
| case PixelFormatType::kRgb2220: |
| return 1u; |
| case PixelFormatType::kL8: |
| return 1u; |
| case PixelFormatType::kR8: |
| return 1u; |
| case PixelFormatType::kR8G8: |
| return 1u; |
| case PixelFormatType::kA2B10G10R10: |
| return 1u; |
| case PixelFormatType::kA2R10G10B10: |
| return 1u; |
| } |
| ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format.type())); |
| return 0u; |
| } |
| |
| uint32_t ImageFormatCodedHeightMinDivisor( |
| const fuchsia_sysmem::wire::PixelFormat& pixel_format_v1) { |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatCodedHeightMinDivisor(pixel_format); |
| } |
| |
| uint32_t ImageFormatCodedHeightMinDivisor(const fuchsia_sysmem_PixelFormat* pixel_format_v1) { |
| ZX_DEBUG_ASSERT(pixel_format_v1); |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, *pixel_format_v1); |
| return ImageFormatCodedHeightMinDivisor(pixel_format); |
| } |
| |
| uint32_t ImageFormatSampleAlignment(const fuchsia_sysmem2::wire::PixelFormat& pixel_format) { |
| ZX_DEBUG_ASSERT(ImageFormatIsSupported(pixel_format)); |
| switch (pixel_format.type()) { |
| case PixelFormatType::kInvalid: |
| case PixelFormatType::kMjpeg: |
| // impossible; checked previously. |
| ZX_DEBUG_ASSERT(false); |
| return 0u; |
| case PixelFormatType::kR8G8B8A8: |
| return 4u; |
| case PixelFormatType::kBgra32: |
| return 4u; |
| case PixelFormatType::kBgr24: |
| return 1u; |
| case PixelFormatType::kI420: |
| return 2u; |
| case PixelFormatType::kM420: |
| return 2u; |
| case PixelFormatType::kNv12: |
| return 2u; |
| case PixelFormatType::kYuy2: |
| return 2u; |
| case PixelFormatType::kYv12: |
| return 2u; |
| case PixelFormatType::kRgb565: |
| return 2u; |
| case PixelFormatType::kRgb332: |
| return 1u; |
| case PixelFormatType::kRgb2220: |
| return 1u; |
| case PixelFormatType::kL8: |
| return 1u; |
| case PixelFormatType::kR8: |
| return 1u; |
| case PixelFormatType::kR8G8: |
| return 2u; |
| case PixelFormatType::kA2B10G10R10: |
| return 4u; |
| case PixelFormatType::kA2R10G10B10: |
| return 4u; |
| } |
| ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format.type())); |
| return 0u; |
| } |
| |
| uint32_t ImageFormatSampleAlignment(const fuchsia_sysmem::wire::PixelFormat& pixel_format_v1) { |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatSampleAlignment(pixel_format); |
| } |
| |
| uint32_t ImageFormatSampleAlignment(const fuchsia_sysmem_PixelFormat* pixel_format_v1) { |
| ZX_DEBUG_ASSERT(pixel_format_v1); |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, *pixel_format_v1); |
| return ImageFormatSampleAlignment(pixel_format); |
| } |
| |
| bool ImageFormatMinimumRowBytes(const fuchsia_sysmem2::wire::ImageFormatConstraints& constraints, |
| uint32_t width, uint32_t* minimum_row_bytes_out) { |
| ZX_DEBUG_ASSERT(minimum_row_bytes_out); |
| // Caller must set pixel_format. |
| ZX_DEBUG_ASSERT(constraints.has_pixel_format()); |
| // Bytes per row is not well-defined for tiled types. |
| if (constraints.pixel_format().has_format_modifier_value() && |
| constraints.pixel_format().format_modifier_value() != |
| fuchsia_sysmem2::wire::kFormatModifierLinear && |
| constraints.pixel_format().format_modifier_value() != |
| fuchsia_sysmem2::wire::kFormatModifierArmLinearTe) { |
| return false; |
| } |
| if ((constraints.has_min_coded_width() && width < constraints.min_coded_width()) || |
| (constraints.has_max_coded_width() && width > constraints.max_coded_width())) { |
| return false; |
| } |
| uint32_t constraints_min_bytes_per_row = |
| constraints.has_min_bytes_per_row() ? constraints.min_bytes_per_row() : 0; |
| uint32_t constraints_bytes_per_row_divisor = |
| constraints.has_bytes_per_row_divisor() ? constraints.bytes_per_row_divisor() : 1; |
| // This code should match the code in garnet/public/rust/fuchsia-framebuffer/src/sysmem.rs. |
| *minimum_row_bytes_out = fbl::round_up( |
| std::max(ImageFormatStrideBytesPerWidthPixel(constraints.pixel_format()) * width, |
| constraints_min_bytes_per_row), |
| constraints_bytes_per_row_divisor); |
| if (constraints.has_max_bytes_per_row() && |
| *minimum_row_bytes_out > constraints.max_bytes_per_row()) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool ImageFormatMinimumRowBytes( |
| const fuchsia_sysmem::wire::ImageFormatConstraints& image_format_constraints_v1, uint32_t width, |
| uint32_t* minimum_row_bytes_out) { |
| ZX_DEBUG_ASSERT(minimum_row_bytes_out); |
| fidl::FidlAllocator allocator; |
| ImageFormatConstraints image_format_constraints = |
| sysmem::V2CopyFromV1ImageFormatConstraints(allocator, image_format_constraints_v1) |
| .take_value(); |
| return ImageFormatMinimumRowBytes(image_format_constraints, width, minimum_row_bytes_out); |
| } |
| |
| bool ImageFormatMinimumRowBytes( |
| const fuchsia_sysmem_ImageFormatConstraints* image_format_constraints_v1, uint32_t width, |
| uint32_t* minimum_row_bytes_out) { |
| ZX_DEBUG_ASSERT(image_format_constraints_v1); |
| ZX_DEBUG_ASSERT(minimum_row_bytes_out); |
| fidl::FidlAllocator allocator; |
| ImageFormatConstraints image_format_constraints = |
| sysmem::V2CopyFromV1ImageFormatConstraints(allocator, *image_format_constraints_v1) |
| .take_value(); |
| return ImageFormatMinimumRowBytes(image_format_constraints, width, minimum_row_bytes_out); |
| } |
| |
| bool ImageFormatConvertSysmemToZx(const fuchsia_sysmem2::wire::PixelFormat& pixel_format, |
| zx_pixel_format_t* zx_pixel_format_out) { |
| if (pixel_format.has_format_modifier_value() && |
| (pixel_format.format_modifier_value() != fuchsia_sysmem2::wire::kFormatModifierLinear)) { |
| return false; |
| } |
| switch (pixel_format.type()) { |
| case PixelFormatType::kBgra32: |
| *zx_pixel_format_out = ZX_PIXEL_FORMAT_ARGB_8888; |
| return true; |
| |
| case PixelFormatType::kBgr24: |
| *zx_pixel_format_out = ZX_PIXEL_FORMAT_RGB_888; |
| return true; |
| |
| case PixelFormatType::kRgb565: |
| *zx_pixel_format_out = ZX_PIXEL_FORMAT_RGB_565; |
| return true; |
| |
| case PixelFormatType::kRgb332: |
| *zx_pixel_format_out = ZX_PIXEL_FORMAT_RGB_332; |
| return true; |
| |
| case PixelFormatType::kRgb2220: |
| *zx_pixel_format_out = ZX_PIXEL_FORMAT_RGB_2220; |
| return true; |
| |
| case PixelFormatType::kL8: |
| *zx_pixel_format_out = ZX_PIXEL_FORMAT_MONO_8; |
| return true; |
| |
| case PixelFormatType::kNv12: |
| *zx_pixel_format_out = ZX_PIXEL_FORMAT_NV12; |
| return true; |
| |
| case PixelFormatType::kA2B10G10R10: |
| *zx_pixel_format_out = ZX_PIXEL_FORMAT_ABGR_2_10_10_10; |
| return true; |
| |
| case PixelFormatType::kA2R10G10B10: |
| *zx_pixel_format_out = ZX_PIXEL_FORMAT_ARGB_2_10_10_10; |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool ImageFormatConvertSysmemToZx(const fuchsia_sysmem::wire::PixelFormat& pixel_format_v1, |
| zx_pixel_format_t* zx_pixel_format_out) { |
| ZX_DEBUG_ASSERT(zx_pixel_format_out); |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatConvertSysmemToZx(pixel_format, zx_pixel_format_out); |
| } |
| |
| bool ImageFormatConvertSysmemToZx(const fuchsia_sysmem_PixelFormat* pixel_format_v1, |
| zx_pixel_format_t* zx_pixel_format_out) { |
| ZX_DEBUG_ASSERT(pixel_format_v1); |
| ZX_DEBUG_ASSERT(zx_pixel_format_out); |
| fidl::FidlAllocator allocator; |
| PixelFormat pixel_format = sysmem::V2CopyFromV1PixelFormat(allocator, *pixel_format_v1); |
| return ImageFormatConvertSysmemToZx(pixel_format, zx_pixel_format_out); |
| } |
| |
| fit::result<fuchsia_sysmem2::wire::PixelFormat> ImageFormatConvertZxToSysmem_v2( |
| fidl::AnyAllocator& allocator, zx_pixel_format_t zx_pixel_format) { |
| PixelFormat v2b = PixelFormat(allocator); |
| v2b.set_format_modifier_value(allocator, fuchsia_sysmem2::wire::kFormatModifierLinear); |
| PixelFormatType out_type; |
| switch (zx_pixel_format) { |
| case ZX_PIXEL_FORMAT_RGB_565: |
| out_type = PixelFormatType::kRgb565; |
| break; |
| |
| case ZX_PIXEL_FORMAT_RGB_332: |
| out_type = PixelFormatType::kRgb332; |
| break; |
| |
| case ZX_PIXEL_FORMAT_RGB_2220: |
| out_type = PixelFormatType::kRgb2220; |
| break; |
| |
| case ZX_PIXEL_FORMAT_ARGB_8888: |
| out_type = PixelFormatType::kBgra32; |
| break; |
| |
| case ZX_PIXEL_FORMAT_RGB_x888: |
| // Switch to using alpha. |
| out_type = PixelFormatType::kBgra32; |
| break; |
| |
| case ZX_PIXEL_FORMAT_MONO_8: |
| out_type = PixelFormatType::kL8; |
| break; |
| |
| case ZX_PIXEL_FORMAT_NV12: |
| out_type = PixelFormatType::kNv12; |
| break; |
| |
| case ZX_PIXEL_FORMAT_RGB_888: |
| out_type = PixelFormatType::kBgr24; |
| break; |
| |
| default: |
| return fit::error(); |
| } |
| v2b.set_type(allocator, out_type); |
| return fit::ok(std::move(v2b)); |
| } |
| |
| fit::result<fuchsia_sysmem::wire::PixelFormat> ImageFormatConvertZxToSysmem_v1( |
| fidl::AnyAllocator& allocator, zx_pixel_format_t zx_pixel_format) { |
| auto pixel_format_v2_result = ImageFormatConvertZxToSysmem_v2(allocator, zx_pixel_format); |
| if (!pixel_format_v2_result.is_ok()) { |
| return fit::error(); |
| } |
| auto pixel_format_v2 = pixel_format_v2_result.take_value(); |
| auto pixel_format_v1 = sysmem::V1CopyFromV2PixelFormat(pixel_format_v2); |
| return fit::ok(std::move(pixel_format_v1)); |
| } |
| |
| bool ImageFormatConvertZxToSysmem(zx_pixel_format_t zx_pixel_format, |
| fuchsia_sysmem_PixelFormat* pixel_format_out) { |
| ZX_DEBUG_ASSERT(pixel_format_out); |
| fidl::FidlAllocator allocator; |
| auto pixel_format_v2_result = ImageFormatConvertZxToSysmem_v2(allocator, zx_pixel_format); |
| if (!pixel_format_v2_result.is_ok()) { |
| return false; |
| } |
| auto pixel_format_v2 = pixel_format_v2_result.take_value(); |
| pixel_format_out->type = static_cast<fuchsia_sysmem_PixelFormatType>(pixel_format_v2.type()); |
| pixel_format_out->has_format_modifier = pixel_format_v2.has_format_modifier_value(); |
| pixel_format_out->format_modifier.value = pixel_format_v2.format_modifier_value(); |
| return true; |
| } |
| |
| // TODO(dustingreen): From here down need to be converted to operate on v2 natively similar to |
| // above (merged while 1st sysmem v2 CL was in flight): |
| |
| fit::result<ImageFormat> ImageConstraintsToFormat(fidl::AnyAllocator& allocator, |
| const ImageFormatConstraints& constraints, |
| uint32_t width, uint32_t height) { |
| if ((constraints.has_min_coded_height() && height < constraints.min_coded_height()) || |
| (constraints.has_max_coded_height() && height > constraints.max_coded_height())) { |
| return fit::error(); |
| } |
| if ((constraints.has_min_coded_width() && width < constraints.min_coded_width()) || |
| (constraints.has_max_coded_width() && width > constraints.max_coded_width())) { |
| return fit::error(); |
| } |
| ImageFormat result(allocator); |
| uint32_t minimum_row_bytes; |
| if (ImageFormatMinimumRowBytes(constraints, width, &minimum_row_bytes)) { |
| result.set_bytes_per_row(allocator, minimum_row_bytes); |
| } else { |
| result.set_bytes_per_row(allocator, 0); |
| } |
| result.set_pixel_format(allocator, |
| sysmem::V2ClonePixelFormat(allocator, constraints.pixel_format())); |
| result.set_coded_width(allocator, width); |
| result.set_coded_height(allocator, height); |
| result.set_display_width(allocator, width); |
| result.set_display_height(allocator, height); |
| if (constraints.has_color_spaces() && constraints.color_spaces().count()) { |
| result.set_color_space(allocator, |
| sysmem::V2CloneColorSpace(allocator, constraints.color_spaces()[0])); |
| } |
| // result's has_pixel_aspect_ratio field remains un-set which is equivalent to false |
| return fit::ok(std::move(result)); |
| } |
| |
| fit::result<fuchsia_sysmem::wire::ImageFormat2> ImageConstraintsToFormat( |
| const fuchsia_sysmem::wire::ImageFormatConstraints& image_format_constraints_v1, uint32_t width, |
| uint32_t height) { |
| fidl::FidlAllocator allocator; |
| ImageFormatConstraints image_format_constraints_v2 = |
| sysmem::V2CopyFromV1ImageFormatConstraints(allocator, image_format_constraints_v1) |
| .take_value(); |
| auto v2_out_result = |
| ImageConstraintsToFormat(allocator, image_format_constraints_v2, width, height); |
| if (!v2_out_result.is_ok()) { |
| return fit::error(); |
| } |
| auto v2_out = v2_out_result.take_value(); |
| auto v1_out_result = sysmem::V1CopyFromV2ImageFormat(v2_out); |
| if (!v1_out_result.is_ok()) { |
| return fit::error(); |
| } |
| return fit::ok(v1_out_result.take_value()); |
| } |
| |
| bool ImageConstraintsToFormat( |
| const fuchsia_sysmem_ImageFormatConstraints* image_format_constraints_v1, uint32_t width, |
| uint32_t height, fuchsia_sysmem_ImageFormat_2* image_format_out) { |
| ZX_DEBUG_ASSERT(image_format_constraints_v1); |
| ZX_DEBUG_ASSERT(image_format_out); |
| fidl::FidlAllocator allocator; |
| ImageFormatConstraints image_format_constraints_v2 = |
| sysmem::V2CopyFromV1ImageFormatConstraints(allocator, *image_format_constraints_v1) |
| .take_value(); |
| auto v2_out_result = |
| ImageConstraintsToFormat(allocator, image_format_constraints_v2, width, height); |
| if (!v2_out_result.is_ok()) { |
| return false; |
| } |
| auto v2_out = v2_out_result.take_value(); |
| auto v1_out_result = sysmem::V1CopyFromV2ImageFormat(v2_out); |
| if (!v1_out_result.is_ok()) { |
| return false; |
| } |
| // ImageFormat doesn't have any hanldes, so this struct copy works to convert from v1 LLCPP to |
| // v1 FIDL C. We can remove this whole function when we're done moving away from FIDL C. |
| static_assert(sizeof(*image_format_out) == sizeof(v1_out_result.value())); |
| *image_format_out = *reinterpret_cast<fuchsia_sysmem_ImageFormat_2*>(&v1_out_result.value()); |
| return true; |
| } |
| |
| bool ImageFormatPlaneByteOffset(const ImageFormat& image_format, uint32_t plane, |
| uint64_t* offset_out) { |
| ZX_DEBUG_ASSERT(offset_out); |
| for (auto& format_set : kImageFormats) { |
| if (format_set->IsSupported(image_format.pixel_format())) { |
| return format_set->ImageFormatPlaneByteOffset(image_format, plane, offset_out); |
| } |
| } |
| return false; |
| } |
| |
| bool ImageFormatPlaneByteOffset(const fuchsia_sysmem::wire::ImageFormat2& image_format, |
| uint32_t plane, uint64_t* offset_out) { |
| ZX_DEBUG_ASSERT(offset_out); |
| fidl::FidlAllocator allocator; |
| auto image_format_v2_result = sysmem::V2CopyFromV1ImageFormat(allocator, image_format); |
| if (!image_format_v2_result.is_ok()) { |
| return false; |
| } |
| auto image_format_v2 = image_format_v2_result.take_value(); |
| return ImageFormatPlaneByteOffset(image_format_v2, plane, offset_out); |
| } |
| |
| bool ImageFormatPlaneByteOffset(const fuchsia_sysmem_ImageFormat_2* image_format, uint32_t plane, |
| uint64_t* offset_out) { |
| ZX_DEBUG_ASSERT(image_format); |
| ZX_DEBUG_ASSERT(offset_out); |
| fidl::FidlAllocator allocator; |
| auto image_format_v2_result = sysmem::V2CopyFromV1ImageFormat(allocator, *image_format); |
| if (!image_format_v2_result.is_ok()) { |
| return false; |
| } |
| auto image_format_v2 = image_format_v2_result.take_value(); |
| return ImageFormatPlaneByteOffset(image_format_v2, plane, offset_out); |
| } |
| |
| bool ImageFormatPlaneRowBytes(const ImageFormat& image_format, uint32_t plane, |
| uint32_t* row_bytes_out) { |
| ZX_DEBUG_ASSERT(row_bytes_out); |
| for (auto& format_set : kImageFormats) { |
| if (format_set->IsSupported(image_format.pixel_format())) { |
| return format_set->ImageFormatPlaneRowBytes(image_format, plane, row_bytes_out); |
| } |
| } |
| return false; |
| } |
| |
| bool ImageFormatPlaneRowBytes(const fuchsia_sysmem::wire::ImageFormat2& image_format, |
| uint32_t plane, uint32_t* row_bytes_out) { |
| ZX_DEBUG_ASSERT(row_bytes_out); |
| fidl::FidlAllocator allocator; |
| auto image_format_v2_result = sysmem::V2CopyFromV1ImageFormat(allocator, image_format); |
| if (!image_format_v2_result.is_ok()) { |
| return false; |
| } |
| auto image_format_v2 = image_format_v2_result.take_value(); |
| return ImageFormatPlaneRowBytes(image_format_v2, plane, row_bytes_out); |
| } |
| |
| bool ImageFormatPlaneRowBytes(const fuchsia_sysmem_ImageFormat_2* image_format, uint32_t plane, |
| uint32_t* row_bytes_out) { |
| ZX_DEBUG_ASSERT(image_format); |
| ZX_DEBUG_ASSERT(row_bytes_out); |
| fidl::FidlAllocator allocator; |
| auto image_format_v2_result = sysmem::V2CopyFromV1ImageFormat(allocator, *image_format); |
| if (!image_format_v2_result.is_ok()) { |
| return false; |
| } |
| auto image_format_v2 = image_format_v2_result.take_value(); |
| return ImageFormatPlaneRowBytes(image_format_v2, plane, row_bytes_out); |
| } |
| |
| bool ImageFormatCompatibleWithProtectedMemory( |
| const fuchsia_sysmem2::wire::PixelFormat& pixel_format) { |
| if (!pixel_format.has_format_modifier_value()) |
| return true; |
| constexpr uint64_t kArmLinearFormat = 0x0800000000000000ul; |
| switch (pixel_format.format_modifier_value() & ~AfbcFormats::kAfbcModifierMask) { |
| case kArmLinearFormat: |
| case fuchsia_sysmem2::wire::kFormatModifierArmAfbc16X16: |
| case fuchsia_sysmem2::wire::kFormatModifierArmAfbc32X8: |
| // TE formats occasionally need CPU writes to the TE buffer. |
| return !(pixel_format.format_modifier_value() & |
| fuchsia_sysmem2::wire::kFormatModifierArmTeBit); |
| |
| default: |
| return true; |
| } |
| } |
| |
| bool ImageFormatCompatibleWithProtectedMemory( |
| const fuchsia_sysmem::wire::PixelFormat& pixel_format_v1) { |
| fidl::FidlAllocator allocator; |
| auto pixel_format_v2 = sysmem::V2CopyFromV1PixelFormat(allocator, pixel_format_v1); |
| return ImageFormatCompatibleWithProtectedMemory(pixel_format_v2); |
| } |
| |
| bool ImageFormatCompatibleWithProtectedMemory(const fuchsia_sysmem_PixelFormat* pixel_format) { |
| ZX_DEBUG_ASSERT(pixel_format); |
| fidl::FidlAllocator allocator; |
| auto pixel_format_v2 = sysmem::V2CopyFromV1PixelFormat(allocator, *pixel_format); |
| return ImageFormatCompatibleWithProtectedMemory(pixel_format_v2); |
| } |