426 lines
11 KiB
C++
426 lines
11 KiB
C++
/*
|
|
* Copyright 2015 The Etc2Comp Authors.
|
|
*
|
|
* 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
|
|
*
|
|
* http://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.
|
|
*/
|
|
|
|
/*
|
|
EtcBlock4x4.cpp
|
|
|
|
Implements the state associated with each 4x4 block of pixels in an image
|
|
|
|
Source images that are not a multiple of 4x4 are extended to fill the Block4x4 using pixels with an
|
|
alpha of NAN
|
|
|
|
*/
|
|
|
|
#include "EtcConfig.h"
|
|
#include "EtcBlock4x4.h"
|
|
|
|
#include "EtcBlock4x4EncodingBits.h"
|
|
#include "EtcColor.h"
|
|
#include "EtcImage.h"
|
|
#include "EtcColorFloatRGBA.h"
|
|
#include "EtcBlock4x4Encoding_RGB8.h"
|
|
#include "EtcBlock4x4Encoding_RGBA8.h"
|
|
#include "EtcBlock4x4Encoding_RGB8A1.h"
|
|
#include "EtcBlock4x4Encoding_R11.h"
|
|
#include "EtcBlock4x4Encoding_RG11.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
namespace Etc
|
|
{
|
|
// ETC pixels are scanned vertically.
|
|
// this mapping is for when someone wants to scan the ETC pixels horizontally
|
|
const unsigned int Block4x4::s_auiPixelOrderHScan[PIXELS] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
//
|
|
Block4x4::Block4x4(void)
|
|
{
|
|
m_pimageSource = nullptr;
|
|
m_uiSourceH = 0;
|
|
m_uiSourceV = 0;
|
|
|
|
m_sourcealphamix = SourceAlphaMix::UNKNOWN;
|
|
m_boolBorderPixels = false;
|
|
m_boolPunchThroughPixels = false;
|
|
|
|
m_pencoding = nullptr;
|
|
|
|
m_errormetric = ErrorMetric::NUMERIC;
|
|
|
|
}
|
|
Block4x4::~Block4x4()
|
|
{
|
|
m_pimageSource = nullptr;
|
|
if (m_pencoding)
|
|
{
|
|
delete m_pencoding;
|
|
m_pencoding = nullptr;
|
|
}
|
|
}
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// initialization prior to encoding from a source image
|
|
// [a_uiSourceH,a_uiSourceV] is the location of the block in a_pimageSource
|
|
// a_paucEncodingBits is the place to store the final encoding
|
|
// a_errormetric is used for finding the best encoding
|
|
//
|
|
void Block4x4::InitFromSource(Image *a_pimageSource,
|
|
unsigned int a_uiSourceH, unsigned int a_uiSourceV,
|
|
unsigned char *a_paucEncodingBits,
|
|
ErrorMetric a_errormetric)
|
|
{
|
|
|
|
Block4x4();
|
|
|
|
m_pimageSource = a_pimageSource;
|
|
m_uiSourceH = a_uiSourceH;
|
|
m_uiSourceV = a_uiSourceV;
|
|
m_errormetric = a_errormetric;
|
|
|
|
SetSourcePixels();
|
|
|
|
// set block encoder function
|
|
switch (m_pimageSource->GetFormat())
|
|
{
|
|
case Image::Format::ETC1:
|
|
m_pencoding = new Block4x4Encoding_ETC1;
|
|
break;
|
|
|
|
case Image::Format::RGB8:
|
|
case Image::Format::SRGB8:
|
|
m_pencoding = new Block4x4Encoding_RGB8;
|
|
break;
|
|
|
|
case Image::Format::RGBA8:
|
|
case Image::Format::SRGBA8:
|
|
if (a_errormetric == RGBX)
|
|
{
|
|
m_pencoding = new Block4x4Encoding_RGBA8;
|
|
}
|
|
else
|
|
{
|
|
switch (m_sourcealphamix)
|
|
{
|
|
case SourceAlphaMix::OPAQUE:
|
|
m_pencoding = new Block4x4Encoding_RGBA8_Opaque;
|
|
break;
|
|
|
|
case SourceAlphaMix::TRANSPARENT:
|
|
m_pencoding = new Block4x4Encoding_RGBA8_Transparent;
|
|
break;
|
|
|
|
case SourceAlphaMix::TRANSLUCENT:
|
|
m_pencoding = new Block4x4Encoding_RGBA8;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case Image::Format::RGB8A1:
|
|
case Image::Format::SRGB8A1:
|
|
switch (m_sourcealphamix)
|
|
{
|
|
case SourceAlphaMix::OPAQUE:
|
|
m_pencoding = new Block4x4Encoding_RGB8A1_Opaque;
|
|
break;
|
|
|
|
case SourceAlphaMix::TRANSPARENT:
|
|
m_pencoding = new Block4x4Encoding_RGB8A1_Transparent;
|
|
break;
|
|
|
|
case SourceAlphaMix::TRANSLUCENT:
|
|
if (m_boolPunchThroughPixels)
|
|
{
|
|
m_pencoding = new Block4x4Encoding_RGB8A1;
|
|
}
|
|
else
|
|
{
|
|
m_pencoding = new Block4x4Encoding_RGB8A1_Opaque;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case Image::Format::R11:
|
|
case Image::Format::SIGNED_R11:
|
|
m_pencoding = new Block4x4Encoding_R11;
|
|
break;
|
|
case Image::Format::RG11:
|
|
case Image::Format::SIGNED_RG11:
|
|
m_pencoding = new Block4x4Encoding_RG11;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
m_pencoding->InitFromSource(this, m_afrgbaSource,
|
|
a_paucEncodingBits, a_errormetric);
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// initialization of encoding state from a prior encoding using encoding bits
|
|
// [a_uiSourceH,a_uiSourceV] is the location of the block in a_pimageSource
|
|
// a_paucEncodingBits is the place to read the prior encoding
|
|
// a_imageformat is used to determine how to interpret a_paucEncodingBits
|
|
// a_errormetric was used for the prior encoding
|
|
//
|
|
void Block4x4::InitFromEtcEncodingBits(Image::Format a_imageformat,
|
|
unsigned int a_uiSourceH, unsigned int a_uiSourceV,
|
|
unsigned char *a_paucEncodingBits,
|
|
Image *a_pimageSource,
|
|
ErrorMetric a_errormetric)
|
|
{
|
|
Block4x4();
|
|
|
|
m_pimageSource = a_pimageSource;
|
|
m_uiSourceH = a_uiSourceH;
|
|
m_uiSourceV = a_uiSourceV;
|
|
m_errormetric = a_errormetric;
|
|
|
|
SetSourcePixels();
|
|
|
|
// set block encoder function
|
|
switch (a_imageformat)
|
|
{
|
|
case Image::Format::ETC1:
|
|
m_pencoding = new Block4x4Encoding_ETC1;
|
|
break;
|
|
|
|
case Image::Format::RGB8:
|
|
case Image::Format::SRGB8:
|
|
m_pencoding = new Block4x4Encoding_RGB8;
|
|
break;
|
|
|
|
case Image::Format::RGBA8:
|
|
case Image::Format::SRGBA8:
|
|
m_pencoding = new Block4x4Encoding_RGBA8;
|
|
break;
|
|
|
|
case Image::Format::RGB8A1:
|
|
case Image::Format::SRGB8A1:
|
|
m_pencoding = new Block4x4Encoding_RGB8A1;
|
|
break;
|
|
|
|
case Image::Format::R11:
|
|
case Image::Format::SIGNED_R11:
|
|
m_pencoding = new Block4x4Encoding_R11;
|
|
break;
|
|
case Image::Format::RG11:
|
|
case Image::Format::SIGNED_RG11:
|
|
m_pencoding = new Block4x4Encoding_RG11;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
m_pencoding->InitFromEncodingBits(this, a_paucEncodingBits, m_afrgbaSource,
|
|
m_pimageSource->GetErrorMetric());
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// set source pixels from m_pimageSource
|
|
// set m_alphamix
|
|
//
|
|
void Block4x4::SetSourcePixels(void)
|
|
{
|
|
|
|
Image::Format imageformat = m_pimageSource->GetFormat();
|
|
|
|
// alpha census
|
|
unsigned int uiTransparentSourcePixels = 0;
|
|
unsigned int uiOpaqueSourcePixels = 0;
|
|
|
|
// copy source to consecutive memory locations
|
|
// convert from image horizontal scan to block vertical scan
|
|
unsigned int uiPixel = 0;
|
|
for (unsigned int uiBlockPixelH = 0; uiBlockPixelH < Block4x4::COLUMNS; uiBlockPixelH++)
|
|
{
|
|
unsigned int uiSourcePixelH = m_uiSourceH + uiBlockPixelH;
|
|
|
|
for (unsigned int uiBlockPixelV = 0; uiBlockPixelV < Block4x4::ROWS; uiBlockPixelV++)
|
|
{
|
|
unsigned int uiSourcePixelV = m_uiSourceV + uiBlockPixelV;
|
|
|
|
ColorFloatRGBA *pfrgbaSource = m_pimageSource->GetSourcePixel(uiSourcePixelH, uiSourcePixelV);
|
|
|
|
// if pixel extends beyond source image because of block padding
|
|
if (pfrgbaSource == nullptr)
|
|
{
|
|
m_afrgbaSource[uiPixel] = ColorFloatRGBA(0.0f, 0.0f, 0.0f, NAN); // denotes border pixel
|
|
m_boolBorderPixels = true;
|
|
uiTransparentSourcePixels++;
|
|
}
|
|
else
|
|
{
|
|
//get teh current pixel data, and store some of the attributes
|
|
//before capping values to fit the encoder type
|
|
|
|
m_afrgbaSource[uiPixel] = (*pfrgbaSource).ClampRGBA();
|
|
|
|
if (m_afrgbaSource[uiPixel].fA == 1.0f || m_errormetric == RGBX)
|
|
{
|
|
m_pimageSource->m_iNumOpaquePixels++;
|
|
}
|
|
else if (m_afrgbaSource[uiPixel].fA == 0.0f)
|
|
{
|
|
m_pimageSource->m_iNumTransparentPixels++;
|
|
}
|
|
else if(m_afrgbaSource[uiPixel].fA > 0.0f && m_afrgbaSource[uiPixel].fA < 1.0f)
|
|
{
|
|
m_pimageSource->m_iNumTranslucentPixels++;
|
|
}
|
|
else
|
|
{
|
|
m_pimageSource->m_numOutOfRangeValues.fA++;
|
|
}
|
|
|
|
if (m_afrgbaSource[uiPixel].fR != 0.0f)
|
|
{
|
|
m_pimageSource->m_numColorValues.fR++;
|
|
//make sure we are getting a float between 0-1
|
|
if (m_afrgbaSource[uiPixel].fR - 1.0f > 0.0f)
|
|
{
|
|
m_pimageSource->m_numOutOfRangeValues.fR++;
|
|
}
|
|
}
|
|
|
|
if (m_afrgbaSource[uiPixel].fG != 0.0f)
|
|
{
|
|
m_pimageSource->m_numColorValues.fG++;
|
|
if (m_afrgbaSource[uiPixel].fG - 1.0f > 0.0f)
|
|
{
|
|
m_pimageSource->m_numOutOfRangeValues.fG++;
|
|
}
|
|
}
|
|
if (m_afrgbaSource[uiPixel].fB != 0.0f)
|
|
{
|
|
m_pimageSource->m_numColorValues.fB++;
|
|
if (m_afrgbaSource[uiPixel].fB - 1.0f > 0.0f)
|
|
{
|
|
m_pimageSource->m_numOutOfRangeValues.fB++;
|
|
}
|
|
}
|
|
// for formats with no alpha, set source alpha to 1
|
|
if (imageformat == Image::Format::ETC1 ||
|
|
imageformat == Image::Format::RGB8 ||
|
|
imageformat == Image::Format::SRGB8)
|
|
{
|
|
m_afrgbaSource[uiPixel].fA = 1.0f;
|
|
}
|
|
|
|
if (imageformat == Image::Format::R11 ||
|
|
imageformat == Image::Format::SIGNED_R11)
|
|
{
|
|
m_afrgbaSource[uiPixel].fA = 1.0f;
|
|
m_afrgbaSource[uiPixel].fG = 0.0f;
|
|
m_afrgbaSource[uiPixel].fB = 0.0f;
|
|
}
|
|
|
|
if (imageformat == Image::Format::RG11 ||
|
|
imageformat == Image::Format::SIGNED_RG11)
|
|
{
|
|
m_afrgbaSource[uiPixel].fA = 1.0f;
|
|
m_afrgbaSource[uiPixel].fB = 0.0f;
|
|
}
|
|
|
|
|
|
// for RGB8A1, set source alpha to 0.0 or 1.0
|
|
// set punch through flag
|
|
if (imageformat == Image::Format::RGB8A1 ||
|
|
imageformat == Image::Format::SRGB8A1)
|
|
{
|
|
if (m_afrgbaSource[uiPixel].fA >= 0.5f)
|
|
{
|
|
m_afrgbaSource[uiPixel].fA = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
m_afrgbaSource[uiPixel].fA = 0.0f;
|
|
m_boolPunchThroughPixels = true;
|
|
}
|
|
}
|
|
|
|
if (m_afrgbaSource[uiPixel].fA == 1.0f || m_errormetric == RGBX)
|
|
{
|
|
uiOpaqueSourcePixels++;
|
|
}
|
|
else if (m_afrgbaSource[uiPixel].fA == 0.0f)
|
|
{
|
|
uiTransparentSourcePixels++;
|
|
}
|
|
|
|
}
|
|
|
|
uiPixel += 1;
|
|
}
|
|
}
|
|
|
|
if (uiOpaqueSourcePixels == PIXELS)
|
|
{
|
|
m_sourcealphamix = SourceAlphaMix::OPAQUE;
|
|
}
|
|
else if (uiTransparentSourcePixels == PIXELS)
|
|
{
|
|
m_sourcealphamix = SourceAlphaMix::TRANSPARENT;
|
|
}
|
|
else
|
|
{
|
|
m_sourcealphamix = SourceAlphaMix::TRANSLUCENT;
|
|
}
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// return a name for the encoding mode
|
|
//
|
|
const char * Block4x4::GetEncodingModeName(void)
|
|
{
|
|
|
|
switch (m_pencoding->GetMode())
|
|
{
|
|
case Block4x4Encoding::MODE_ETC1:
|
|
return "ETC1";
|
|
case Block4x4Encoding::MODE_T:
|
|
return "T";
|
|
case Block4x4Encoding::MODE_H:
|
|
return "H";
|
|
case Block4x4Encoding::MODE_PLANAR:
|
|
return "PLANAR";
|
|
default:
|
|
return "???";
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
//
|
|
|
|
}
|