blob: c0ad3714fa569dd02141f5c23481cfc48e15bfa3 [file]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -----------------------------------------------------------------------------
//
// Tool for finding the best block layout.
//
// Author: Yannis Guyon (yguyon@google.com)
#include "src/enc/partitioning/partitioner.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include "src/common/lossy/block_size.h"
#include "src/common/lossy/block_size_io.h"
#include "src/enc/partitioning/partition_score_func.h"
#include "src/utils/utils.h"
#include "src/utils/vector.h"
#include "src/wp2/base.h"
#include "src/wp2/encode.h"
#include "src/wp2/format_constants.h"
namespace WP2 {
//------------------------------------------------------------------------------
WP2Status Partitioner::Init(const EncoderConfig& config, const YUVPlane& yuv,
const Rectangle& tile_rect,
PartitionScoreFunc* const score_func) {
config_ = &config;
tile_rect_ = tile_rect;
src_ = &yuv;
num_block_cols_ = SizeBlocks(yuv.Y.w_);
num_block_rows_ = SizeBlocks(yuv.Y.h_);
score_func_ = score_func;
WP2_CHECK_ALLOC_OK(occupancy_.resize(num_block_cols_ * num_block_rows_));
return WP2_STATUS_OK;
}
bool Partitioner::IsOccupied(const Block& block) const {
const uint32_t stride = num_block_cols_;
const bool* occupancy = occupancy_.data() + block.y() * stride + block.x();
for (uint32_t y = 0; y < block.h(); ++y) {
for (uint32_t x = 0; x < block.w(); ++x) {
if (occupancy[x]) return true;
}
occupancy += num_block_cols_;
}
return false;
}
void Partitioner::Occupy(const Block& block) {
const uint32_t stride = num_block_cols_;
bool* occupancy = occupancy_.data() + block.y() * stride + block.x();
for (uint32_t y = 0; y < block.h(); ++y) {
for (uint32_t x = 0; x < block.w(); ++x) occupancy[x] = true;
occupancy += num_block_cols_;
}
}
bool Partitioner::IsBlockValid(const Block& block, const Block& max_block,
uint32_t num_forced_blocks) const {
if (block.w() > max_block.w()) return false;
if (block.h() > max_block.h()) return false;
// Thanks to the FrontMgrLexico, this 'block' can only be rejected
// because of other forced blocks or forced snapping.
if (config_->partition_snapping && !block.IsSnapped()) return false;
if (num_forced_blocks > 0) return !IsOccupied(block);
assert(!IsOccupied(block));
return true;
}
//------------------------------------------------------------------------------
WP2Status AddForcedBlocks(const EncoderConfig& config,
const Rectangle& padded_tile_rect,
VectorNoCtor<Block>* const out) {
if (config.info == nullptr) return WP2_STATUS_OK;
for (const Rectangle& rect_px : config.info->force_partition) {
if (rect_px.x < padded_tile_rect.x || rect_px.y < padded_tile_rect.y) {
continue; // Ignore this block, it's outside of this tile.
}
const Rectangle local_rect = {rect_px.x - padded_tile_rect.x,
rect_px.y - padded_tile_rect.y, rect_px.width,
rect_px.height};
if (local_rect.x >= padded_tile_rect.width ||
local_rect.y >= padded_tile_rect.height) {
continue; // Ignore this block, it's outside of this tile.
}
WP2_CHECK_OK((local_rect.x + local_rect.width <= padded_tile_rect.width &&
local_rect.y + local_rect.height <= padded_tile_rect.height),
WP2_STATUS_INVALID_CONFIGURATION);
WP2::BlockSize matching_size = BLK_LAST;
for (const BlockSize* size = GetBlockSizes(config.partition_set);
*size != BLK_LAST; ++size) {
if (local_rect.width == WP2::BlockWidthPix(*size) &&
local_rect.height == WP2::BlockHeightPix(*size)) {
matching_size = *size;
break;
}
}
WP2_CHECK_OK(matching_size != BLK_LAST, WP2_STATUS_INVALID_CONFIGURATION);
// Create a Block that matches the rectangle.
Block block(local_rect.x / kMinBlockSizePix,
local_rect.y / kMinBlockSizePix, matching_size);
assert(local_rect.width == block.w_pix() &&
local_rect.height == block.h_pix());
// Verify that position coordinates are aligned on the block grid (multiples
// of kMinBlockSizePix). Width and height are checked and asserted above.
WP2_CHECK_OK(local_rect.x == block.x_pix() && local_rect.y == block.y_pix(),
WP2_STATUS_INVALID_CONFIGURATION);
// No IsSnapped() check, trust the user's intention on that.
WP2_CHECK_ALLOC_OK(out->push_back(block));
}
return WP2_STATUS_OK;
}
WP2Status Partitioner::RegisterForcedBlocks(
const VectorNoCtor<Block>& forced_blocks,
uint32_t* const max_num_blocks_left) {
for (const Block& block : forced_blocks) {
WP2_CHECK_OK(!IsOccupied(block), WP2_STATUS_INVALID_CONFIGURATION);
Occupy(block);
assert(*max_num_blocks_left >= block.w() * block.h());
*max_num_blocks_left -= block.w() * block.h();
}
return WP2_STATUS_OK;
}
bool IsCompatibleWithForcedBlocks(const Block& block, const Block sub_blocks[4],
const bool splittable[4],
uint32_t num_sub_blocks,
const VectorNoCtor<Block>& forced_blocks) {
if (num_sub_blocks == 1) {
assert(!splittable[0]);
assert(sub_blocks[0].dim() == block.dim());
}
auto forced_block_it =
std::lower_bound(forced_blocks.begin(), forced_blocks.end(), block);
for (; forced_block_it != forced_blocks.end(); ++forced_block_it) {
if (forced_block_it->y() >= block.y() + block.h()) break;
const Rectangle forced_block = forced_block_it->rect();
if (!block.rect().Intersects(forced_block)) continue;
// Intersects with but does not contain it => not compatible.
if (!block.rect().Contains(forced_block)) return false;
for (uint32_t i = 0; i < num_sub_blocks; ++i) {
if (!sub_blocks[i].rect().Intersects(forced_block)) continue;
if (!sub_blocks[i].rect().Contains(forced_block)) return false;
if (!splittable[i] && (sub_blocks[i].rect() != forced_block)) {
return false;
}
}
}
return true;
}
//------------------------------------------------------------------------------
} // namespace WP2