VideoCodec/Modules/Transform/MacroblockHandler.cpp
#include "MacroblockHandler.h"
namespace VideoCodec
{
MacroblockHandler::MacroblockHandler() : Module(MACROBLOCK_HANDLER_MODULE_ID)
{
}
MacroblockHandler::~MacroblockHandler()
{
}
ConfigurationStatus MacroblockHandler::Configure(ConfigurationElement* configuration)
{
this->mbLayout = PIXEL_LAYOUT_MB_FOUR_TWO_ZERO;
this->colourResizeFilter = SIMPLE_COLOUR_RESIZE_FILTER;
return MODULE_CONFIGURATION_OK;
}
void MacroblockHandler::ConvertSignedScanToSignedMacroblockLayout(PictureInfo* picture)
{
// See Constants.h for a description of this MB layout.
if (this->mbLayout == PIXEL_LAYOUT_MB_FOUR_TWO_ZERO)
{
// Make sure that the input has the correct dimensions.
if ((picture->GetPixelsX() % 16) != 0
|| (picture->GetPixelsY() % 16) != 0)
{
Logger::LogMessage("Error: The image could not be converted into macroblocks; the dimensions are not valid for the configured macroblock size.");
return;
}
// Make sure the colour space of the input scan data is the correct format.
if (picture->GetColourFormat() != COLOUR_FORMAT_YUV)
Logger::LogMessage("Warning: Unexpected colour format in macroblock handler. Wanted YUV format.");
BlockCount blockRows = picture->GetPixelsY() >> 4;
BlockCount blockColumns = picture->GetPixelsX() >> 4;
// Pointer into the scan data.
SignedShort* scanData = picture->GetSignedShortData();
int scanLocation = 0;
PixelCount stride = picture->GetPixelsX();
// The new frame data.
// For each block in the picture (|blocks| == blockRows * blockColumns)
// store six (4 * luminance, Cb and Cr)
// groups of eight by eight bytes.
SignedShort* values = new SignedShort[blockRows * blockColumns * 6 * 8 * 8];
// Keep track of the current location.
SignedShort* currentBlock = values;
for (int y = 0; y < blockRows; y++)
{
for (int x = 0; x < blockColumns; x++)
{
scanLocation = ((y << 4) * stride) + (x << 4);
// Copy pixels from the luminance channel into the first four sets of 8 * 8.
for (int bY = 0; bY < 8; bY++)
for (int bX = 0; bX < 8; bX++)
{
// Luminance 0:
currentBlock[(0 * 8 * 8) + (bY << 3) + bX] =
scanData[(scanLocation + (bY * stride) + bX) * 3];
// Luminance 1:
currentBlock[(1 * 8 * 8) + (bY << 3) + bX] =
scanData[(scanLocation + (bY * stride) + (bX + 8)) * 3];
// Luminance 2:
currentBlock[(2 * 8 * 8) + (bY << 3) + bX] =
scanData[(scanLocation + ((bY + 8) * stride) + bX) * 3];
// Luminance 3:
currentBlock[(3 * 8 * 8) + (bY << 3) + bX] =
scanData[(scanLocation + ((bY + 8) * stride) + (bX + 8)) * 3];
if (this->colourResizeFilter == SIMPLE_COLOUR_RESIZE_FILTER)
{
// In this filter, average out the colour from the four pixels.
int quadBaseIndex = scanLocation + ((bY << 1) * stride) + (bX << 1);
// Cb:
currentBlock[(4 * 8 * 8) + (bY << 3) + bX] = (scanData[quadBaseIndex * 3 + 1] + scanData[(quadBaseIndex + 1) * 3 + 1] + scanData[(quadBaseIndex + stride) * 3 + 1] + scanData[(quadBaseIndex + stride + 1) * 3 + 1]) / 4;
// Cr:
currentBlock[(5 * 8 * 8) + (bY << 3) + bX] = (scanData[quadBaseIndex * 3 + 2] + scanData[(quadBaseIndex + 1) * 3 + 2] + scanData[(quadBaseIndex + stride) * 3 + 2] + scanData[(quadBaseIndex + stride + 1) * 3 + 2]) / 4;
}
else
{
Logger::LogMessage("Error: Invalid colour channel resizing filter.");
return;
}
}
currentBlock += (8 * 8 * 6);
}
}
// Store the new values in the picture. The old data will be deleted.
picture->SetSignedShortData(values, blockRows * blockColumns * 6 * 64);
picture->SetBlockRows(blockRows);
picture->SetBlockColumns(blockColumns);
picture->SetPixelLayout(PIXEL_LAYOUT_MB_FOUR_TWO_ZERO);
}
else
{
Logger::LogMessage("Error: Unsupported macroblock layout selected.");
return;
}
}
void MacroblockHandler::ConvertScanToMacroblockLayout(PictureInfo* picture)
{
// See Constants.h for a description of this MB layout.
if (this->mbLayout == PIXEL_LAYOUT_MB_FOUR_TWO_ZERO)
{
// Make sure that the input has the correct dimensions.
if ((picture->GetPixelsX() % 16) != 0
|| (picture->GetPixelsY() % 16) != 0)
{
Logger::LogMessage("Error: The image could not be converted into macroblocks; the dimensions are not valid for the configured macroblock size.");
return;
}
// Make sure the colour space of the input scan data is the correct format.
if (picture->GetColourFormat() != COLOUR_FORMAT_YUV)
Logger::LogMessage("Warning: Unexpected colour format in macroblock handler. Wanted YUV format.");
BlockCount blockRows = picture->GetPixelsY() >> 4;
BlockCount blockColumns = picture->GetPixelsX() >> 4;
// Pointer into the scan data.
UnsignedByte* scanData = picture->GetUnsignedByteData();
int scanLocation = 0;
PixelCount stride = picture->GetPixelsX();
// The new frame data.
// For each block in the picture (|blocks| == blockRows * blockColumns)
// store six (4 * luminance, Cb and Cr)
// groups of eight by eight bytes.
UnsignedByte* values = new UnsignedByte[blockRows * blockColumns * 6 * 8 * 8];
// Keep track of the current location.
UnsignedByte* currentBlock = values;
for (int y = 0; y < blockRows; y++)
{
for (int x = 0; x < blockColumns; x++)
{
scanLocation = ((y << 4) * stride) + (x << 4);
// Copy pixels from the luminance channel into the first four sets of 8 * 8.
for (int bY = 0; bY < 8; bY++)
for (int bX = 0; bX < 8; bX++)
{
// Luminance 0:
currentBlock[(0 * 8 * 8) + (bY << 3) + bX] =
scanData[(scanLocation + (bY * stride) + bX) * 3];
// Luminance 1:
currentBlock[(1 * 8 * 8) + (bY << 3) + bX] =
scanData[(scanLocation + (bY * stride) + (bX + 8)) * 3];
// Luminance 2:
currentBlock[(2 * 8 * 8) + (bY << 3) + bX] =
scanData[(scanLocation + ((bY + 8) * stride) + bX) * 3];
// Luminance 3:
currentBlock[(3 * 8 * 8) + (bY << 3) + bX] =
scanData[(scanLocation + ((bY + 8) * stride) + (bX + 8)) * 3];
if (this->colourResizeFilter == SIMPLE_COLOUR_RESIZE_FILTER)
{
// In this filter, average out the colour from the four pixels.
int quadBaseIndex = scanLocation + ((bY << 1) * stride) + (bX << 1);
// Cb:
currentBlock[(4 * 8 * 8) + (bY << 3) + bX] = (scanData[quadBaseIndex * 3 + 1] + scanData[(quadBaseIndex + 1) * 3 + 1] + scanData[(quadBaseIndex + stride) * 3 + 1] + scanData[(quadBaseIndex + stride + 1) * 3 + 1]) >> 2;
// Cr:
currentBlock[(5 * 8 * 8) + (bY << 3) + bX] = (scanData[quadBaseIndex * 3 + 2] + scanData[(quadBaseIndex + 1) * 3 + 2] + scanData[(quadBaseIndex + stride) * 3 + 2] + scanData[(quadBaseIndex + stride + 1) * 3 + 2]) >> 2;
}
else
{
Logger::LogMessage("Error: Invalid colour channel resizing filter.");
return;
}
}
currentBlock += (8 * 8 * 6);
}
}
// Store the new values in the picture. The old data will be deleted.
picture->SetUnsignedByteData(values, blockRows * blockColumns * 6 * 64);
picture->SetBlockRows(blockRows);
picture->SetBlockColumns(blockColumns);
picture->SetPixelLayout(PIXEL_LAYOUT_MB_FOUR_TWO_ZERO);
}
else
{
Logger::LogMessage("Error: Unsupported macroblock layout selected.");
return;
}
}
void MacroblockHandler::ConvertSignedMacroblockToSignedScanLayout(PictureInfo* picture)
{
if (picture->GetPixelLayout() == PIXEL_LAYOUT_MB_FOUR_TWO_ZERO)
{
SignedShort* blockValues = picture->GetSignedShortData();
BlockCount blockRows = picture->GetBlockRows();
BlockCount blockColumns = picture->GetBlockColumns();
// Allocate space for the new scan order image.
// Four luminance 8 * 8 blocks go into each 16 * 16 macroblock region.
// Two colour channels are expanded into each 16 * 16 macroblock region.
PixelCount imageWidth = picture->GetBlockColumns() << 4;
PixelCount imageHeight = picture->GetBlockRows() << 4;
SignedShort* scanValues = new SignedShort[imageWidth * imageHeight * 3];
// Iterate over the stored macroblocks, transferring the information in each one to the scan order picture.
for (int y = 0; y < blockRows; y++)
{
for (int x = 0; x < blockColumns; x++)
{
// Get a pointer to the current macroblock.
SignedShort* mb = blockValues + (x + (y * blockColumns)) * (6 * 8 * 8);
// Copy the luminance channels in.
for (int bY = 0; bY < 8; bY++)
for (int bX = 0; bX < 8; bX++)
{
// Luminance 0, 1, 2 and 3.
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * imageWidth) + bX) * 3] =
mb[(0 * 8 * 8) + (bY * 8) + bX];
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * imageWidth) + (bX + 8)) * 3] =
mb[(1 * 8 * 8) + (bY * 8) + bX];
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY + 8) * imageWidth) + bX) * 3] =
mb[(2 * 8 * 8) + (bY * 8) + bX];
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY + 8) * imageWidth) + (bX + 8)) * 3] =
mb[(3 * 8 * 8) + (bY * 8) + bX];
}
// Expand and copy the colour channels in.
if (this->colourResizeFilter == SIMPLE_COLOUR_RESIZE_FILTER)
{
for (int bY = 0; bY < 8; bY++)
for (int bX = 0; bX < 8; bX++)
{
// Cb and Cr:
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * 2 * imageWidth) + (bX * 2)) * 3 + 1] =
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * 2 * imageWidth) + (bX * 2 + 1)) * 3 + 1] =
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY * 2 + 1) * imageWidth) + (bX * 2)) * 3 + 1] =
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY * 2 + 1) * imageWidth) + (bX * 2 + 1)) * 3 + 1] =
mb[(4 * 8 * 8) + (bY * 8) + bX];
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * 2 * imageWidth) + (bX * 2)) * 3 + 2] =
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * 2 * imageWidth) + (bX * 2 + 1)) * 3 + 2] =
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY * 2 + 1) * imageWidth) + (bX * 2)) * 3 + 2] =
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY * 2 + 1) * imageWidth) + (bX * 2 + 1)) * 3 + 2] =
mb[(5 * 8 * 8) + (bY * 8) + bX];
}
}
else
{
Logger::LogMessage("Error: Invalid colour channel resizing filter.");
return;
}
}
}
// Store the scan ordered data back in the picture.
picture->SetSignedShortData(scanValues, 3 * imageWidth * imageHeight);
picture->SetPixelLayout(PIXEL_LAYOUT_SCAN);
}
else
{
Logger::LogMessage("Error: Unsupported macroblock layout selected.");
return;
}
}
void MacroblockHandler::ConvertMacroblockToScanLayout(PictureInfo* picture)
{
if (picture->GetPixelLayout() == PIXEL_LAYOUT_MB_FOUR_TWO_ZERO)
{
UnsignedByte* blockValues = picture->GetUnsignedByteData();
BlockCount blockRows = picture->GetBlockRows();
BlockCount blockColumns = picture->GetBlockColumns();
// Allocate space for the new scan order image.
// Four luminance 8 * 8 blocks go into each 16 * 16 macroblock region.
// Two colour channels are expanded into each 16 * 16 macroblock region.
PixelCount imageWidth = picture->GetBlockColumns() << 4;
PixelCount imageHeight = picture->GetBlockRows() << 4;
UnsignedByte* scanValues = new UnsignedByte[imageWidth * imageHeight * 3];
// Iterate over the stored macroblocks, transferring the information in each one to the scan order picture.
for (int y = 0; y < blockRows; y++)
{
for (int x = 0; x < blockColumns; x++)
{
// Get a pointer to the current macroblock.
UnsignedByte* mb = blockValues + (x + (y * blockColumns)) * (6 * 8 * 8);
// Copy the luminance channels in.
for (int bY = 0; bY < 8; bY++)
for (int bX = 0; bX < 8; bX++)
{
// Luminance 0, 1, 2 and 3.
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * imageWidth) + bX) * 3] =
mb[(0 * 8 * 8) + (bY * 8) + bX];
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * imageWidth) + (bX + 8)) * 3] =
mb[(1 * 8 * 8) + (bY * 8) + bX];
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY + 8) * imageWidth) + bX) * 3] =
mb[(2 * 8 * 8) + (bY * 8) + bX];
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY + 8) * imageWidth) + (bX + 8)) * 3] =
mb[(3 * 8 * 8) + (bY * 8) + bX];
}
// Expand and copy the colour channels in.
if (this->colourResizeFilter == SIMPLE_COLOUR_RESIZE_FILTER)
{
for (int bY = 0; bY < 8; bY++)
for (int bX = 0; bX < 8; bX++)
{
// Cb and Cr:
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * 2 * imageWidth) + (bX * 2)) * 3 + 1] =
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * 2 * imageWidth) + (bX * 2 + 1)) * 3 + 1] =
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY * 2 + 1) * imageWidth) + (bX * 2)) * 3 + 1] =
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY * 2 + 1) * imageWidth) + (bX * 2 + 1)) * 3 + 1] =
mb[(4 * 8 * 8) + (bY * 8) + bX];
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * 2 * imageWidth) + (bX * 2)) * 3 + 2] =
scanValues[((y * 16 * imageWidth) + (x * 16) + (bY * 2 * imageWidth) + (bX * 2 + 1)) * 3 + 2] =
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY * 2 + 1) * imageWidth) + (bX * 2)) * 3 + 2] =
scanValues[((y * 16 * imageWidth) + (x * 16) + ((bY * 2 + 1) * imageWidth) + (bX * 2 + 1)) * 3 + 2] =
mb[(5 * 8 * 8) + (bY * 8) + bX];
}
}
else
{
Logger::LogMessage("Error: Invalid colour channel resizing filter.");
return;
}
}
}
// Store the scan ordered data back in the picture.
picture->SetUnsignedByteData(scanValues, 3 * imageWidth * imageHeight);
picture->SetPixelLayout(PIXEL_LAYOUT_SCAN);
}
else
{
Logger::LogMessage("Error: Unsupported macroblock layout selected.");
return;
}
}
}