686 lines
20 KiB
C++
686 lines
20 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.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
EtcImage.cpp
|
||
|
|
||
|
Image is an array of 4x4 blocks that represent the encoding of the source image
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "EtcConfig.h"
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "EtcImage.h"
|
||
|
|
||
|
#include "Etc.h"
|
||
|
#include "EtcBlock4x4.h"
|
||
|
#include "EtcBlock4x4EncodingBits.h"
|
||
|
#include "EtcSortedBlockList.h"
|
||
|
|
||
|
#if ETC_WINDOWS
|
||
|
#include <windows.h>
|
||
|
#endif
|
||
|
#include <ctime>
|
||
|
#include <chrono>
|
||
|
#include <future>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
// fix conflict with Block4x4::AlphaMix
|
||
|
#ifdef OPAQUE
|
||
|
#undef OPAQUE
|
||
|
#endif
|
||
|
#ifdef TRANSPARENT
|
||
|
#undef TRANSPARENT
|
||
|
#endif
|
||
|
|
||
|
namespace Etc
|
||
|
{
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
//
|
||
|
Image::Image(void)
|
||
|
{
|
||
|
m_encodingStatus = EncodingStatus::SUCCESS;
|
||
|
m_warningsToCapture = EncodingStatus::SUCCESS;
|
||
|
m_pafrgbaSource = nullptr;
|
||
|
|
||
|
m_pablock = nullptr;
|
||
|
|
||
|
m_encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN;
|
||
|
m_uiEncodingBitsBytes = 0;
|
||
|
m_paucEncodingBits = nullptr;
|
||
|
|
||
|
m_format = Format::UNKNOWN;
|
||
|
m_iNumOpaquePixels = 0;
|
||
|
m_iNumTranslucentPixels = 0;
|
||
|
m_iNumTransparentPixels = 0;
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// constructor using source image
|
||
|
// used to set state before Encode() is called
|
||
|
//
|
||
|
Image::Image(float *a_pafSourceRGBA, unsigned int a_uiSourceWidth,
|
||
|
unsigned int a_uiSourceHeight,
|
||
|
ErrorMetric a_errormetric)
|
||
|
{
|
||
|
m_encodingStatus = EncodingStatus::SUCCESS;
|
||
|
m_warningsToCapture = EncodingStatus::SUCCESS;
|
||
|
m_pafrgbaSource = (ColorFloatRGBA *) a_pafSourceRGBA;
|
||
|
m_uiSourceWidth = a_uiSourceWidth;
|
||
|
m_uiSourceHeight = a_uiSourceHeight;
|
||
|
|
||
|
m_uiExtendedWidth = CalcExtendedDimension((unsigned short)m_uiSourceWidth);
|
||
|
m_uiExtendedHeight = CalcExtendedDimension((unsigned short)m_uiSourceHeight);
|
||
|
|
||
|
m_uiBlockColumns = m_uiExtendedWidth >> 2;
|
||
|
m_uiBlockRows = m_uiExtendedHeight >> 2;
|
||
|
|
||
|
m_pablock = new Block4x4[GetNumberOfBlocks()];
|
||
|
assert(m_pablock);
|
||
|
|
||
|
m_format = Format::UNKNOWN;
|
||
|
|
||
|
m_encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN;
|
||
|
m_uiEncodingBitsBytes = 0;
|
||
|
m_paucEncodingBits = nullptr;
|
||
|
|
||
|
m_errormetric = a_errormetric;
|
||
|
m_fEffort = 0.0f;
|
||
|
|
||
|
m_iEncodeTime_ms = -1;
|
||
|
|
||
|
m_iNumOpaquePixels = 0;
|
||
|
m_iNumTranslucentPixels = 0;
|
||
|
m_iNumTransparentPixels = 0;
|
||
|
m_bVerboseOutput = false;
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// constructor using encoding bits
|
||
|
// recreates encoding state using a previously encoded image
|
||
|
//
|
||
|
Image::Image(Format a_format,
|
||
|
unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight,
|
||
|
unsigned char *a_paucEncidingBits, unsigned int a_uiEncodingBitsBytes,
|
||
|
Image *a_pimageSource, ErrorMetric a_errormetric)
|
||
|
{
|
||
|
m_encodingStatus = EncodingStatus::SUCCESS;
|
||
|
m_pafrgbaSource = nullptr;
|
||
|
m_uiSourceWidth = a_uiSourceWidth;
|
||
|
m_uiSourceHeight = a_uiSourceHeight;
|
||
|
|
||
|
m_uiExtendedWidth = CalcExtendedDimension((unsigned short)m_uiSourceWidth);
|
||
|
m_uiExtendedHeight = CalcExtendedDimension((unsigned short)m_uiSourceHeight);
|
||
|
|
||
|
m_uiBlockColumns = m_uiExtendedWidth >> 2;
|
||
|
m_uiBlockRows = m_uiExtendedHeight >> 2;
|
||
|
|
||
|
unsigned int uiBlocks = GetNumberOfBlocks();
|
||
|
|
||
|
m_pablock = new Block4x4[uiBlocks];
|
||
|
assert(m_pablock);
|
||
|
|
||
|
m_format = a_format;
|
||
|
|
||
|
m_iNumOpaquePixels = 0;
|
||
|
m_iNumTranslucentPixels = 0;
|
||
|
m_iNumTransparentPixels = 0;
|
||
|
|
||
|
m_encodingbitsformat = DetermineEncodingBitsFormat(m_format);
|
||
|
if (m_encodingbitsformat == Block4x4EncodingBits::Format::UNKNOWN)
|
||
|
{
|
||
|
AddToEncodingStatus(ERROR_UNKNOWN_FORMAT);
|
||
|
return;
|
||
|
}
|
||
|
m_uiEncodingBitsBytes = a_uiEncodingBitsBytes;
|
||
|
m_paucEncodingBits = a_paucEncidingBits;
|
||
|
|
||
|
m_errormetric = a_errormetric;
|
||
|
m_fEffort = 0.0f;
|
||
|
m_bVerboseOutput = false;
|
||
|
m_iEncodeTime_ms = -1;
|
||
|
|
||
|
unsigned char *paucEncodingBits = m_paucEncodingBits;
|
||
|
unsigned int uiEncodingBitsBytesPerBlock = Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat);
|
||
|
|
||
|
unsigned int uiH = 0;
|
||
|
unsigned int uiV = 0;
|
||
|
for (unsigned int uiBlock = 0; uiBlock < uiBlocks; uiBlock++)
|
||
|
{
|
||
|
m_pablock[uiBlock].InitFromEtcEncodingBits(a_format, uiH, uiV, paucEncodingBits,
|
||
|
a_pimageSource, a_errormetric);
|
||
|
paucEncodingBits += uiEncodingBitsBytesPerBlock;
|
||
|
uiH += 4;
|
||
|
if (uiH >= m_uiSourceWidth)
|
||
|
{
|
||
|
uiH = 0;
|
||
|
uiV += 4;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
//
|
||
|
Image::~Image(void)
|
||
|
{
|
||
|
if (m_pablock != nullptr)
|
||
|
{
|
||
|
delete[] m_pablock;
|
||
|
m_pablock = nullptr;
|
||
|
}
|
||
|
|
||
|
/*if (m_paucEncodingBits != nullptr)
|
||
|
{
|
||
|
delete[] m_paucEncodingBits;
|
||
|
m_paucEncodingBits = nullptr;
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// encode an image
|
||
|
// create a set of encoding bits that conforms to a_format
|
||
|
// find best fit using a_errormetric
|
||
|
// explore a range of possible encodings based on a_fEffort (range = [0:100])
|
||
|
// speed up process using a_uiJobs as the number of process threads (a_uiJobs must not excede a_uiMaxJobs)
|
||
|
//
|
||
|
Image::EncodingStatus Image::Encode(Format a_format, ErrorMetric a_errormetric, float a_fEffort, unsigned int a_uiJobs, unsigned int a_uiMaxJobs)
|
||
|
{
|
||
|
|
||
|
auto start = std::chrono::steady_clock::now();
|
||
|
|
||
|
m_encodingStatus = EncodingStatus::SUCCESS;
|
||
|
|
||
|
m_format = a_format;
|
||
|
m_errormetric = a_errormetric;
|
||
|
m_fEffort = a_fEffort;
|
||
|
|
||
|
if (m_errormetric < 0 || m_errormetric > ERROR_METRICS)
|
||
|
{
|
||
|
AddToEncodingStatus(ERROR_UNKNOWN_ERROR_METRIC);
|
||
|
return m_encodingStatus;
|
||
|
}
|
||
|
|
||
|
if (m_fEffort < ETCCOMP_MIN_EFFORT_LEVEL)
|
||
|
{
|
||
|
AddToEncodingStatus(WARNING_EFFORT_OUT_OF_RANGE);
|
||
|
m_fEffort = ETCCOMP_MIN_EFFORT_LEVEL;
|
||
|
}
|
||
|
else if (m_fEffort > ETCCOMP_MAX_EFFORT_LEVEL)
|
||
|
{
|
||
|
AddToEncodingStatus(WARNING_EFFORT_OUT_OF_RANGE);
|
||
|
m_fEffort = ETCCOMP_MAX_EFFORT_LEVEL;
|
||
|
}
|
||
|
if (a_uiJobs < 1)
|
||
|
{
|
||
|
a_uiJobs = 1;
|
||
|
AddToEncodingStatus(WARNING_JOBS_OUT_OF_RANGE);
|
||
|
}
|
||
|
else if (a_uiJobs > a_uiMaxJobs)
|
||
|
{
|
||
|
a_uiJobs = a_uiMaxJobs;
|
||
|
AddToEncodingStatus(WARNING_JOBS_OUT_OF_RANGE);
|
||
|
}
|
||
|
|
||
|
m_encodingbitsformat = DetermineEncodingBitsFormat(m_format);
|
||
|
|
||
|
if (m_encodingbitsformat == Block4x4EncodingBits::Format::UNKNOWN)
|
||
|
{
|
||
|
AddToEncodingStatus(ERROR_UNKNOWN_FORMAT);
|
||
|
return m_encodingStatus;
|
||
|
}
|
||
|
|
||
|
assert(m_paucEncodingBits == nullptr);
|
||
|
m_uiEncodingBitsBytes = GetNumberOfBlocks() * Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat);
|
||
|
m_paucEncodingBits = new unsigned char[m_uiEncodingBitsBytes];
|
||
|
|
||
|
InitBlocksAndBlockSorter();
|
||
|
|
||
|
|
||
|
std::future<void> *handle = new std::future<void>[a_uiMaxJobs];
|
||
|
|
||
|
unsigned int uiNumThreadsNeeded = 0;
|
||
|
unsigned int uiUnfinishedBlocks = GetNumberOfBlocks();
|
||
|
|
||
|
uiNumThreadsNeeded = (uiUnfinishedBlocks < a_uiJobs) ? uiUnfinishedBlocks : a_uiJobs;
|
||
|
|
||
|
for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
|
||
|
{
|
||
|
handle[i] = async(std::launch::async, &Image::RunFirstPass, this, i, uiNumThreadsNeeded);
|
||
|
}
|
||
|
|
||
|
RunFirstPass(uiNumThreadsNeeded - 1, uiNumThreadsNeeded);
|
||
|
|
||
|
for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
|
||
|
{
|
||
|
handle[i].get();
|
||
|
}
|
||
|
|
||
|
// perform effort-based encoding
|
||
|
if (m_fEffort > ETCCOMP_MIN_EFFORT_LEVEL)
|
||
|
{
|
||
|
unsigned int uiFinishedBlocks = 0;
|
||
|
unsigned int uiTotalEffortBlocks = static_cast<unsigned int>(roundf(0.01f * m_fEffort * GetNumberOfBlocks()));
|
||
|
|
||
|
if (m_bVerboseOutput)
|
||
|
{
|
||
|
printf("effortblocks = %d\n", uiTotalEffortBlocks);
|
||
|
}
|
||
|
unsigned int uiPass = 0;
|
||
|
while (1)
|
||
|
{
|
||
|
if (m_bVerboseOutput)
|
||
|
{
|
||
|
uiPass++;
|
||
|
printf("pass %u\n", uiPass);
|
||
|
}
|
||
|
m_psortedblocklist->Sort();
|
||
|
uiUnfinishedBlocks = m_psortedblocklist->GetNumberOfSortedBlocks();
|
||
|
uiFinishedBlocks = GetNumberOfBlocks() - uiUnfinishedBlocks;
|
||
|
if (m_bVerboseOutput)
|
||
|
{
|
||
|
printf(" %u unfinished blocks\n", uiUnfinishedBlocks);
|
||
|
// m_psortedblocklist->Print();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//stop enocding when we did enough to satify the effort percentage
|
||
|
if (uiFinishedBlocks >= uiTotalEffortBlocks)
|
||
|
{
|
||
|
if (m_bVerboseOutput)
|
||
|
{
|
||
|
printf("Finished %d Blocks out of %d\n", uiFinishedBlocks, uiTotalEffortBlocks);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
unsigned int uiIteratedBlocks = 0;
|
||
|
unsigned int blocksToIterateThisPass = (uiTotalEffortBlocks - uiFinishedBlocks);
|
||
|
uiNumThreadsNeeded = (uiUnfinishedBlocks < a_uiJobs) ? uiUnfinishedBlocks : a_uiJobs;
|
||
|
|
||
|
if (uiNumThreadsNeeded <= 1)
|
||
|
{
|
||
|
//since we already how many blocks each thread will process
|
||
|
//cap the thread limit to do the proper amount of work, and not more
|
||
|
uiIteratedBlocks = IterateThroughWorstBlocks(blocksToIterateThisPass, 0, 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//we have a lot of work to do, so lets multi thread it
|
||
|
std::future<unsigned int> *handleToBlockEncoders = new std::future<unsigned int>[uiNumThreadsNeeded-1];
|
||
|
|
||
|
for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
|
||
|
{
|
||
|
handleToBlockEncoders[i] = async(std::launch::async, &Image::IterateThroughWorstBlocks, this, blocksToIterateThisPass, i, uiNumThreadsNeeded);
|
||
|
}
|
||
|
uiIteratedBlocks = IterateThroughWorstBlocks(blocksToIterateThisPass, uiNumThreadsNeeded - 1, uiNumThreadsNeeded);
|
||
|
|
||
|
for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
|
||
|
{
|
||
|
uiIteratedBlocks += handleToBlockEncoders[i].get();
|
||
|
}
|
||
|
|
||
|
delete[] handleToBlockEncoders;
|
||
|
}
|
||
|
|
||
|
if (m_bVerboseOutput)
|
||
|
{
|
||
|
printf(" %u iterated blocks\n", uiIteratedBlocks);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// generate Etc2-compatible bit-format 4x4 blocks
|
||
|
for (int i = 0; i < (int)a_uiJobs - 1; i++)
|
||
|
{
|
||
|
handle[i] = async(std::launch::async, &Image::SetEncodingBits, this, i, a_uiJobs);
|
||
|
}
|
||
|
SetEncodingBits(a_uiJobs - 1, a_uiJobs);
|
||
|
|
||
|
for (int i = 0; i < (int)a_uiJobs - 1; i++)
|
||
|
{
|
||
|
handle[i].get();
|
||
|
}
|
||
|
|
||
|
auto end = std::chrono::steady_clock::now();
|
||
|
std::chrono::milliseconds elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||
|
m_iEncodeTime_ms = (int)elapsed.count();
|
||
|
|
||
|
delete[] handle;
|
||
|
delete m_psortedblocklist;
|
||
|
return m_encodingStatus;
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// iterate the encoding thru the blocks with the worst error
|
||
|
// stop when a_uiMaxBlocks blocks have been iterated
|
||
|
// split the blocks between the process threads using a_uiMultithreadingOffset and a_uiMultithreadingStride
|
||
|
//
|
||
|
unsigned int Image::IterateThroughWorstBlocks(unsigned int a_uiMaxBlocks,
|
||
|
unsigned int a_uiMultithreadingOffset,
|
||
|
unsigned int a_uiMultithreadingStride)
|
||
|
{
|
||
|
assert(a_uiMultithreadingStride > 0);
|
||
|
unsigned int uiIteratedBlocks = a_uiMultithreadingOffset;
|
||
|
|
||
|
SortedBlockList::Link *plink = m_psortedblocklist->GetLinkToFirstBlock();
|
||
|
for (plink = plink->Advance(a_uiMultithreadingOffset);
|
||
|
plink != nullptr;
|
||
|
plink = plink->Advance(a_uiMultithreadingStride) )
|
||
|
{
|
||
|
if (uiIteratedBlocks >= a_uiMaxBlocks)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
plink->GetBlock()->PerformEncodingIteration(m_fEffort);
|
||
|
|
||
|
uiIteratedBlocks += a_uiMultithreadingStride;
|
||
|
}
|
||
|
|
||
|
return uiIteratedBlocks;
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// determine which warnings to check for during Encode() based on encoding format
|
||
|
//
|
||
|
void Image::FindEncodingWarningTypesForCurFormat()
|
||
|
{
|
||
|
TrackEncodingWarning(WARNING_ALL_TRANSPARENT_PIXELS);
|
||
|
TrackEncodingWarning(WARNING_SOME_RGBA_NOT_0_TO_1);
|
||
|
switch (m_format)
|
||
|
{
|
||
|
case Image::Format::ETC1:
|
||
|
case Image::Format::RGB8:
|
||
|
case Image::Format::SRGB8:
|
||
|
TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS);
|
||
|
TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
|
||
|
break;
|
||
|
|
||
|
case Image::Format::RGB8A1:
|
||
|
case Image::Format::SRGB8A1:
|
||
|
TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
|
||
|
TrackEncodingWarning(WARNING_ALL_OPAQUE_PIXELS);
|
||
|
break;
|
||
|
case Image::Format::RGBA8:
|
||
|
case Image::Format::SRGBA8:
|
||
|
TrackEncodingWarning(WARNING_ALL_OPAQUE_PIXELS);
|
||
|
break;
|
||
|
|
||
|
case Image::Format::R11:
|
||
|
case Image::Format::SIGNED_R11:
|
||
|
TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS);
|
||
|
TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
|
||
|
TrackEncodingWarning(WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO);
|
||
|
TrackEncodingWarning(WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO);
|
||
|
break;
|
||
|
|
||
|
case Image::Format::RG11:
|
||
|
case Image::Format::SIGNED_RG11:
|
||
|
TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS);
|
||
|
TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
|
||
|
TrackEncodingWarning(WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO);
|
||
|
break;
|
||
|
case Image::Format::FORMATS:
|
||
|
case Image::Format::UNKNOWN:
|
||
|
default:
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// examine source pixels to check for warnings
|
||
|
//
|
||
|
void Image::FindAndSetEncodingWarnings()
|
||
|
{
|
||
|
int numPixels = (m_uiBlockRows * 4) * (m_uiBlockColumns * 4);
|
||
|
if (m_iNumOpaquePixels == numPixels)
|
||
|
{
|
||
|
AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_ALL_OPAQUE_PIXELS);
|
||
|
}
|
||
|
if (m_iNumOpaquePixels < numPixels)
|
||
|
{
|
||
|
AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_NON_OPAQUE_PIXELS);
|
||
|
}
|
||
|
if (m_iNumTranslucentPixels > 0)
|
||
|
{
|
||
|
AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_TRANSLUCENT_PIXELS);
|
||
|
}
|
||
|
if (m_iNumTransparentPixels == numPixels)
|
||
|
{
|
||
|
AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_ALL_TRANSPARENT_PIXELS);
|
||
|
}
|
||
|
if (m_numColorValues.fB > 0.0f)
|
||
|
{
|
||
|
AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO);
|
||
|
}
|
||
|
if (m_numColorValues.fG > 0.0f)
|
||
|
{
|
||
|
AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO);
|
||
|
}
|
||
|
|
||
|
if (m_numOutOfRangeValues.fR > 0.0f || m_numOutOfRangeValues.fG > 0.0f)
|
||
|
{
|
||
|
AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_RGBA_NOT_0_TO_1);
|
||
|
}
|
||
|
if (m_numOutOfRangeValues.fB > 0.0f || m_numOutOfRangeValues.fA > 0.0f)
|
||
|
{
|
||
|
AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_RGBA_NOT_0_TO_1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// return a string name for a given image format
|
||
|
//
|
||
|
const char * Image::EncodingFormatToString(Image::Format a_format)
|
||
|
{
|
||
|
switch (a_format)
|
||
|
{
|
||
|
case Image::Format::ETC1:
|
||
|
return "ETC1";
|
||
|
case Image::Format::RGB8:
|
||
|
return "RGB8";
|
||
|
case Image::Format::SRGB8:
|
||
|
return "SRGB8";
|
||
|
|
||
|
case Image::Format::RGB8A1:
|
||
|
return "RGB8A1";
|
||
|
case Image::Format::SRGB8A1:
|
||
|
return "SRGB8A1";
|
||
|
case Image::Format::RGBA8:
|
||
|
return "RGBA8";
|
||
|
case Image::Format::SRGBA8:
|
||
|
return "SRGBA8";
|
||
|
|
||
|
case Image::Format::R11:
|
||
|
return "R11";
|
||
|
case Image::Format::SIGNED_R11:
|
||
|
return "SIGNED_R11";
|
||
|
|
||
|
case Image::Format::RG11:
|
||
|
return "RG11";
|
||
|
case Image::Format::SIGNED_RG11:
|
||
|
return "SIGNED_RG11";
|
||
|
case Image::Format::FORMATS:
|
||
|
case Image::Format::UNKNOWN:
|
||
|
default:
|
||
|
return "UNKNOWN";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// return a string name for the image's format
|
||
|
//
|
||
|
const char * Image::EncodingFormatToString(void)
|
||
|
{
|
||
|
return EncodingFormatToString(m_format);
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// init image blocks prior to encoding
|
||
|
// init block sorter for subsequent sortings
|
||
|
// check for encoding warnings
|
||
|
//
|
||
|
void Image::InitBlocksAndBlockSorter(void)
|
||
|
{
|
||
|
|
||
|
FindEncodingWarningTypesForCurFormat();
|
||
|
|
||
|
// init each block
|
||
|
Block4x4 *pblock = m_pablock;
|
||
|
unsigned char *paucEncodingBits = m_paucEncodingBits;
|
||
|
for (unsigned int uiBlockRow = 0; uiBlockRow < m_uiBlockRows; uiBlockRow++)
|
||
|
{
|
||
|
unsigned int uiBlockV = uiBlockRow * 4;
|
||
|
|
||
|
for (unsigned int uiBlockColumn = 0; uiBlockColumn < m_uiBlockColumns; uiBlockColumn++)
|
||
|
{
|
||
|
unsigned int uiBlockH = uiBlockColumn * 4;
|
||
|
|
||
|
pblock->InitFromSource(this, uiBlockH, uiBlockV, paucEncodingBits, m_errormetric);
|
||
|
|
||
|
paucEncodingBits += Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat);
|
||
|
|
||
|
pblock++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FindAndSetEncodingWarnings();
|
||
|
|
||
|
// init block sorter
|
||
|
{
|
||
|
m_psortedblocklist = new SortedBlockList(GetNumberOfBlocks(), 100);
|
||
|
|
||
|
for (unsigned int uiBlock = 0; uiBlock < GetNumberOfBlocks(); uiBlock++)
|
||
|
{
|
||
|
pblock = &m_pablock[uiBlock];
|
||
|
m_psortedblocklist->AddBlock(pblock);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// run the first pass of the encoder
|
||
|
// the encoder generally finds a reasonable, fast encoding
|
||
|
// this is run on all blocks regardless of effort to ensure that all blocks have a valid encoding
|
||
|
//
|
||
|
void Image::RunFirstPass(unsigned int a_uiMultithreadingOffset, unsigned int a_uiMultithreadingStride)
|
||
|
{
|
||
|
assert(a_uiMultithreadingStride > 0);
|
||
|
|
||
|
for (unsigned int uiBlock = a_uiMultithreadingOffset;
|
||
|
uiBlock < GetNumberOfBlocks();
|
||
|
uiBlock += a_uiMultithreadingStride)
|
||
|
{
|
||
|
Block4x4 *pblock = &m_pablock[uiBlock];
|
||
|
pblock->PerformEncodingIteration(m_fEffort);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// set the encoding bits (for the output file) based on the best encoding for each block
|
||
|
//
|
||
|
void Image::SetEncodingBits(unsigned int a_uiMultithreadingOffset,
|
||
|
unsigned int a_uiMultithreadingStride)
|
||
|
{
|
||
|
assert(a_uiMultithreadingStride > 0);
|
||
|
|
||
|
for (unsigned int uiBlock = a_uiMultithreadingOffset;
|
||
|
uiBlock < GetNumberOfBlocks();
|
||
|
uiBlock += a_uiMultithreadingStride)
|
||
|
{
|
||
|
Block4x4 *pblock = &m_pablock[uiBlock];
|
||
|
pblock->SetEncodingBitsFromEncoding();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// return the image error
|
||
|
// image error is the sum of all block errors
|
||
|
//
|
||
|
float Image::GetError(void)
|
||
|
{
|
||
|
float fError = 0.0f;
|
||
|
|
||
|
for (unsigned int uiBlock = 0; uiBlock < GetNumberOfBlocks(); uiBlock++)
|
||
|
{
|
||
|
Block4x4 *pblock = &m_pablock[uiBlock];
|
||
|
fError += pblock->GetError();
|
||
|
}
|
||
|
|
||
|
return fError;
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// determine the encoding bits format based on the encoding format
|
||
|
// the encoding bits format is a family of bit encodings that are shared across various encoding formats
|
||
|
//
|
||
|
Block4x4EncodingBits::Format Image::DetermineEncodingBitsFormat(Format a_format)
|
||
|
{
|
||
|
Block4x4EncodingBits::Format encodingbitsformat;
|
||
|
|
||
|
// determine encoding bits format from image format
|
||
|
switch (a_format)
|
||
|
{
|
||
|
case Format::ETC1:
|
||
|
case Format::RGB8:
|
||
|
case Format::SRGB8:
|
||
|
encodingbitsformat = Block4x4EncodingBits::Format::RGB8;
|
||
|
break;
|
||
|
|
||
|
case Format::RGBA8:
|
||
|
case Format::SRGBA8:
|
||
|
encodingbitsformat = Block4x4EncodingBits::Format::RGBA8;
|
||
|
break;
|
||
|
|
||
|
case Format::R11:
|
||
|
case Format::SIGNED_R11:
|
||
|
encodingbitsformat = Block4x4EncodingBits::Format::R11;
|
||
|
break;
|
||
|
|
||
|
case Format::RG11:
|
||
|
case Format::SIGNED_RG11:
|
||
|
encodingbitsformat = Block4x4EncodingBits::Format::RG11;
|
||
|
break;
|
||
|
|
||
|
case Format::RGB8A1:
|
||
|
case Format::SRGB8A1:
|
||
|
encodingbitsformat = Block4x4EncodingBits::Format::RGB8A1;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return encodingbitsformat;
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
//
|
||
|
|
||
|
} // namespace Etc
|