| #ifndef LIBGAV1_SRC_YUV_BUFFER_H_ |
| #define LIBGAV1_SRC_YUV_BUFFER_H_ |
| |
| #include <cstddef> |
| #include <cstdint> |
| |
| #include "src/frame_buffer.h" |
| #include "src/utils/constants.h" |
| |
| namespace libgav1 { |
| |
| class YuvBuffer { |
| public: |
| // If the memory was allocated by YuvBuffer directly, the memory is freed. |
| ~YuvBuffer(); |
| |
| // Allocates the buffer. Returns true on success. Returns false on failure. |
| // |
| // * |width| and |height| are the image dimensions in pixels. |
| // * |subsampling_x| and |subsampling_y| (either 0 or 1) specify the |
| // subsampling of the width and height of the chroma planes, respectively. |
| // * |border| is the size of the borders (on all four sides) in pixels. |
| // * |byte_alignment| specifies the additional alignment requirement of the |
| // data buffers of the Y, U, and V planes. If |byte_alignment| is 0, there |
| // is no additional alignment requirement. Otherwise, |byte_alignment| |
| // must be a power of 2 and greater than or equal to 16. |
| // NOTE: The strides are a multiple of 16. Therefore only the first row in |
| // each plane is aligned to |byte_alignment|. Subsequent rows are only |
| // 16-byte aligned. |
| // * If |get_frame_buffer| is not null, it is invoked to allocate the memory. |
| // If |get_frame_buffer| is null, YuvBuffer allocates the memory directly |
| // and ignores the |private_data| and |frame_buffer| parameters, which |
| // should be null. |
| // |
| // Example: bitdepth=8 width=20 height=6 border=2. The diagram below shows |
| // how Realloc() allocates the data buffer for the Y plane. |
| // |
| // 16-byte aligned |
| // | |
| // v |
| // BBBBBBBBBBBBBBBBBBBBBBBBBBBBpppp |
| // BBBBBBBBBBBBBBBBBBBBBBBBBBBBpppp |
| // BB01234567890123456789....BBpppp |
| // BB11234567890123456789....BBpppp |
| // BB21234567890123456789....BBpppp |
| // BB31234567890123456789....BBpppp |
| // BB41234567890123456789....BBpppp |
| // BB51234567890123456789....BBpppp |
| // BB........................BBpppp |
| // BB........................BBpppp |
| // BBBBBBBBBBBBBBBBBBBBBBBBBBBBpppp |
| // BBBBBBBBBBBBBBBBBBBBBBBBBBBBpppp |
| // | | |
| // |<-- stride (multiple of 16) ->| |
| // |
| // The video frame has 6 rows of 20 pixels each. Each row is shown as the |
| // pattern r1234567890123456789, where |r| is 0, 1, 2, 3, 4, 5. |
| // |
| // Realloc() first aligns |width| and |height| to multiples of 8 pixels. The |
| // pixels added in this step are shown as dots ('.'). In this example, the |
| // aligned width is 24 pixels and the aligned height is 8 pixels. NOTE: The |
| // purpose of this step is unknown. We should be able to remove this step. |
| // |
| // Realloc() then adds a border of 2 pixels around this region. The border |
| // pixels are shown as capital 'B'. NOTE: This example uses a tiny border of |
| // 2 pixels to keep the diagram small. The current implementation of |
| // Realloc() actually requires that |border| be a multiple of 32. We should |
| // be able to only require that |border| be a multiple of 2. |
| // |
| // Each row is now padded to a multiple of the default alignment in bytes, |
| // which is 16. The padding bytes are shown as lowercase 'p'. (Since |
| // |bitdepth| is 8 in this example, each pixel is one byte.) The padded size |
| // in bytes is the stride. In this example, the stride is 32 bytes. |
| // |
| // Finally, Realloc() aligns the first byte of frame data, which is the '0' |
| // pixel/byte in the upper left corner of the frame, to the default (16-byte) |
| // alignemnt boundary and also the |byte_alignment| boundary, if |
| // |byte_alignment| is nonzero. |
| // |
| // TODO(wtc): We don't need to allocate the U and V plane buffers if |
| // |monochrome| is true. |
| // TODO(wtc): Add a check for width and height limits to defend against |
| // invalid bitstreams. |
| bool Realloc(int bitdepth, bool is_monochrome, int width, int height, |
| int8_t subsampling_x, int8_t subsampling_y, int border, |
| int byte_alignment, GetFrameBufferCallback get_frame_buffer, |
| void* private_data, FrameBuffer* frame_buffer); |
| |
| int bitdepth() const { return bitdepth_; } |
| |
| bool is_monochrome() const { return is_monochrome_; } |
| |
| int8_t subsampling_x() const { return subsampling_x_; } |
| int8_t subsampling_y() const { return subsampling_y_; } |
| |
| int width(int plane) const { |
| return (plane == kPlaneY) ? y_width_ : uv_width_; |
| } |
| int height(int plane) const { |
| return (plane == kPlaneY) ? y_height_ : uv_height_; |
| } |
| |
| int displayed_width(int plane) const { |
| return (plane == kPlaneY) ? y_crop_width_ : uv_crop_width_; |
| } |
| int displayed_height(int plane) const { |
| return (plane == kPlaneY) ? y_crop_height_ : uv_crop_height_; |
| } |
| |
| // Returns border sizes in pixels. |
| int left_border(int plane) const { return left_border_[plane]; } |
| int right_border(int plane) const { return right_border_[plane]; } |
| int top_border(int plane) const { return top_border_[plane]; } |
| int bottom_border(int plane) const { return bottom_border_[plane]; } |
| |
| // Returns the alignment of frame buffer row in bytes. |
| int alignment() const { return 16; } |
| |
| // Returns whether shifing frame buffer is successful. |
| // |vertical_shift| and |horizontal_shift| are in pixels. |
| // TODO(chengchen): |
| // Warning: this implementation doesn't handle the byte_alignment requirement. |
| // For example, if the frame is required to be 4K-byte aligned, this method |
| // fails. Figure out alternative solutions if the feature of |
| // byte_alignment is required in practice. |
| bool ShiftBuffer(int plane, int horizontal_shift, int vertical_shift) { |
| if (!ValidHorizontalShift(plane, horizontal_shift) || |
| !ValidVerticalShift(plane, vertical_shift)) { |
| return false; |
| } |
| left_border_[plane] += horizontal_shift; |
| right_border_[plane] -= horizontal_shift; |
| top_border_[plane] += vertical_shift; |
| bottom_border_[plane] -= vertical_shift; |
| const int pixel_size = |
| static_cast<int>((bitdepth_ == 8) ? sizeof(uint8_t) : sizeof(uint16_t)); |
| buffer_[plane] += |
| vertical_shift * stride_[plane] + horizontal_shift * pixel_size; |
| return true; |
| } |
| |
| // Returns the data buffer for |plane|. |
| uint8_t* data(int plane) { return buffer_[plane]; } |
| const uint8_t* data(int plane) const { return buffer_[plane]; } |
| |
| // Returns the stride in bytes for |plane|. |
| int stride(int plane) const { return stride_[plane]; } |
| |
| private: |
| // Frame buffer pointer, i.e., |buffer_[plane]| can only be shifted in loop |
| // restoration. If loop restoration is applied on plane, |buffer_[plane]| |
| // will be shifted kRestorationBorder rows above, and |
| // kFrameBufferRowAlignment columns left. |
| // |shift| is in pixels. |
| // Positive vertical shift is a down shift. Negative vertical shift is an |
| // up shift. |
| bool ValidVerticalShift(int plane, int shift) const { |
| return (shift >= 0) ? bottom_border_[plane] >= shift |
| : top_border_[plane] + shift >= 0; |
| } |
| // Positive horizontal shift is a right shift. Negative horizontal shift is |
| // a left shift. |
| bool ValidHorizontalShift(int plane, int shift) const { |
| return (shift >= 0) ? right_border_[plane] >= shift |
| : left_border_[plane] + shift >= 0; |
| } |
| |
| int bitdepth_ = 0; |
| bool is_monochrome_ = false; |
| |
| // y_crop_width_ and y_crop_height_ are the original width and height (the |
| // |width| and |height| arguments passed to the Realloc() method). y_width_ |
| // and y_height_ are the original width and height padded to a multiple of |
| // 8. |
| // |
| // The UV widths and heights are computed from Y widths and heights as |
| // follows: |
| // uv_crop_width_ = (y_crop_width_ + subsampling_x_) >> subsampling_x_ |
| // uv_crop_height_ = (y_crop_height_ + subsampling_y_) >> subsampling_y_ |
| // uv_width_ = y_width_ >> subsampling_x_ |
| // uv_height_ = y_height_ >> subsampling_y_ |
| int y_width_ = 0; |
| int uv_width_ = 0; |
| int y_height_ = 0; |
| int uv_height_ = 0; |
| int y_crop_width_ = 0; |
| int uv_crop_width_ = 0; |
| int y_crop_height_ = 0; |
| int uv_crop_height_ = 0; |
| |
| int left_border_[kMaxPlanes] = {}; |
| int right_border_[kMaxPlanes] = {}; |
| int top_border_[kMaxPlanes] = {}; |
| int bottom_border_[kMaxPlanes] = {}; |
| |
| int stride_[kMaxPlanes] = {}; |
| uint8_t* buffer_[kMaxPlanes] = {}; |
| |
| // buffer_alloc_ and buffer_alloc_size_ are only used if the |
| // get_frame_buffer callback is null and we allocate the buffer ourselves. |
| uint8_t* buffer_alloc_ = nullptr; |
| size_t buffer_alloc_size_ = 0; |
| |
| int8_t subsampling_x_ = 0; // 0 or 1. |
| int8_t subsampling_y_ = 0; // 0 or 1. |
| }; |
| |
| } // namespace libgav1 |
| |
| #endif // LIBGAV1_SRC_YUV_BUFFER_H_ |