summaryrefslogtreecommitdiff
path: root/thirdparty/etc2comp/EtcImage.cpp
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2017-06-13 19:17:13 -0300
committerGitHub <noreply@github.com>2017-06-13 19:17:13 -0300
commita8e845a474c01c4cde1416ed00e6ae456786c1fb (patch)
tree073d311971752a8fbd3df72def311c6f70e8e8de /thirdparty/etc2comp/EtcImage.cpp
parenta8a1f2e2a864e6b58d5bcf1c7e53a43cdb6d95d9 (diff)
parent6a9c990da72a737fa95d9e97d53f835706aea7c3 (diff)
Merge pull request #8548 from tagcup/etc2comp
Add ETC1/ETC2 compression support though etc2comp.
Diffstat (limited to 'thirdparty/etc2comp/EtcImage.cpp')
-rw-r--r--thirdparty/etc2comp/EtcImage.cpp685
1 files changed, 685 insertions, 0 deletions
diff --git a/thirdparty/etc2comp/EtcImage.cpp b/thirdparty/etc2comp/EtcImage.cpp
new file mode 100644
index 0000000000..7a1058844d
--- /dev/null
+++ b/thirdparty/etc2comp/EtcImage.cpp
@@ -0,0 +1,685 @@
+/*
+ * 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