summaryrefslogtreecommitdiff
path: root/thirdparty
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
parenta8a1f2e2a864e6b58d5bcf1c7e53a43cdb6d95d9 (diff)
parent6a9c990da72a737fa95d9e97d53f835706aea7c3 (diff)
Merge pull request #8548 from tagcup/etc2comp
Add ETC1/ETC2 compression support though etc2comp.
Diffstat (limited to 'thirdparty')
-rw-r--r--thirdparty/README.md23
-rw-r--r--thirdparty/etc2comp/AUTHORS7
-rw-r--r--thirdparty/etc2comp/Etc.cpp128
-rw-r--r--thirdparty/etc2comp/Etc.h71
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4.cpp425
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4.h172
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding.cpp261
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding.h148
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4EncodingBits.h315
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.cpp1281
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.h186
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_R11.cpp429
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_R11.h122
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.cpp447
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.h86
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.cpp1730
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.h96
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.cpp1819
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.h129
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.cpp474
-rw-r--r--thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.h121
-rw-r--r--thirdparty/etc2comp/EtcColor.h64
-rw-r--r--thirdparty/etc2comp/EtcColorFloatRGBA.h321
-rw-r--r--thirdparty/etc2comp/EtcConfig.h67
-rw-r--r--thirdparty/etc2comp/EtcDifferentialTrys.cpp173
-rw-r--r--thirdparty/etc2comp/EtcDifferentialTrys.h97
-rw-r--r--thirdparty/etc2comp/EtcErrorMetric.h54
-rw-r--r--thirdparty/etc2comp/EtcFile.cpp390
-rw-r--r--thirdparty/etc2comp/EtcFile.h136
-rw-r--r--thirdparty/etc2comp/EtcFileHeader.cpp185
-rw-r--r--thirdparty/etc2comp/EtcFileHeader.h146
-rw-r--r--thirdparty/etc2comp/EtcFilter.cpp401
-rw-r--r--thirdparty/etc2comp/EtcFilter.h244
-rw-r--r--thirdparty/etc2comp/EtcImage.cpp685
-rw-r--r--thirdparty/etc2comp/EtcImage.h249
-rw-r--r--thirdparty/etc2comp/EtcIndividualTrys.cpp85
-rw-r--r--thirdparty/etc2comp/EtcIndividualTrys.h95
-rw-r--r--thirdparty/etc2comp/EtcMath.cpp64
-rw-r--r--thirdparty/etc2comp/EtcMath.h40
-rw-r--r--thirdparty/etc2comp/EtcSortedBlockList.cpp228
-rw-r--r--thirdparty/etc2comp/EtcSortedBlockList.h124
-rw-r--r--thirdparty/etc2comp/LICENSE202
-rw-r--r--thirdparty/etc2comp/README.md197
-rw-r--r--thirdparty/rg-etc1/rg_etc1.cpp2446
-rw-r--r--thirdparty/rg-etc1/rg_etc1.h76
45 files changed, 12706 insertions, 2533 deletions
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 1f0543e6dc..c3dd1dff6e 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -37,6 +37,18 @@ Check the diff of enet.h, protocol.c, and host.c with the 1.3.13
tarball before the next update.
+## etc2comp
+
+- Upstream: https://github.com/google/etc2comp
+- Version: 9cd0f9c (git)
+- License: Apache
+
+Files extracted from upstream source:
+
+- all .cpp and .h files in EtcLib/
+- README.md, LICENSE, AUTHORS
+
+
## fonts
- Upstream: ?
@@ -296,17 +308,6 @@ Files extracted from upstream source:
- LICENSE.TXT
-## rg-etc1
-
-- Upstream: https://github.com/richgel999/rg-etc1
-- Version: 1.04
-- License: zlib
-
-Files extracted from upstream source:
-
-- `rg_etc1.{cpp,h}`
-
-
## rtaudio
- Upstream: http://www.music.mcgill.ca/~gary/rtaudio/
diff --git a/thirdparty/etc2comp/AUTHORS b/thirdparty/etc2comp/AUTHORS
new file mode 100644
index 0000000000..32daca27fe
--- /dev/null
+++ b/thirdparty/etc2comp/AUTHORS
@@ -0,0 +1,7 @@
+# This is the list of Etc2Comp authors for copyright purposes.
+#
+# This does not necessarily list everyone who has contributed code, since in
+# some cases, their employer may be the copyright holder. To see the full list
+# of contributors, see the revision history in source control.
+Google Inc.
+Blue Shift Inc.
diff --git a/thirdparty/etc2comp/Etc.cpp b/thirdparty/etc2comp/Etc.cpp
new file mode 100644
index 0000000000..a5ee706048
--- /dev/null
+++ b/thirdparty/etc2comp/Etc.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+#include "EtcConfig.h"
+#include "Etc.h"
+#include "EtcFilter.h"
+
+#include <string.h>
+
+namespace Etc
+{
+ // ----------------------------------------------------------------------------------------------------
+ // C-style inteface to the encoder
+ //
+ void Encode(float *a_pafSourceRGBA,
+ unsigned int a_uiSourceWidth,
+ unsigned int a_uiSourceHeight,
+ Image::Format a_format,
+ ErrorMetric a_eErrMetric,
+ float a_fEffort,
+ unsigned int a_uiJobs,
+ unsigned int a_uiMaxJobs,
+ unsigned char **a_ppaucEncodingBits,
+ unsigned int *a_puiEncodingBitsBytes,
+ unsigned int *a_puiExtendedWidth,
+ unsigned int *a_puiExtendedHeight,
+ int *a_piEncodingTime_ms, bool a_bVerboseOutput)
+ {
+
+ Image image(a_pafSourceRGBA, a_uiSourceWidth,
+ a_uiSourceHeight,
+ a_eErrMetric);
+ image.m_bVerboseOutput = a_bVerboseOutput;
+ image.Encode(a_format, a_eErrMetric, a_fEffort, a_uiJobs, a_uiMaxJobs);
+
+ *a_ppaucEncodingBits = image.GetEncodingBits();
+ *a_puiEncodingBitsBytes = image.GetEncodingBitsBytes();
+ *a_puiExtendedWidth = image.GetExtendedWidth();
+ *a_puiExtendedHeight = image.GetExtendedHeight();
+ *a_piEncodingTime_ms = image.GetEncodingTimeMs();
+ }
+
+ void EncodeMipmaps(float *a_pafSourceRGBA,
+ unsigned int a_uiSourceWidth,
+ unsigned int a_uiSourceHeight,
+ Image::Format a_format,
+ ErrorMetric a_eErrMetric,
+ float a_fEffort,
+ unsigned int a_uiJobs,
+ unsigned int a_uiMaxJobs,
+ unsigned int a_uiMaxMipmaps,
+ unsigned int a_uiMipFilterFlags,
+ RawImage* a_pMipmapImages,
+ int *a_piEncodingTime_ms,
+ bool a_bVerboseOutput)
+ {
+ auto mipWidth = a_uiSourceWidth;
+ auto mipHeight = a_uiSourceHeight;
+ int totalEncodingTime = 0;
+ for(unsigned int mip = 0; mip < a_uiMaxMipmaps && mipWidth >= 1 && mipHeight >= 1; mip++)
+ {
+ float* pImageData = nullptr;
+ float* pMipImage = nullptr;
+
+ if(mip == 0)
+ {
+ pImageData = a_pafSourceRGBA;
+ }
+ else
+ {
+ pMipImage = new float[mipWidth*mipHeight*4];
+ if(FilterTwoPass(a_pafSourceRGBA, a_uiSourceWidth, a_uiSourceHeight, pMipImage, mipWidth, mipHeight, a_uiMipFilterFlags, Etc::FilterLanczos3) )
+ {
+ pImageData = pMipImage;
+ }
+ }
+
+ if ( pImageData )
+ {
+
+ Image image(pImageData, mipWidth, mipHeight, a_eErrMetric);
+
+ image.m_bVerboseOutput = a_bVerboseOutput;
+ image.Encode(a_format, a_eErrMetric, a_fEffort, a_uiJobs, a_uiMaxJobs);
+
+ a_pMipmapImages[mip].paucEncodingBits = std::shared_ptr<unsigned char>(image.GetEncodingBits(), [](unsigned char *p) { delete[] p; });
+ a_pMipmapImages[mip].uiEncodingBitsBytes = image.GetEncodingBitsBytes();
+ a_pMipmapImages[mip].uiExtendedWidth = image.GetExtendedWidth();
+ a_pMipmapImages[mip].uiExtendedHeight = image.GetExtendedHeight();
+
+ totalEncodingTime += image.GetEncodingTimeMs();
+ }
+
+ if(pMipImage)
+ {
+ delete[] pMipImage;
+ }
+
+ if (!pImageData)
+ {
+ break;
+ }
+
+ mipWidth >>= 1;
+ mipHeight >>= 1;
+ }
+
+ *a_piEncodingTime_ms = totalEncodingTime;
+ }
+
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+}
diff --git a/thirdparty/etc2comp/Etc.h b/thirdparty/etc2comp/Etc.h
new file mode 100644
index 0000000000..439388d649
--- /dev/null
+++ b/thirdparty/etc2comp/Etc.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcConfig.h"
+#include "EtcImage.h"
+#include "EtcColor.h"
+#include "EtcErrorMetric.h"
+#include <memory>
+
+#define ETCCOMP_MIN_EFFORT_LEVEL (0.0f)
+#define ETCCOMP_DEFAULT_EFFORT_LEVEL (40.0f)
+#define ETCCOMP_MAX_EFFORT_LEVEL (100.0f)
+
+namespace Etc
+{
+ class Block4x4EncodingBits;
+
+ struct RawImage
+ {
+ int uiExtendedWidth;
+ int uiExtendedHeight;
+ unsigned int uiEncodingBitsBytes;
+ std::shared_ptr<unsigned char> paucEncodingBits;
+ };
+
+
+
+ // C-style inteface to the encoder
+ void Encode(float *a_pafSourceRGBA,
+ unsigned int a_uiSourceWidth,
+ unsigned int a_uiSourceHeight,
+ Image::Format a_format,
+ ErrorMetric a_eErrMetric,
+ float a_fEffort,
+ unsigned int a_uiJobs,
+ unsigned int a_uimaxJobs,
+ unsigned char **a_ppaucEncodingBits,
+ unsigned int *a_puiEncodingBitsBytes,
+ unsigned int *a_puiExtendedWidth,
+ unsigned int *a_puiExtendedHeight,
+ int *a_piEncodingTime_ms, bool a_bVerboseOutput = false);
+
+ void EncodeMipmaps(float *a_pafSourceRGBA,
+ unsigned int a_uiSourceWidth,
+ unsigned int a_uiSourceHeight,
+ Image::Format a_format,
+ ErrorMetric a_eErrMetric,
+ float a_fEffort,
+ unsigned int a_uiJobs,
+ unsigned int a_uiMaxJobs,
+ unsigned int a_uiMaxMipmaps,
+ unsigned int a_uiMipFilterFlags,
+ RawImage* a_pMipmaps,
+ int *a_piEncodingTime_ms, bool a_bVerboseOutput = false);
+
+}
diff --git a/thirdparty/etc2comp/EtcBlock4x4.cpp b/thirdparty/etc2comp/EtcBlock4x4.cpp
new file mode 100644
index 0000000000..3082fe60db
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4.cpp
@@ -0,0 +1,425 @@
+/*
+ * 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 "???";
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+}
diff --git a/thirdparty/etc2comp/EtcBlock4x4.h b/thirdparty/etc2comp/EtcBlock4x4.h
new file mode 100644
index 0000000000..0fd30c598d
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4.h
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColor.h"
+#include "EtcColorFloatRGBA.h"
+#include "EtcErrorMetric.h"
+#include "EtcImage.h"
+#include "EtcBlock4x4Encoding.h"
+
+namespace Etc
+{
+ class Block4x4EncodingBits;
+
+ class Block4x4
+ {
+ public:
+
+ static const unsigned int ROWS = 4;
+ static const unsigned int COLUMNS = 4;
+ static const unsigned int PIXELS = ROWS * COLUMNS;
+
+ // the alpha mix for a 4x4 block of pixels
+ enum class SourceAlphaMix
+ {
+ UNKNOWN,
+ //
+ OPAQUE, // all 1.0
+ TRANSPARENT, // all 0.0 or NAN
+ TRANSLUCENT // not all opaque or transparent
+ };
+
+ typedef void (Block4x4::*EncoderFunctionPtr)(void);
+
+ Block4x4(void);
+ ~Block4x4();
+ void InitFromSource(Image *a_pimageSource,
+ unsigned int a_uiSourceH,
+ unsigned int a_uiSourceV,
+ unsigned char *a_paucEncodingBits,
+ ErrorMetric a_errormetric);
+
+ void InitFromEtcEncodingBits(Image::Format a_imageformat,
+ unsigned int a_uiSourceH,
+ unsigned int a_uiSourceV,
+ unsigned char *a_paucEncodingBits,
+ Image *a_pimageSource,
+ ErrorMetric a_errormetric);
+
+ // return true if final iteration was performed
+ inline void PerformEncodingIteration(float a_fEffort)
+ {
+ m_pencoding->PerformIteration(a_fEffort);
+ }
+
+ inline void SetEncodingBitsFromEncoding(void)
+ {
+ m_pencoding->SetEncodingBits();
+ }
+
+ inline unsigned int GetSourceH(void)
+ {
+ return m_uiSourceH;
+ }
+
+ inline unsigned int GetSourceV(void)
+ {
+ return m_uiSourceV;
+ }
+
+ inline float GetError(void)
+ {
+ return m_pencoding->GetError();
+ }
+
+ static const unsigned int s_auiPixelOrderHScan[PIXELS];
+
+ inline ColorFloatRGBA * GetDecodedColors(void)
+ {
+ return m_pencoding->GetDecodedColors();
+ }
+
+ inline float * GetDecodedAlphas(void)
+ {
+ return m_pencoding->GetDecodedAlphas();
+ }
+
+ inline Block4x4Encoding::Mode GetEncodingMode(void)
+ {
+ return m_pencoding->GetMode();
+ }
+
+ inline bool GetFlip(void)
+ {
+ return m_pencoding->GetFlip();
+ }
+
+ inline bool IsDifferential(void)
+ {
+ return m_pencoding->IsDifferential();
+ }
+
+ inline ColorFloatRGBA * GetSource()
+ {
+ return m_afrgbaSource;
+ }
+
+ inline ErrorMetric GetErrorMetric()
+ {
+ return m_errormetric;
+ }
+
+ const char * GetEncodingModeName(void);
+
+ inline Block4x4Encoding * GetEncoding(void)
+ {
+ return m_pencoding;
+ }
+
+ inline SourceAlphaMix GetSourceAlphaMix(void)
+ {
+ return m_sourcealphamix;
+ }
+
+ inline Image * GetImageSource(void)
+ {
+ return m_pimageSource;
+ }
+
+ inline bool HasBorderPixels(void)
+ {
+ return m_boolBorderPixels;
+ }
+
+ inline bool HasPunchThroughPixels(void)
+ {
+ return m_boolPunchThroughPixels;
+ }
+
+ private:
+
+ void SetSourcePixels(void);
+
+ Image *m_pimageSource;
+ unsigned int m_uiSourceH;
+ unsigned int m_uiSourceV;
+ ErrorMetric m_errormetric;
+ ColorFloatRGBA m_afrgbaSource[PIXELS]; // vertical scan
+
+ SourceAlphaMix m_sourcealphamix;
+ bool m_boolBorderPixels; // marked as rgba(NAN, NAN, NAN, NAN)
+ bool m_boolPunchThroughPixels; // RGB8A1 or SRGB8A1 with any pixels with alpha < 0.5
+
+ Block4x4Encoding *m_pencoding;
+
+ };
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding.cpp b/thirdparty/etc2comp/EtcBlock4x4Encoding.cpp
new file mode 100644
index 0000000000..7a9e68c4cf
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding.cpp
@@ -0,0 +1,261 @@
+/*
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding.cpp
+
+Block4x4Encoding is the abstract base class for the different encoders. Each encoder targets a
+particular file format (e.g. ETC1, RGB8, RGBA8, R11)
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+namespace Etc
+{
+ // ----------------------------------------------------------------------------------------------------
+ //
+ const float Block4x4Encoding::LUMA_WEIGHT = 3.0f;
+ const float Block4x4Encoding::CHROMA_BLUE_WEIGHT = 0.5f;
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ Block4x4Encoding::Block4x4Encoding(void)
+ {
+
+ m_pblockParent = nullptr;
+
+ m_pafrgbaSource = nullptr;
+
+ m_boolBorderPixels = false;
+
+ m_fError = -1.0f;
+
+ m_mode = MODE_UNKNOWN;
+
+ m_uiEncodingIterations = 0;
+ m_boolDone = false;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(-1.0f, -1.0f, -1.0f, -1.0f);
+ m_afDecodedAlphas[uiPixel] = -1.0f;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialize the generic encoding for a 4x4 block
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // init the decoded pixels to -1 to mark them as undefined
+ // init the error to -1 to mark it as undefined
+ //
+ void Block4x4Encoding::Init(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric)
+ {
+
+ m_pblockParent = a_pblockParent;
+
+ m_pafrgbaSource = a_pafrgbaSource;
+
+ m_boolBorderPixels = m_pblockParent->HasBorderPixels();
+
+ m_fError = -1.0f;
+
+ m_uiEncodingIterations = 0;
+
+ m_errormetric = a_errormetric;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(-1.0f, -1.0f, -1.0f, -1.0f);
+ m_afDecodedAlphas[uiPixel] = -1.0f;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // calculate the error for the block by summing the pixel errors
+ //
+ void Block4x4Encoding::CalcBlockError(void)
+ {
+ m_fError = 0.0f;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_fError += CalcPixelError(m_afrgbaDecodedColors[uiPixel], m_afDecodedAlphas[uiPixel],
+ m_pafrgbaSource[uiPixel]);
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // calculate the error between the source pixel and the decoded pixel
+ // the error amount is base on the error metric
+ //
+ float Block4x4Encoding::CalcPixelError(ColorFloatRGBA a_frgbaDecodedColor, float a_fDecodedAlpha,
+ ColorFloatRGBA a_frgbaSourcePixel)
+ {
+
+ // if a border pixel
+ if (isnan(a_frgbaSourcePixel.fA))
+ {
+ return 0.0f;
+ }
+
+ if (m_errormetric == ErrorMetric::RGBA)
+ {
+ assert(a_fDecodedAlpha >= 0.0f);
+
+ float fDRed = (a_fDecodedAlpha * a_frgbaDecodedColor.fR) -
+ (a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fR);
+ float fDGreen = (a_fDecodedAlpha * a_frgbaDecodedColor.fG) -
+ (a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fG);
+ float fDBlue = (a_fDecodedAlpha * a_frgbaDecodedColor.fB) -
+ (a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fB);
+
+ float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA;
+
+ return fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue + fDAlpha*fDAlpha;
+ }
+ else if (m_errormetric == ErrorMetric::RGBX)
+ {
+ assert(a_fDecodedAlpha >= 0.0f);
+
+ float fDRed = a_frgbaDecodedColor.fR - a_frgbaSourcePixel.fR;
+ float fDGreen = a_frgbaDecodedColor.fG - a_frgbaSourcePixel.fG;
+ float fDBlue = a_frgbaDecodedColor.fB - a_frgbaSourcePixel.fB;
+ float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA;
+
+ return fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue + fDAlpha*fDAlpha;
+ }
+ else if (m_errormetric == ErrorMetric::REC709)
+ {
+ assert(a_fDecodedAlpha >= 0.0f);
+
+ float fLuma1 = a_frgbaSourcePixel.fR*0.2126f + a_frgbaSourcePixel.fG*0.7152f + a_frgbaSourcePixel.fB*0.0722f;
+ float fChromaR1 = 0.5f * ((a_frgbaSourcePixel.fR - fLuma1) * (1.0f / (1.0f - 0.2126f)));
+ float fChromaB1 = 0.5f * ((a_frgbaSourcePixel.fB - fLuma1) * (1.0f / (1.0f - 0.0722f)));
+
+ float fLuma2 = a_frgbaDecodedColor.fR*0.2126f +
+ a_frgbaDecodedColor.fG*0.7152f +
+ a_frgbaDecodedColor.fB*0.0722f;
+ float fChromaR2 = 0.5f * ((a_frgbaDecodedColor.fR - fLuma2) * (1.0f / (1.0f - 0.2126f)));
+ float fChromaB2 = 0.5f * ((a_frgbaDecodedColor.fB - fLuma2) * (1.0f / (1.0f - 0.0722f)));
+
+ float fDeltaL = a_frgbaSourcePixel.fA * fLuma1 - a_fDecodedAlpha * fLuma2;
+ float fDeltaCr = a_frgbaSourcePixel.fA * fChromaR1 - a_fDecodedAlpha * fChromaR2;
+ float fDeltaCb = a_frgbaSourcePixel.fA * fChromaB1 - a_fDecodedAlpha * fChromaB2;
+
+ float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA;
+
+ // Favor Luma accuracy over Chroma, and Red over Blue
+ return LUMA_WEIGHT*fDeltaL*fDeltaL +
+ fDeltaCr*fDeltaCr +
+ CHROMA_BLUE_WEIGHT*fDeltaCb*fDeltaCb +
+ fDAlpha*fDAlpha;
+ #if 0
+ float fDRed = a_frgbaDecodedPixel.fR - a_frgbaSourcePixel.fR;
+ float fDGreen = a_frgbaDecodedPixel.fG - a_frgbaSourcePixel.fG;
+ float fDBlue = a_frgbaDecodedPixel.fB - a_frgbaSourcePixel.fB;
+ return 2.0f * 3.0f * fDeltaL * fDeltaL + fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue;
+#endif
+ }
+ else if (m_errormetric == ErrorMetric::NORMALXYZ)
+ {
+ float fDecodedX = 2.0f * a_frgbaDecodedColor.fR - 1.0f;
+ float fDecodedY = 2.0f * a_frgbaDecodedColor.fG - 1.0f;
+ float fDecodedZ = 2.0f * a_frgbaDecodedColor.fB - 1.0f;
+
+ float fDecodedLength = sqrtf(fDecodedX*fDecodedX + fDecodedY*fDecodedY + fDecodedZ*fDecodedZ);
+
+ if (fDecodedLength < 0.5f)
+ {
+ return 1.0f;
+ }
+ else if (fDecodedLength == 0.0f)
+ {
+ fDecodedX = 1.0f;
+ fDecodedY = 0.0f;
+ fDecodedZ = 0.0f;
+ }
+ else
+ {
+ fDecodedX /= fDecodedLength;
+ fDecodedY /= fDecodedLength;
+ fDecodedZ /= fDecodedLength;
+ }
+
+ float fSourceX = 2.0f * a_frgbaSourcePixel.fR - 1.0f;
+ float fSourceY = 2.0f * a_frgbaSourcePixel.fG - 1.0f;
+ float fSourceZ = 2.0f * a_frgbaSourcePixel.fB - 1.0f;
+
+ float fSourceLength = sqrtf(fSourceX*fSourceX + fSourceY*fSourceY + fSourceZ*fSourceZ);
+
+ if (fSourceLength == 0.0f)
+ {
+ fSourceX = 1.0f;
+ fSourceY = 0.0f;
+ fSourceZ = 0.0f;
+ }
+ else
+ {
+ fSourceX /= fSourceLength;
+ fSourceY /= fSourceLength;
+ fSourceZ /= fSourceLength;
+ }
+
+ float fDotProduct = fSourceX*fDecodedX + fSourceY*fDecodedY + fSourceZ*fDecodedZ;
+ float fNormalizedDotProduct = 1.0f - 0.5f * (fDotProduct + 1.0f);
+ float fDotProductError = fNormalizedDotProduct * fNormalizedDotProduct;
+
+ float fLength2 = fDecodedX*fDecodedX + fDecodedY*fDecodedY + fDecodedZ*fDecodedZ;
+ float fLength2Error = fabsf(1.0f - fLength2);
+
+ float fDeltaW = a_frgbaDecodedColor.fA - a_frgbaSourcePixel.fA;
+ float fErrorW = fDeltaW * fDeltaW;
+
+ return fDotProductError + fLength2Error + fErrorW;
+ }
+ else // ErrorMetric::NUMERIC
+ {
+ assert(a_fDecodedAlpha >= 0.0f);
+
+ float fDX = a_frgbaDecodedColor.fR - a_frgbaSourcePixel.fR;
+ float fDY = a_frgbaDecodedColor.fG - a_frgbaSourcePixel.fG;
+ float fDZ = a_frgbaDecodedColor.fB - a_frgbaSourcePixel.fB;
+ float fDW = a_frgbaDecodedColor.fA - a_frgbaSourcePixel.fA;
+
+ return fDX*fDX + fDY*fDY + fDZ*fDZ + fDW*fDW;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
+
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding.h b/thirdparty/etc2comp/EtcBlock4x4Encoding.h
new file mode 100644
index 0000000000..c14c3b8616
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding.h
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColorFloatRGBA.h"
+
+#include "EtcErrorMetric.h"
+
+#include <assert.h>
+#include <float.h>
+
+namespace Etc
+{
+ class Block4x4;
+
+ // abstract base class for specific encodings
+ class Block4x4Encoding
+ {
+ public:
+
+ static const unsigned int ROWS = 4;
+ static const unsigned int COLUMNS = 4;
+ static const unsigned int PIXELS = ROWS * COLUMNS;
+ static const float LUMA_WEIGHT;
+ static const float CHROMA_BLUE_WEIGHT;
+
+ typedef enum
+ {
+ MODE_UNKNOWN,
+ //
+ MODE_ETC1,
+ MODE_T,
+ MODE_H,
+ MODE_PLANAR,
+ MODE_R11,
+ MODE_RG11,
+ //
+ MODES
+ } Mode;
+
+ Block4x4Encoding(void);
+ //virtual ~Block4x4Encoding(void) =0;
+ virtual ~Block4x4Encoding(void) {}
+ virtual void InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+
+ unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric) = 0;
+
+ virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+
+ ErrorMetric a_errormetric) = 0;
+
+ // perform an iteration of the encoding
+ // the first iteration must generate a complete, valid (if poor) encoding
+ virtual void PerformIteration(float a_fEffort) = 0;
+
+ void CalcBlockError(void);
+
+ inline float GetError(void)
+ {
+ assert(m_fError >= 0.0f);
+
+ return m_fError;
+ }
+
+ inline ColorFloatRGBA * GetDecodedColors(void)
+ {
+ return m_afrgbaDecodedColors;
+ }
+
+ inline float * GetDecodedAlphas(void)
+ {
+ return m_afDecodedAlphas;
+ }
+
+ virtual void SetEncodingBits(void) = 0;
+
+ virtual bool GetFlip(void) = 0;
+
+ virtual bool IsDifferential(void) = 0;
+
+ virtual bool HasSeverelyBentDifferentialColors(void) const = 0;
+
+ inline Mode GetMode(void)
+ {
+ return m_mode;
+ }
+
+ inline bool IsDone(void)
+ {
+ return m_boolDone;
+ }
+
+ inline void SetDoneIfPerfect()
+ {
+ if (GetError() == 0.0f)
+ {
+ m_boolDone = true;
+ }
+ }
+
+ float CalcPixelError(ColorFloatRGBA a_frgbaDecodedColor, float a_fDecodedAlpha,
+ ColorFloatRGBA a_frgbaSourcePixel);
+
+ protected:
+
+ void Init(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+
+ ErrorMetric a_errormetric);
+
+ Block4x4 *m_pblockParent;
+ ColorFloatRGBA *m_pafrgbaSource;
+
+ bool m_boolBorderPixels; // if block has any border pixels
+
+ ColorFloatRGBA m_afrgbaDecodedColors[PIXELS]; // decoded RGB components, ignore Alpha
+ float m_afDecodedAlphas[PIXELS]; // decoded alpha component
+ float m_fError; // error for RGBA relative to m_pafrgbaSource
+
+ // intermediate encoding
+ Mode m_mode;
+
+ unsigned int m_uiEncodingIterations;
+ bool m_boolDone; // all iterations have been done
+ ErrorMetric m_errormetric;
+
+ private:
+
+ };
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcBlock4x4EncodingBits.h b/thirdparty/etc2comp/EtcBlock4x4EncodingBits.h
new file mode 100644
index 0000000000..4065700379
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4EncodingBits.h
@@ -0,0 +1,315 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <assert.h>
+
+namespace Etc
+{
+
+ // ################################################################################
+ // Block4x4EncodingBits
+ // Base class for Block4x4EncodingBits_XXXX
+ // ################################################################################
+
+ class Block4x4EncodingBits
+ {
+ public:
+
+ enum class Format
+ {
+ UNKNOWN,
+ //
+ RGB8,
+ RGBA8,
+ R11,
+ RG11,
+ RGB8A1,
+ //
+ FORMATS
+ };
+
+ static unsigned int GetBytesPerBlock(Format a_format)
+ {
+ switch (a_format)
+ {
+ case Format::RGB8:
+ case Format::R11:
+ case Format::RGB8A1:
+ return 8;
+ break;
+
+ case Format::RGBA8:
+ case Format::RG11:
+ return 16;
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+
+ }
+
+ };
+
+ // ################################################################################
+ // Block4x4EncodingBits_RGB8
+ // Encoding bits for the RGB portion of ETC1, RGB8, RGB8A1 and RGBA8
+ // ################################################################################
+
+ class Block4x4EncodingBits_RGB8
+ {
+ public:
+
+ static const unsigned int BYTES_PER_BLOCK = 8;
+
+ inline Block4x4EncodingBits_RGB8(void)
+ {
+ assert(sizeof(Block4x4EncodingBits_RGB8) == BYTES_PER_BLOCK);
+
+ for (unsigned int uiByte = 0; uiByte < BYTES_PER_BLOCK; uiByte++)
+ {
+ auc[uiByte] = 0;
+ }
+
+ }
+
+ typedef struct
+ {
+ unsigned red2 : 4;
+ unsigned red1 : 4;
+ //
+ unsigned green2 : 4;
+ unsigned green1 : 4;
+ //
+ unsigned blue2 : 4;
+ unsigned blue1 : 4;
+ //
+ unsigned flip : 1;
+ unsigned diff : 1;
+ unsigned cw2 : 3;
+ unsigned cw1 : 3;
+ //
+ unsigned int selectors;
+ } Individual;
+
+ typedef struct
+ {
+ signed dred2 : 3;
+ unsigned red1 : 5;
+ //
+ signed dgreen2 : 3;
+ unsigned green1 : 5;
+ //
+ signed dblue2 : 3;
+ unsigned blue1 : 5;
+ //
+ unsigned flip : 1;
+ unsigned diff : 1;
+ unsigned cw2 : 3;
+ unsigned cw1 : 3;
+ //
+ unsigned int selectors;
+ } Differential;
+
+ typedef struct
+ {
+ unsigned red1b : 2;
+ unsigned detect2 : 1;
+ unsigned red1a : 2;
+ unsigned detect1 : 3;
+ //
+ unsigned blue1 : 4;
+ unsigned green1 : 4;
+ //
+ unsigned green2 : 4;
+ unsigned red2 : 4;
+ //
+ unsigned db : 1;
+ unsigned diff : 1;
+ unsigned da : 2;
+ unsigned blue2 : 4;
+ //
+ unsigned int selectors;
+ } T;
+
+ typedef struct
+ {
+ unsigned green1a : 3;
+ unsigned red1 : 4;
+ unsigned detect1 : 1;
+ //
+ unsigned blue1b : 2;
+ unsigned detect3 : 1;
+ unsigned blue1a : 1;
+ unsigned green1b : 1;
+ unsigned detect2 : 3;
+ //
+ unsigned green2a : 3;
+ unsigned red2 : 4;
+ unsigned blue1c : 1;
+ //
+ unsigned db : 1;
+ unsigned diff : 1;
+ unsigned da : 1;
+ unsigned blue2 : 4;
+ unsigned green2b : 1;
+ //
+ unsigned int selectors;
+ } H;
+
+ typedef struct
+ {
+ unsigned originGreen1 : 1;
+ unsigned originRed : 6;
+ unsigned detect1 : 1;
+ //
+ unsigned originBlue1 : 1;
+ unsigned originGreen2 : 6;
+ unsigned detect2 : 1;
+ //
+ unsigned originBlue3 : 2;
+ unsigned detect4 : 1;
+ unsigned originBlue2 : 2;
+ unsigned detect3 : 3;
+ //
+ unsigned horizRed2 : 1;
+ unsigned diff : 1;
+ unsigned horizRed1 : 5;
+ unsigned originBlue4 : 1;
+ //
+ unsigned horizBlue1: 1;
+ unsigned horizGreen : 7;
+ //
+ unsigned vertRed1 : 3;
+ unsigned horizBlue2 : 5;
+ //
+ unsigned vertGreen1 : 5;
+ unsigned vertRed2 : 3;
+ //
+ unsigned vertBlue : 6;
+ unsigned vertGreen2 : 2;
+ } Planar;
+
+ union
+ {
+ unsigned char auc[BYTES_PER_BLOCK];
+ unsigned long int ul;
+ Individual individual;
+ Differential differential;
+ T t;
+ H h;
+ Planar planar;
+ };
+
+ };
+
+ // ################################################################################
+ // Block4x4EncodingBits_A8
+ // Encoding bits for the A portion of RGBA8
+ // ################################################################################
+
+ class Block4x4EncodingBits_A8
+ {
+ public:
+
+ static const unsigned int BYTES_PER_BLOCK = 8;
+ static const unsigned int SELECTOR_BYTES = 6;
+
+ typedef struct
+ {
+ unsigned base : 8;
+ unsigned table : 4;
+ unsigned multiplier : 4;
+ unsigned selectors0 : 8;
+ unsigned selectors1 : 8;
+ unsigned selectors2 : 8;
+ unsigned selectors3 : 8;
+ unsigned selectors4 : 8;
+ unsigned selectors5 : 8;
+ } Data;
+
+ Data data;
+
+ };
+
+ // ################################################################################
+ // Block4x4EncodingBits_R11
+ // Encoding bits for the R portion of R11
+ // ################################################################################
+
+ class Block4x4EncodingBits_R11
+ {
+ public:
+
+ static const unsigned int BYTES_PER_BLOCK = 8;
+ static const unsigned int SELECTOR_BYTES = 6;
+
+ typedef struct
+ {
+ unsigned base : 8;
+ unsigned table : 4;
+ unsigned multiplier : 4;
+ unsigned selectors0 : 8;
+ unsigned selectors1 : 8;
+ unsigned selectors2 : 8;
+ unsigned selectors3 : 8;
+ unsigned selectors4 : 8;
+ unsigned selectors5 : 8;
+ } Data;
+
+ Data data;
+
+ };
+
+ class Block4x4EncodingBits_RG11
+ {
+ public:
+
+ static const unsigned int BYTES_PER_BLOCK = 16;
+ static const unsigned int SELECTOR_BYTES = 12;
+
+ typedef struct
+ {
+ //Red portion
+ unsigned baseR : 8;
+ unsigned tableIndexR : 4;
+ unsigned multiplierR : 4;
+ unsigned selectorsR0 : 8;
+ unsigned selectorsR1 : 8;
+ unsigned selectorsR2 : 8;
+ unsigned selectorsR3 : 8;
+ unsigned selectorsR4 : 8;
+ unsigned selectorsR5 : 8;
+ //Green portion
+ unsigned baseG : 8;
+ unsigned tableIndexG : 4;
+ unsigned multiplierG : 4;
+ unsigned selectorsG0 : 8;
+ unsigned selectorsG1 : 8;
+ unsigned selectorsG2 : 8;
+ unsigned selectorsG3 : 8;
+ unsigned selectorsG4 : 8;
+ unsigned selectorsG5 : 8;
+ } Data;
+
+ Data data;
+
+ };
+
+}
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.cpp b/thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.cpp
new file mode 100644
index 0000000000..a27f74c0d5
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.cpp
@@ -0,0 +1,1281 @@
+/*
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_ETC1.cpp
+
+Block4x4Encoding_ETC1 is the encoder to use when targetting file format ETC1. This encoder is also
+used for the ETC1 subset of file format RGB8, RGBA8 and RGB8A1
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_ETC1.h"
+
+#include "EtcBlock4x4.h"
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcDifferentialTrys.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+
+ // pixel processing order if the flip bit = 0 (horizontal split)
+ const unsigned int Block4x4Encoding_ETC1::s_auiPixelOrderFlip0[PIXELS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+
+ // pixel processing order if the flip bit = 1 (vertical split)
+ const unsigned int Block4x4Encoding_ETC1::s_auiPixelOrderFlip1[PIXELS] = { 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15 };
+
+ // pixel processing order for horizontal scan (ETC normally does a vertical scan)
+ const unsigned int Block4x4Encoding_ETC1::s_auiPixelOrderHScan[PIXELS] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
+
+ // pixel indices for different block halves
+ const unsigned int Block4x4Encoding_ETC1::s_auiLeftPixelMapping[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+ const unsigned int Block4x4Encoding_ETC1::s_auiRightPixelMapping[8] = { 8, 9, 10, 11, 12, 13, 14, 15 };
+ const unsigned int Block4x4Encoding_ETC1::s_auiTopPixelMapping[8] = { 0, 1, 4, 5, 8, 9, 12, 13 };
+ const unsigned int Block4x4Encoding_ETC1::s_auiBottomPixelMapping[8] = { 2, 3, 6, 7, 10, 11, 14, 15 };
+
+ // CW ranges that the ETC1 decoders use
+ // CW is basically a contrast for the different selector bits, since these values are offsets to the base color
+ // the first axis in the array is indexed by the CW in the encoding bits
+ // the second axis in the array is indexed by the selector bits
+ float Block4x4Encoding_ETC1::s_aafCwTable[CW_RANGES][SELECTORS] =
+ {
+ { 2.0f / 255.0f, 8.0f / 255.0f, -2.0f / 255.0f, -8.0f / 255.0f },
+ { 5.0f / 255.0f, 17.0f / 255.0f, -5.0f / 255.0f, -17.0f / 255.0f },
+ { 9.0f / 255.0f, 29.0f / 255.0f, -9.0f / 255.0f, -29.0f / 255.0f },
+ { 13.0f / 255.0f, 42.0f / 255.0f, -13.0f / 255.0f, -42.0f / 255.0f },
+ { 18.0f / 255.0f, 60.0f / 255.0f, -18.0f / 255.0f, -60.0f / 255.0f },
+ { 24.0f / 255.0f, 80.0f / 255.0f, -24.0f / 255.0f, -80.0f / 255.0f },
+ { 33.0f / 255.0f, 106.0f / 255.0f, -33.0f / 255.0f, -106.0f / 255.0f },
+ { 47.0f / 255.0f, 183.0f / 255.0f, -47.0f / 255.0f, -183.0f / 255.0f }
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ Block4x4Encoding_ETC1::Block4x4Encoding_ETC1(void)
+ {
+ m_mode = MODE_ETC1;
+ m_boolDiff = false;
+ m_boolFlip = false;
+ m_frgbaColor1 = ColorFloatRGBA();
+ m_frgbaColor2 = ColorFloatRGBA();
+ m_uiCW1 = 0;
+ m_uiCW2 = 0;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = 0;
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+
+ m_boolMostLikelyFlip = false;
+
+ m_fError = -1.0f;
+
+ m_fError1 = -1.0f;
+ m_fError2 = -1.0f;
+ m_boolSeverelyBentDifferentialColors = false;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+
+ }
+
+ Block4x4Encoding_ETC1::~Block4x4Encoding_ETC1(void) {}
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization prior to encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits
+ //
+ void Block4x4Encoding_ETC1::InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+ unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric)
+ {
+
+ Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+
+ m_fError = -1.0f;
+
+ m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)(a_paucEncodingBits);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits of a previous encoding
+ //
+ void Block4x4Encoding_ETC1::InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric)
+ {
+
+ Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+ m_fError = -1.0f;
+
+ m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits;
+
+ m_mode = MODE_ETC1;
+ m_boolDiff = m_pencodingbitsRGB8->individual.diff;
+ m_boolFlip = m_pencodingbitsRGB8->individual.flip;
+ if (m_boolDiff)
+ {
+ int iR2 = (int)(m_pencodingbitsRGB8->differential.red1 + m_pencodingbitsRGB8->differential.dred2);
+ if (iR2 < 0)
+ {
+ iR2 = 0;
+ }
+ else if (iR2 > 31)
+ {
+ iR2 = 31;
+ }
+
+ int iG2 = (int)(m_pencodingbitsRGB8->differential.green1 + m_pencodingbitsRGB8->differential.dgreen2);
+ if (iG2 < 0)
+ {
+ iG2 = 0;
+ }
+ else if (iG2 > 31)
+ {
+ iG2 = 31;
+ }
+
+ int iB2 = (int)(m_pencodingbitsRGB8->differential.blue1 + m_pencodingbitsRGB8->differential.dblue2);
+ if (iB2 < 0)
+ {
+ iB2 = 0;
+ }
+ else if (iB2 > 31)
+ {
+ iB2 = 31;
+ }
+
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5(m_pencodingbitsRGB8->differential.red1, m_pencodingbitsRGB8->differential.green1, m_pencodingbitsRGB8->differential.blue1);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iR2, (unsigned char)iG2, (unsigned char)iB2);
+
+ }
+ else
+ {
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(m_pencodingbitsRGB8->individual.red1, m_pencodingbitsRGB8->individual.green1, m_pencodingbitsRGB8->individual.blue1);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(m_pencodingbitsRGB8->individual.red2, m_pencodingbitsRGB8->individual.green2, m_pencodingbitsRGB8->individual.blue2);
+ }
+
+ m_uiCW1 = m_pencodingbitsRGB8->individual.cw1;
+ m_uiCW2 = m_pencodingbitsRGB8->individual.cw2;
+
+ InitFromEncodingBits_Selectors();
+
+ Decode();
+
+ CalcBlockError();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // init the selectors from a prior encoding
+ //
+ void Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(void)
+ {
+
+ unsigned char *paucSelectors = (unsigned char *)&m_pencodingbitsRGB8->individual.selectors;
+
+ for (unsigned int iPixel = 0; iPixel < PIXELS; iPixel++)
+ {
+ unsigned int uiByteMSB = (unsigned int)(1 - (iPixel / 8));
+ unsigned int uiByteLSB = (unsigned int)(3 - (iPixel / 8));
+ unsigned int uiShift = (unsigned int)(iPixel & 7);
+
+ unsigned int uiSelectorMSB = (unsigned int)((paucSelectors[uiByteMSB] >> uiShift) & 1);
+ unsigned int uiSelectorLSB = (unsigned int)((paucSelectors[uiByteLSB] >> uiShift) & 1);
+
+ m_auiSelectors[iPixel] = (uiSelectorMSB << 1) + uiSelectorLSB;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ void Block4x4Encoding_ETC1::PerformIteration(float a_fEffort)
+ {
+ assert(!m_boolDone);
+
+ switch (m_uiEncodingIterations)
+ {
+ case 0:
+ PerformFirstIteration();
+ break;
+
+ case 1:
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, 0);
+ break;
+
+ case 2:
+ TryIndividual(m_boolMostLikelyFlip, 1);
+ if (a_fEffort <= 49.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 3:
+ TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0);
+ if (a_fEffort <= 59.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 4:
+ TryIndividual(!m_boolMostLikelyFlip, 1);
+ if (a_fEffort <= 69.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 5:
+ TryDegenerates1();
+ if (a_fEffort <= 79.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 6:
+ TryDegenerates2();
+ if (a_fEffort <= 89.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 7:
+ TryDegenerates3();
+ if (a_fEffort <= 99.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 8:
+ TryDegenerates4();
+ m_boolDone = true;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ m_uiEncodingIterations++;
+ SetDoneIfPerfect();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find best initial encoding to ensure block has a valid encoding
+ //
+ void Block4x4Encoding_ETC1::PerformFirstIteration(void)
+ {
+ CalculateMostLikelyFlip();
+
+ m_fError = FLT_MAX;
+
+ TryDifferential(m_boolMostLikelyFlip, 0, 0, 0);
+ SetDoneIfPerfect();
+ if (m_boolDone)
+ {
+ return;
+ }
+
+ TryIndividual(m_boolMostLikelyFlip, 0);
+ SetDoneIfPerfect();
+ if (m_boolDone)
+ {
+ return;
+ }
+ TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0);
+ SetDoneIfPerfect();
+ if (m_boolDone)
+ {
+ return;
+ }
+ TryIndividual(!m_boolMostLikelyFlip, 0);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // algorithm:
+ // create a source average color for the Left, Right, Top and Bottom halves using the 8 pixels in each half
+ // note: the "gray line" is the line of equal delta RGB that goes thru the average color
+ // for each half:
+ // see how close each of the 8 pixels are to the "gray line" that goes thru the source average color
+ // create an error value that is the sum of the distances from the gray line
+ // h_error is the sum of Left and Right errors
+ // v_error is the sum of Top and Bottom errors
+ //
+ void Block4x4Encoding_ETC1::CalculateMostLikelyFlip(void)
+ {
+ static const bool DEBUG_PRINT = false;
+
+ CalculateSourceAverages();
+
+ float fLeftGrayErrorSum = 0.0f;
+ float fRightGrayErrorSum = 0.0f;
+ float fTopGrayErrorSum = 0.0f;
+ float fBottomGrayErrorSum = 0.0f;
+
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ ColorFloatRGBA *pfrgbaLeft = &m_pafrgbaSource[uiPixel];
+ ColorFloatRGBA *pfrgbaRight = &m_pafrgbaSource[uiPixel + 8];
+ ColorFloatRGBA *pfrgbaTop = &m_pafrgbaSource[s_auiTopPixelMapping[uiPixel]];
+ ColorFloatRGBA *pfrgbaBottom = &m_pafrgbaSource[s_auiBottomPixelMapping[uiPixel]];
+
+ float fLeftGrayError = CalcGrayDistance2(*pfrgbaLeft, m_frgbaSourceAverageLeft);
+ float fRightGrayError = CalcGrayDistance2(*pfrgbaRight, m_frgbaSourceAverageRight);
+ float fTopGrayError = CalcGrayDistance2(*pfrgbaTop, m_frgbaSourceAverageTop);
+ float fBottomGrayError = CalcGrayDistance2(*pfrgbaBottom, m_frgbaSourceAverageBottom);
+
+ fLeftGrayErrorSum += fLeftGrayError;
+ fRightGrayErrorSum += fRightGrayError;
+ fTopGrayErrorSum += fTopGrayError;
+ fBottomGrayErrorSum += fBottomGrayError;
+ }
+
+ if (DEBUG_PRINT)
+ {
+ printf("\n%.2f %.2f\n", fLeftGrayErrorSum + fRightGrayErrorSum, fTopGrayErrorSum + fBottomGrayErrorSum);
+ }
+
+ m_boolMostLikelyFlip = (fTopGrayErrorSum + fBottomGrayErrorSum) < (fLeftGrayErrorSum + fRightGrayErrorSum);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // calculate source pixel averages for each 2x2 quadrant in a 4x4 block
+ // these are used to determine the averages for each of the 4 different halves (left, right, top, bottom)
+ // ignore pixels that have alpha == NAN (these are border pixels outside of the source image)
+ // weight the averages based on a pixel's alpha
+ //
+ void Block4x4Encoding_ETC1::CalculateSourceAverages(void)
+ {
+ static const bool DEBUG_PRINT = false;
+
+ bool boolRGBX = m_pblockParent->GetImageSource()->GetErrorMetric() == ErrorMetric::RGBX;
+
+ if (m_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::OPAQUE || boolRGBX)
+ {
+ ColorFloatRGBA frgbaSumUL = m_pafrgbaSource[0] + m_pafrgbaSource[1] + m_pafrgbaSource[4] + m_pafrgbaSource[5];
+ ColorFloatRGBA frgbaSumLL = m_pafrgbaSource[2] + m_pafrgbaSource[3] + m_pafrgbaSource[6] + m_pafrgbaSource[7];
+ ColorFloatRGBA frgbaSumUR = m_pafrgbaSource[8] + m_pafrgbaSource[9] + m_pafrgbaSource[12] + m_pafrgbaSource[13];
+ ColorFloatRGBA frgbaSumLR = m_pafrgbaSource[10] + m_pafrgbaSource[11] + m_pafrgbaSource[14] + m_pafrgbaSource[15];
+
+ m_frgbaSourceAverageLeft = (frgbaSumUL + frgbaSumLL) * 0.125f;
+ m_frgbaSourceAverageRight = (frgbaSumUR + frgbaSumLR) * 0.125f;
+ m_frgbaSourceAverageTop = (frgbaSumUL + frgbaSumUR) * 0.125f;
+ m_frgbaSourceAverageBottom = (frgbaSumLL + frgbaSumLR) * 0.125f;
+ }
+ else
+ {
+ float afSourceAlpha[PIXELS];
+
+ // treat alpha NAN as 0.0f
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ afSourceAlpha[uiPixel] = isnan(m_pafrgbaSource[uiPixel].fA) ?
+ 0.0f :
+ m_pafrgbaSource[uiPixel].fA;
+ }
+
+ ColorFloatRGBA afrgbaAlphaWeightedSource[PIXELS];
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ afrgbaAlphaWeightedSource[uiPixel] = m_pafrgbaSource[uiPixel] * afSourceAlpha[uiPixel];
+ }
+
+ ColorFloatRGBA frgbaSumUL = afrgbaAlphaWeightedSource[0] +
+ afrgbaAlphaWeightedSource[1] +
+ afrgbaAlphaWeightedSource[4] +
+ afrgbaAlphaWeightedSource[5];
+
+ ColorFloatRGBA frgbaSumLL = afrgbaAlphaWeightedSource[2] +
+ afrgbaAlphaWeightedSource[3] +
+ afrgbaAlphaWeightedSource[6] +
+ afrgbaAlphaWeightedSource[7];
+
+ ColorFloatRGBA frgbaSumUR = afrgbaAlphaWeightedSource[8] +
+ afrgbaAlphaWeightedSource[9] +
+ afrgbaAlphaWeightedSource[12] +
+ afrgbaAlphaWeightedSource[13];
+
+ ColorFloatRGBA frgbaSumLR = afrgbaAlphaWeightedSource[10] +
+ afrgbaAlphaWeightedSource[11] +
+ afrgbaAlphaWeightedSource[14] +
+ afrgbaAlphaWeightedSource[15];
+
+ float fWeightSumUL = afSourceAlpha[0] +
+ afSourceAlpha[1] +
+ afSourceAlpha[4] +
+ afSourceAlpha[5];
+
+ float fWeightSumLL = afSourceAlpha[2] +
+ afSourceAlpha[3] +
+ afSourceAlpha[6] +
+ afSourceAlpha[7];
+
+ float fWeightSumUR = afSourceAlpha[8] +
+ afSourceAlpha[9] +
+ afSourceAlpha[12] +
+ afSourceAlpha[13];
+
+ float fWeightSumLR = afSourceAlpha[10] +
+ afSourceAlpha[11] +
+ afSourceAlpha[14] +
+ afSourceAlpha[15];
+
+ ColorFloatRGBA frgbaSumLeft = frgbaSumUL + frgbaSumLL;
+ ColorFloatRGBA frgbaSumRight = frgbaSumUR + frgbaSumLR;
+ ColorFloatRGBA frgbaSumTop = frgbaSumUL + frgbaSumUR;
+ ColorFloatRGBA frgbaSumBottom = frgbaSumLL + frgbaSumLR;
+
+ float fWeightSumLeft = fWeightSumUL + fWeightSumLL;
+ float fWeightSumRight = fWeightSumUR + fWeightSumLR;
+ float fWeightSumTop = fWeightSumUL + fWeightSumUR;
+ float fWeightSumBottom = fWeightSumLL + fWeightSumLR;
+
+ // check to see if there is at least 1 pixel with non-zero alpha
+ // completely transparent block should not make it to this code
+ assert((fWeightSumLeft + fWeightSumRight) > 0.0f);
+ assert((fWeightSumTop + fWeightSumBottom) > 0.0f);
+
+ if (fWeightSumLeft > 0.0f)
+ {
+ m_frgbaSourceAverageLeft = frgbaSumLeft * (1.0f/fWeightSumLeft);
+ }
+ if (fWeightSumRight > 0.0f)
+ {
+ m_frgbaSourceAverageRight = frgbaSumRight * (1.0f/fWeightSumRight);
+ }
+ if (fWeightSumTop > 0.0f)
+ {
+ m_frgbaSourceAverageTop = frgbaSumTop * (1.0f/fWeightSumTop);
+ }
+ if (fWeightSumBottom > 0.0f)
+ {
+ m_frgbaSourceAverageBottom = frgbaSumBottom * (1.0f/fWeightSumBottom);
+ }
+
+ if (fWeightSumLeft == 0.0f)
+ {
+ assert(fWeightSumRight > 0.0f);
+ m_frgbaSourceAverageLeft = m_frgbaSourceAverageRight;
+ }
+ if (fWeightSumRight == 0.0f)
+ {
+ assert(fWeightSumLeft > 0.0f);
+ m_frgbaSourceAverageRight = m_frgbaSourceAverageLeft;
+ }
+ if (fWeightSumTop == 0.0f)
+ {
+ assert(fWeightSumBottom > 0.0f);
+ m_frgbaSourceAverageTop = m_frgbaSourceAverageBottom;
+ }
+ if (fWeightSumBottom == 0.0f)
+ {
+ assert(fWeightSumTop > 0.0f);
+ m_frgbaSourceAverageBottom = m_frgbaSourceAverageTop;
+ }
+ }
+
+
+
+ if (DEBUG_PRINT)
+ {
+ printf("\ntarget: [%.2f,%.2f,%.2f] [%.2f,%.2f,%.2f] [%.2f,%.2f,%.2f] [%.2f,%.2f,%.2f]\n",
+ m_frgbaSourceAverageLeft.fR, m_frgbaSourceAverageLeft.fG, m_frgbaSourceAverageLeft.fB,
+ m_frgbaSourceAverageRight.fR, m_frgbaSourceAverageRight.fG, m_frgbaSourceAverageRight.fB,
+ m_frgbaSourceAverageTop.fR, m_frgbaSourceAverageTop.fG, m_frgbaSourceAverageTop.fB,
+ m_frgbaSourceAverageBottom.fR, m_frgbaSourceAverageBottom.fG, m_frgbaSourceAverageBottom.fB);
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try an ETC1 differential mode encoding
+ // use a_boolFlip to set the encoding F bit
+ // use a_uiRadius to alter basecolor components in the range[-a_uiRadius:a_uiRadius]
+ // use a_iGrayOffset1 and a_iGrayOffset2 to offset the basecolor to search for degenerate encodings
+ // replace the encoding if the encoding error is less than previous encoding
+ //
+ void Block4x4Encoding_ETC1::TryDifferential(bool a_boolFlip, unsigned int a_uiRadius,
+ int a_iGrayOffset1, int a_iGrayOffset2)
+ {
+
+ ColorFloatRGBA frgbaColor1;
+ ColorFloatRGBA frgbaColor2;
+
+ const unsigned int *pauiPixelMapping1;
+ const unsigned int *pauiPixelMapping2;
+
+ if (a_boolFlip)
+ {
+ frgbaColor1 = m_frgbaSourceAverageTop;
+ frgbaColor2 = m_frgbaSourceAverageBottom;
+
+ pauiPixelMapping1 = s_auiTopPixelMapping;
+ pauiPixelMapping2 = s_auiBottomPixelMapping;
+ }
+ else
+ {
+ frgbaColor1 = m_frgbaSourceAverageLeft;
+ frgbaColor2 = m_frgbaSourceAverageRight;
+
+ pauiPixelMapping1 = s_auiLeftPixelMapping;
+ pauiPixelMapping2 = s_auiRightPixelMapping;
+ }
+
+ DifferentialTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2,
+ a_uiRadius, a_iGrayOffset1, a_iGrayOffset2);
+
+ Block4x4Encoding_ETC1 encodingTry = *this;
+ encodingTry.m_boolFlip = a_boolFlip;
+
+ encodingTry.TryDifferentialHalf(&trys.m_half1);
+ encodingTry.TryDifferentialHalf(&trys.m_half2);
+
+ // find best halves that are within differential range
+ DifferentialTrys::Try *ptryBest1 = nullptr;
+ DifferentialTrys::Try *ptryBest2 = nullptr;
+ encodingTry.m_fError = FLT_MAX;
+
+ // see if the best of each half are in differential range
+ int iDRed = trys.m_half2.m_ptryBest->m_iRed - trys.m_half1.m_ptryBest->m_iRed;
+ int iDGreen = trys.m_half2.m_ptryBest->m_iGreen - trys.m_half1.m_ptryBest->m_iGreen;
+ int iDBlue = trys.m_half2.m_ptryBest->m_iBlue - trys.m_half1.m_ptryBest->m_iBlue;
+ if (iDRed >= -4 && iDRed <= 3 && iDGreen >= -4 && iDGreen <= 3 && iDBlue >= -4 && iDBlue <= 3)
+ {
+ ptryBest1 = trys.m_half1.m_ptryBest;
+ ptryBest2 = trys.m_half2.m_ptryBest;
+ encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError;
+ }
+ else
+ {
+ // else, find the next best halves that are in differential range
+ for (DifferentialTrys::Try *ptry1 = &trys.m_half1.m_atry[0];
+ ptry1 < &trys.m_half1.m_atry[trys.m_half1.m_uiTrys];
+ ptry1++)
+ {
+ for (DifferentialTrys::Try *ptry2 = &trys.m_half2.m_atry[0];
+ ptry2 < &trys.m_half2.m_atry[trys.m_half2.m_uiTrys];
+ ptry2++)
+ {
+ iDRed = ptry2->m_iRed - ptry1->m_iRed;
+ bool boolValidRedDelta = iDRed <= 3 && iDRed >= -4;
+ iDGreen = ptry2->m_iGreen - ptry1->m_iGreen;
+ bool boolValidGreenDelta = iDGreen <= 3 && iDGreen >= -4;
+ iDBlue = ptry2->m_iBlue - ptry1->m_iBlue;
+ bool boolValidBlueDelta = iDBlue <= 3 && iDBlue >= -4;
+
+ if (boolValidRedDelta && boolValidGreenDelta && boolValidBlueDelta)
+ {
+ float fError = ptry1->m_fError + ptry2->m_fError;
+
+ if (fError < encodingTry.m_fError)
+ {
+ encodingTry.m_fError = fError;
+
+ ptryBest1 = ptry1;
+ ptryBest2 = ptry2;
+ }
+ }
+
+ }
+ }
+ assert(encodingTry.m_fError < FLT_MAX);
+ assert(ptryBest1 != nullptr);
+ assert(ptryBest2 != nullptr);
+ }
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = MODE_ETC1;
+ m_boolDiff = true;
+ m_boolFlip = encodingTry.m_boolFlip;
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue);
+ m_uiCW1 = ptryBest1->m_uiCW;
+ m_uiCW2 = ptryBest2->m_uiCW;
+
+ for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++)
+ {
+ unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder];
+ unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder];
+
+ unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder];
+ unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+ m_auiSelectors[uiPixel1] = uiSelector1;
+ m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+ float fDeltaRGB1 = s_aafCwTable[m_uiCW1][uiSelector1];
+ float fDeltaRGB2 = s_aafCwTable[m_uiCW2][uiSelector2];
+
+ m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB();
+ m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB();
+ }
+
+ m_fError1 = ptryBest1->m_fError;
+ m_fError2 = ptryBest2->m_fError;
+ m_boolSeverelyBentDifferentialColors = trys.m_boolSeverelyBentColors;
+ m_fError = m_fError1 + m_fError2;
+
+ // sanity check
+ {
+ int iRed1 = m_frgbaColor1.IntRed(31.0f);
+ int iGreen1 = m_frgbaColor1.IntGreen(31.0f);
+ int iBlue1 = m_frgbaColor1.IntBlue(31.0f);
+
+ int iRed2 = m_frgbaColor2.IntRed(31.0f);
+ int iGreen2 = m_frgbaColor2.IntGreen(31.0f);
+ int iBlue2 = m_frgbaColor2.IntBlue(31.0f);
+
+ iDRed = iRed2 - iRed1;
+ iDGreen = iGreen2 - iGreen1;
+ iDBlue = iBlue2 - iBlue1;
+
+ assert(iDRed >= -4 && iDRed < 4);
+ assert(iDGreen >= -4 && iDGreen < 4);
+ assert(iDBlue >= -4 && iDBlue < 4);
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try an ETC1 differential mode encoding for a half of a 4x4 block
+ // vary the basecolor components using a radius
+ //
+ void Block4x4Encoding_ETC1::TryDifferentialHalf(DifferentialTrys::Half *a_phalf)
+ {
+
+ a_phalf->m_ptryBest = nullptr;
+ float fBestTryError = FLT_MAX;
+
+ a_phalf->m_uiTrys = 0;
+ for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius;
+ iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius;
+ iRed++)
+ {
+ assert(iRed >= 0 && iRed <= 31);
+
+ for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius;
+ iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius;
+ iGreen++)
+ {
+ assert(iGreen >= 0 && iGreen <= 31);
+
+ for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius;
+ iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius;
+ iBlue++)
+ {
+ assert(iBlue >= 0 && iBlue <= 31);
+
+ DifferentialTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys];
+ assert(ptry < &a_phalf->m_atry[DifferentialTrys::Half::MAX_TRYS]);
+
+ ptry->m_iRed = iRed;
+ ptry->m_iGreen = iGreen;
+ ptry->m_iBlue = iBlue;
+ ptry->m_fError = FLT_MAX;
+ ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue);
+
+ // try each CW
+ for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++)
+ {
+ unsigned int auiPixelSelectors[PIXELS / 2];
+ ColorFloatRGBA afrgbaDecodedPixels[PIXELS / 2];
+ float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+ // pre-compute decoded pixels for each selector
+ ColorFloatRGBA afrgbaSelectors[SELECTORS];
+ assert(SELECTORS == 4);
+ afrgbaSelectors[0] = (frgbaColor + s_aafCwTable[uiCW][0]).ClampRGB();
+ afrgbaSelectors[1] = (frgbaColor + s_aafCwTable[uiCW][1]).ClampRGB();
+ afrgbaSelectors[2] = (frgbaColor + s_aafCwTable[uiCW][2]).ClampRGB();
+ afrgbaSelectors[3] = (frgbaColor + s_aafCwTable[uiCW][3]).ClampRGB();
+
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]];
+ ColorFloatRGBA frgbaDecodedPixel;
+
+ for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+ {
+ frgbaDecodedPixel = afrgbaSelectors[uiSelector];
+
+ float fPixelError;
+
+ fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]],
+ *pfrgbaSourcePixel);
+
+ if (fPixelError < afPixelErrors[uiPixel])
+ {
+ auiPixelSelectors[uiPixel] = uiSelector;
+ afrgbaDecodedPixels[uiPixel] = frgbaDecodedPixel;
+ afPixelErrors[uiPixel] = fPixelError;
+ }
+
+ }
+ }
+
+ // add up all pixel errors
+ float fCWError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ fCWError += afPixelErrors[uiPixel];
+ }
+
+ // if best CW so far
+ if (fCWError < ptry->m_fError)
+ {
+ ptry->m_uiCW = uiCW;
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel];
+ }
+ ptry->m_fError = fCWError;
+ }
+
+ }
+
+ if (ptry->m_fError < fBestTryError)
+ {
+ a_phalf->m_ptryBest = ptry;
+ fBestTryError = ptry->m_fError;
+ }
+
+ assert(ptry->m_fError < FLT_MAX);
+
+ a_phalf->m_uiTrys++;
+ }
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try an ETC1 individual mode encoding
+ // use a_boolFlip to set the encoding F bit
+ // use a_uiRadius to alter basecolor components in the range[-a_uiRadius:a_uiRadius]
+ // replace the encoding if the encoding error is less than previous encoding
+ //
+ void Block4x4Encoding_ETC1::TryIndividual(bool a_boolFlip, unsigned int a_uiRadius)
+ {
+
+ ColorFloatRGBA frgbaColor1;
+ ColorFloatRGBA frgbaColor2;
+
+ const unsigned int *pauiPixelMapping1;
+ const unsigned int *pauiPixelMapping2;
+
+ if (a_boolFlip)
+ {
+ frgbaColor1 = m_frgbaSourceAverageTop;
+ frgbaColor2 = m_frgbaSourceAverageBottom;
+
+ pauiPixelMapping1 = s_auiTopPixelMapping;
+ pauiPixelMapping2 = s_auiBottomPixelMapping;
+ }
+ else
+ {
+ frgbaColor1 = m_frgbaSourceAverageLeft;
+ frgbaColor2 = m_frgbaSourceAverageRight;
+
+ pauiPixelMapping1 = s_auiLeftPixelMapping;
+ pauiPixelMapping2 = s_auiRightPixelMapping;
+ }
+
+ IndividualTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2, a_uiRadius);
+
+ Block4x4Encoding_ETC1 encodingTry = *this;
+ encodingTry.m_boolFlip = a_boolFlip;
+
+ encodingTry.TryIndividualHalf(&trys.m_half1);
+ encodingTry.TryIndividualHalf(&trys.m_half2);
+
+ // use the best of each half
+ IndividualTrys::Try *ptryBest1 = trys.m_half1.m_ptryBest;
+ IndividualTrys::Try *ptryBest2 = trys.m_half2.m_ptryBest;
+ encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError;
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = MODE_ETC1;
+ m_boolDiff = false;
+ m_boolFlip = encodingTry.m_boolFlip;
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue);
+ m_uiCW1 = ptryBest1->m_uiCW;
+ m_uiCW2 = ptryBest2->m_uiCW;
+
+ for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++)
+ {
+ unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder];
+ unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder];
+
+ unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder];
+ unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+ m_auiSelectors[uiPixel1] = uiSelector1;
+ m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+ float fDeltaRGB1 = s_aafCwTable[m_uiCW1][uiSelector1];
+ float fDeltaRGB2 = s_aafCwTable[m_uiCW2][uiSelector2];
+
+ m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB();
+ m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB();
+ }
+
+ m_fError1 = ptryBest1->m_fError;
+ m_fError2 = ptryBest2->m_fError;
+ m_fError = m_fError1 + m_fError2;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try an ETC1 differential mode encoding for a half of a 4x4 block
+ // vary the basecolor components using a radius
+ //
+ void Block4x4Encoding_ETC1::TryIndividualHalf(IndividualTrys::Half *a_phalf)
+ {
+
+ a_phalf->m_ptryBest = nullptr;
+ float fBestTryError = FLT_MAX;
+
+ a_phalf->m_uiTrys = 0;
+ for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius;
+ iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius;
+ iRed++)
+ {
+ assert(iRed >= 0 && iRed <= 15);
+
+ for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius;
+ iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius;
+ iGreen++)
+ {
+ assert(iGreen >= 0 && iGreen <= 15);
+
+ for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius;
+ iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius;
+ iBlue++)
+ {
+ assert(iBlue >= 0 && iBlue <= 15);
+
+ IndividualTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys];
+ assert(ptry < &a_phalf->m_atry[IndividualTrys::Half::MAX_TRYS]);
+
+ ptry->m_iRed = iRed;
+ ptry->m_iGreen = iGreen;
+ ptry->m_iBlue = iBlue;
+ ptry->m_fError = FLT_MAX;
+ ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue);
+
+ // try each CW
+ for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++)
+ {
+ unsigned int auiPixelSelectors[PIXELS / 2];
+ ColorFloatRGBA afrgbaDecodedPixels[PIXELS / 2];
+ float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+ // pre-compute decoded pixels for each selector
+ ColorFloatRGBA afrgbaSelectors[SELECTORS];
+ assert(SELECTORS == 4);
+ afrgbaSelectors[0] = (frgbaColor + s_aafCwTable[uiCW][0]).ClampRGB();
+ afrgbaSelectors[1] = (frgbaColor + s_aafCwTable[uiCW][1]).ClampRGB();
+ afrgbaSelectors[2] = (frgbaColor + s_aafCwTable[uiCW][2]).ClampRGB();
+ afrgbaSelectors[3] = (frgbaColor + s_aafCwTable[uiCW][3]).ClampRGB();
+
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]];
+ ColorFloatRGBA frgbaDecodedPixel;
+
+ for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+ {
+ frgbaDecodedPixel = afrgbaSelectors[uiSelector];
+
+ float fPixelError;
+
+ fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]],
+ *pfrgbaSourcePixel);
+
+ if (fPixelError < afPixelErrors[uiPixel])
+ {
+ auiPixelSelectors[uiPixel] = uiSelector;
+ afrgbaDecodedPixels[uiPixel] = frgbaDecodedPixel;
+ afPixelErrors[uiPixel] = fPixelError;
+ }
+
+ }
+ }
+
+ // add up all pixel errors
+ float fCWError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ fCWError += afPixelErrors[uiPixel];
+ }
+
+ // if best CW so far
+ if (fCWError < ptry->m_fError)
+ {
+ ptry->m_uiCW = uiCW;
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel];
+ }
+ ptry->m_fError = fCWError;
+ }
+
+ }
+
+ if (ptry->m_fError < fBestTryError)
+ {
+ a_phalf->m_ptryBest = ptry;
+ fBestTryError = ptry->m_fError;
+ }
+
+ assert(ptry->m_fError < FLT_MAX);
+
+ a_phalf->m_uiTrys++;
+ }
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try version 1 of the degenerate search
+ // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+ // each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+ // be successfull
+ //
+ void Block4x4Encoding_ETC1::TryDegenerates1(void)
+ {
+
+ TryDifferential(m_boolMostLikelyFlip, 1, -2, 0);
+ TryDifferential(m_boolMostLikelyFlip, 1, 2, 0);
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, 2);
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, -2);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try version 2 of the degenerate search
+ // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+ // each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+ // be successfull
+ //
+ void Block4x4Encoding_ETC1::TryDegenerates2(void)
+ {
+
+ TryDifferential(!m_boolMostLikelyFlip, 1, -2, 0);
+ TryDifferential(!m_boolMostLikelyFlip, 1, 2, 0);
+ TryDifferential(!m_boolMostLikelyFlip, 1, 0, 2);
+ TryDifferential(!m_boolMostLikelyFlip, 1, 0, -2);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try version 3 of the degenerate search
+ // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+ // each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+ // be successfull
+ //
+ void Block4x4Encoding_ETC1::TryDegenerates3(void)
+ {
+
+ TryDifferential(m_boolMostLikelyFlip, 1, -2, -2);
+ TryDifferential(m_boolMostLikelyFlip, 1, -2, 2);
+ TryDifferential(m_boolMostLikelyFlip, 1, 2, -2);
+ TryDifferential(m_boolMostLikelyFlip, 1, 2, 2);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try version 4 of the degenerate search
+ // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+ // each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+ // be successfull
+ //
+ void Block4x4Encoding_ETC1::TryDegenerates4(void)
+ {
+
+ TryDifferential(m_boolMostLikelyFlip, 1, -4, 0);
+ TryDifferential(m_boolMostLikelyFlip, 1, 4, 0);
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, 4);
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, -4);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find the best selector for each pixel based on a particular basecolor and CW that have been previously set
+ // calculate the selectors for each half of the block separately
+ // set the block error as the sum of each half's error
+ //
+ void Block4x4Encoding_ETC1::CalculateSelectors()
+ {
+ if (m_boolFlip)
+ {
+ CalculateHalfOfTheSelectors(0, s_auiTopPixelMapping);
+ CalculateHalfOfTheSelectors(1, s_auiBottomPixelMapping);
+ }
+ else
+ {
+ CalculateHalfOfTheSelectors(0, s_auiLeftPixelMapping);
+ CalculateHalfOfTheSelectors(1, s_auiRightPixelMapping);
+ }
+
+ m_fError = m_fError1 + m_fError2;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // choose best selectors for half of the block
+ // calculate the error for half of the block
+ //
+ void Block4x4Encoding_ETC1::CalculateHalfOfTheSelectors(unsigned int a_uiHalf,
+ const unsigned int *pauiPixelMapping)
+ {
+ static const bool DEBUG_PRINT = false;
+
+ ColorFloatRGBA *pfrgbaColor = a_uiHalf ? &m_frgbaColor2 : &m_frgbaColor1;
+ unsigned int *puiCW = a_uiHalf ? &m_uiCW2 : &m_uiCW1;
+
+ float *pfHalfError = a_uiHalf ? &m_fError2 : &m_fError1;
+ *pfHalfError = FLT_MAX;
+
+ // try each CW
+ for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++)
+ {
+ if (DEBUG_PRINT)
+ {
+ printf("\ncw=%u\n", uiCW);
+ }
+
+ unsigned int auiPixelSelectors[PIXELS / 2];
+ ColorFloatRGBA afrgbaDecodedPixels[PIXELS / 2];
+ float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ if (DEBUG_PRINT)
+ {
+ printf("\tsource [%.2f,%.2f,%.2f]\n", m_pafrgbaSource[pauiPixelMapping[uiPixel]].fR,
+ m_pafrgbaSource[pauiPixelMapping[uiPixel]].fG, m_pafrgbaSource[pauiPixelMapping[uiPixel]].fB);
+ }
+
+ ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[pauiPixelMapping[uiPixel]];
+ ColorFloatRGBA frgbaDecodedPixel;
+
+ for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+ {
+ float fDeltaRGB = s_aafCwTable[uiCW][uiSelector];
+
+ frgbaDecodedPixel = (*pfrgbaColor + fDeltaRGB).ClampRGB();
+
+ float fPixelError;
+
+ fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[pauiPixelMapping[uiPixel]],
+ *pfrgbaSourcePixel);
+
+ if (DEBUG_PRINT)
+ {
+ printf("\tpixel %u, index %u [%.2f,%.2f,%.2f], error %.2f", uiPixel, uiSelector,
+ frgbaDecodedPixel.fR,
+ frgbaDecodedPixel.fG,
+ frgbaDecodedPixel.fB,
+ fPixelError);
+ }
+
+ if (fPixelError < afPixelErrors[uiPixel])
+ {
+ if (DEBUG_PRINT)
+ {
+ printf(" *");
+ }
+
+ auiPixelSelectors[uiPixel] = uiSelector;
+ afrgbaDecodedPixels[uiPixel] = frgbaDecodedPixel;
+ afPixelErrors[uiPixel] = fPixelError;
+ }
+
+ if (DEBUG_PRINT)
+ {
+ printf("\n");
+ }
+ }
+ }
+
+ // add up all pixel errors
+ float fCWError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ fCWError += afPixelErrors[uiPixel];
+ }
+ if (DEBUG_PRINT)
+ {
+ printf("\terror %.2f\n", fCWError);
+ }
+
+ // if best CW so far
+ if (fCWError < *pfHalfError)
+ {
+ *pfHalfError = fCWError;
+ *puiCW = uiCW;
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ m_auiSelectors[pauiPixelMapping[uiPixel]] = auiPixelSelectors[uiPixel];
+ m_afrgbaDecodedColors[pauiPixelMapping[uiPixel]] = afrgbaDecodedPixels[uiPixel];
+ }
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state
+ //
+ void Block4x4Encoding_ETC1::SetEncodingBits(void)
+ {
+ assert(m_mode == MODE_ETC1);
+
+ if (m_boolDiff)
+ {
+ int iRed1 = m_frgbaColor1.IntRed(31.0f);
+ int iGreen1 = m_frgbaColor1.IntGreen(31.0f);
+ int iBlue1 = m_frgbaColor1.IntBlue(31.0f);
+
+ int iRed2 = m_frgbaColor2.IntRed(31.0f);
+ int iGreen2 = m_frgbaColor2.IntGreen(31.0f);
+ int iBlue2 = m_frgbaColor2.IntBlue(31.0f);
+
+ int iDRed2 = iRed2 - iRed1;
+ int iDGreen2 = iGreen2 - iGreen1;
+ int iDBlue2 = iBlue2 - iBlue1;
+
+ assert(iDRed2 >= -4 && iDRed2 < 4);
+ assert(iDGreen2 >= -4 && iDGreen2 < 4);
+ assert(iDBlue2 >= -4 && iDBlue2 < 4);
+
+ m_pencodingbitsRGB8->differential.red1 = (unsigned int)iRed1;
+ m_pencodingbitsRGB8->differential.green1 = (unsigned int)iGreen1;
+ m_pencodingbitsRGB8->differential.blue1 = (unsigned int)iBlue1;
+
+ m_pencodingbitsRGB8->differential.dred2 = iDRed2;
+ m_pencodingbitsRGB8->differential.dgreen2 = iDGreen2;
+ m_pencodingbitsRGB8->differential.dblue2 = iDBlue2;
+ }
+ else
+ {
+ m_pencodingbitsRGB8->individual.red1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+ m_pencodingbitsRGB8->individual.green1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+ m_pencodingbitsRGB8->individual.blue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+ m_pencodingbitsRGB8->individual.red2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+ m_pencodingbitsRGB8->individual.green2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+ m_pencodingbitsRGB8->individual.blue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+ }
+
+ m_pencodingbitsRGB8->individual.cw1 = m_uiCW1;
+ m_pencodingbitsRGB8->individual.cw2 = m_uiCW2;
+
+ SetEncodingBits_Selectors();
+
+ m_pencodingbitsRGB8->individual.diff = (unsigned int)m_boolDiff;
+ m_pencodingbitsRGB8->individual.flip = (unsigned int)m_boolFlip;
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the selectors in the encoding bits
+ //
+ void Block4x4Encoding_ETC1::SetEncodingBits_Selectors(void)
+ {
+
+ m_pencodingbitsRGB8->individual.selectors = 0;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ unsigned int uiSelector = m_auiSelectors[uiPixel];
+
+ // set index msb
+ m_pencodingbitsRGB8->individual.selectors |= (uiSelector >> 1) << (uiPixel ^ 8);
+
+ // set index lsb
+ m_pencodingbitsRGB8->individual.selectors |= (uiSelector & 1) << ((16 + uiPixel) ^ 8);
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the decoded colors and decoded alpha based on the encoding state
+ //
+ void Block4x4Encoding_ETC1::Decode(void)
+ {
+
+ const unsigned int *pauiPixelOrder = m_boolFlip ? s_auiPixelOrderFlip1 : s_auiPixelOrderFlip0;
+
+ for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS; uiPixelOrder++)
+ {
+ ColorFloatRGBA *pfrgbaCenter = uiPixelOrder < 8 ? &m_frgbaColor1 : &m_frgbaColor2;
+ unsigned int uiCW = uiPixelOrder < 8 ? m_uiCW1 : m_uiCW2;
+
+ unsigned int uiPixel = pauiPixelOrder[uiPixelOrder];
+
+ float fDelta = s_aafCwTable[uiCW][m_auiSelectors[uiPixel]];
+ m_afrgbaDecodedColors[uiPixel] = (*pfrgbaCenter + fDelta).ClampRGB();
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.h b/thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.h
new file mode 100644
index 0000000000..c0dc84d5d5
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.h
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding.h"
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcDifferentialTrys.h"
+#include "EtcIndividualTrys.h"
+
+namespace Etc
+{
+
+ // base class for Block4x4Encoding_RGB8
+ class Block4x4Encoding_ETC1 : public Block4x4Encoding
+ {
+ public:
+
+ Block4x4Encoding_ETC1(void);
+ virtual ~Block4x4Encoding_ETC1(void);
+
+ virtual void InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+
+ unsigned char *a_paucEncodingBits,
+ ErrorMetric a_errormetric);
+
+ virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+
+ ErrorMetric a_errormetric);
+
+ virtual void PerformIteration(float a_fEffort);
+
+ inline virtual bool GetFlip(void)
+ {
+ return m_boolFlip;
+ }
+
+ inline virtual bool IsDifferential(void)
+ {
+ return m_boolDiff;
+ }
+
+ virtual void SetEncodingBits(void);
+
+ void Decode(void);
+
+ inline ColorFloatRGBA GetColor1(void) const
+ {
+ return m_frgbaColor1;
+ }
+
+ inline ColorFloatRGBA GetColor2(void) const
+ {
+ return m_frgbaColor2;
+ }
+
+ inline const unsigned int * GetSelectors(void) const
+ {
+ return m_auiSelectors;
+ }
+
+ inline unsigned int GetCW1(void) const
+ {
+ return m_uiCW1;
+ }
+
+ inline unsigned int GetCW2(void) const
+ {
+ return m_uiCW2;
+ }
+
+ inline bool HasSeverelyBentDifferentialColors(void) const
+ {
+ return m_boolSeverelyBentDifferentialColors;
+ }
+
+ protected:
+
+ static const unsigned int s_auiPixelOrderFlip0[PIXELS];
+ static const unsigned int s_auiPixelOrderFlip1[PIXELS];
+ static const unsigned int s_auiPixelOrderHScan[PIXELS];
+
+ static const unsigned int s_auiLeftPixelMapping[8];
+ static const unsigned int s_auiRightPixelMapping[8];
+ static const unsigned int s_auiTopPixelMapping[8];
+ static const unsigned int s_auiBottomPixelMapping[8];
+
+ static const unsigned int SELECTOR_BITS = 2;
+ static const unsigned int SELECTORS = 1 << SELECTOR_BITS;
+
+ static const unsigned int CW_BITS = 3;
+ static const unsigned int CW_RANGES = 1 << CW_BITS;
+
+ static float s_aafCwTable[CW_RANGES][SELECTORS];
+ static unsigned char s_aucDifferentialCwRange[256];
+
+ static const int MAX_DIFFERENTIAL = 3;
+ static const int MIN_DIFFERENTIAL = -4;
+
+ void InitFromEncodingBits_Selectors(void);
+
+ void PerformFirstIteration(void);
+ void CalculateMostLikelyFlip(void);
+
+ void TryDifferential(bool a_boolFlip, unsigned int a_uiRadius,
+ int a_iGrayOffset1, int a_iGrayOffset2);
+ void TryDifferentialHalf(DifferentialTrys::Half *a_phalf);
+
+ void TryIndividual(bool a_boolFlip, unsigned int a_uiRadius);
+ void TryIndividualHalf(IndividualTrys::Half *a_phalf);
+
+ void TryDegenerates1(void);
+ void TryDegenerates2(void);
+ void TryDegenerates3(void);
+ void TryDegenerates4(void);
+
+ void CalculateSelectors();
+ void CalculateHalfOfTheSelectors(unsigned int a_uiHalf,
+ const unsigned int *pauiPixelMapping);
+
+ // calculate the distance2 of r_frgbaPixel from r_frgbaTarget's gray line
+ inline float CalcGrayDistance2(ColorFloatRGBA &r_frgbaPixel,
+ ColorFloatRGBA &r_frgbaTarget)
+ {
+ float fDeltaGray = ((r_frgbaPixel.fR - r_frgbaTarget.fR) +
+ (r_frgbaPixel.fG - r_frgbaTarget.fG) +
+ (r_frgbaPixel.fB - r_frgbaTarget.fB)) / 3.0f;
+
+ ColorFloatRGBA frgbaPointOnGrayLine = (r_frgbaTarget + fDeltaGray).ClampRGB();
+
+ float fDR = r_frgbaPixel.fR - frgbaPointOnGrayLine.fR;
+ float fDG = r_frgbaPixel.fG - frgbaPointOnGrayLine.fG;
+ float fDB = r_frgbaPixel.fB - frgbaPointOnGrayLine.fB;
+
+ return (fDR*fDR) + (fDG*fDG) + (fDB*fDB);
+ }
+
+ void SetEncodingBits_Selectors(void);
+
+ // intermediate encoding
+ bool m_boolDiff;
+ bool m_boolFlip;
+ ColorFloatRGBA m_frgbaColor1;
+ ColorFloatRGBA m_frgbaColor2;
+ unsigned int m_uiCW1;
+ unsigned int m_uiCW2;
+ unsigned int m_auiSelectors[PIXELS];
+
+ // state shared between iterations
+ ColorFloatRGBA m_frgbaSourceAverageLeft;
+ ColorFloatRGBA m_frgbaSourceAverageRight;
+ ColorFloatRGBA m_frgbaSourceAverageTop;
+ ColorFloatRGBA m_frgbaSourceAverageBottom;
+ bool m_boolMostLikelyFlip;
+
+ // stats
+ float m_fError1; // error for Etc1 half 1
+ float m_fError2; // error for Etc1 half 2
+ bool m_boolSeverelyBentDifferentialColors; // only valid if m_boolDiff;
+
+ // final encoding
+ Block4x4EncodingBits_RGB8 *m_pencodingbitsRGB8; // or RGB8 portion of Block4x4EncodingBits_RGB8A8
+
+ private:
+
+ void CalculateSourceAverages(void);
+
+ };
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_R11.cpp b/thirdparty/etc2comp/EtcBlock4x4Encoding_R11.cpp
new file mode 100644
index 0000000000..4c012fbbf1
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_R11.cpp
@@ -0,0 +1,429 @@
+/*
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_R11.cpp
+
+Block4x4Encoding_R11 is the encoder to use when targetting file format R11 and SR11 (signed R11).
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_R11.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+
+ // modifier values to use for R11, SR11, RG11 and SRG11
+ float Block4x4Encoding_R11::s_aafModifierTable[MODIFIER_TABLE_ENTRYS][SELECTORS]
+ {
+ { -3.0f / 255.0f, -6.0f / 255.0f, -9.0f / 255.0f, -15.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 8.0f / 255.0f, 14.0f / 255.0f },
+ { -3.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, -13.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f, 12.0f / 255.0f },
+ { -2.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 12.0f / 255.0f },
+ { -2.0f / 255.0f, -4.0f / 255.0f, -6.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 12.0f / 255.0f },
+
+ { -3.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -12.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 11.0f / 255.0f },
+ { -3.0f / 255.0f, -7.0f / 255.0f, -9.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f, 10.0f / 255.0f },
+ { -4.0f / 255.0f, -7.0f / 255.0f, -8.0f / 255.0f, -11.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f },
+ { -3.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f },
+
+ { -2.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f },
+ { -2.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f },
+ { -2.0f / 255.0f, -4.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f },
+ { -2.0f / 255.0f, -5.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f },
+
+ { -3.0f / 255.0f, -4.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f },
+ { -1.0f / 255.0f, -2.0f / 255.0f, -3.0f / 255.0f, -10.0f / 255.0f, 0.0f / 255.0f, 1.0f / 255.0f, 2.0f / 255.0f, 9.0f / 255.0f },
+ { -4.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -9.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 8.0f / 255.0f },
+ { -3.0f / 255.0f, -5.0f / 255.0f, -7.0f / 255.0f, -9.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f }
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ Block4x4Encoding_R11::Block4x4Encoding_R11(void)
+ {
+
+ m_pencodingbitsR11 = nullptr;
+
+ }
+
+ Block4x4Encoding_R11::~Block4x4Encoding_R11(void) {}
+ // ----------------------------------------------------------------------------------------------------
+ // initialization prior to encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits
+ //
+ void Block4x4Encoding_R11::InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+ unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric)
+ {
+ Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+
+ m_pencodingbitsR11 = (Block4x4EncodingBits_R11 *)a_paucEncodingBits;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits of a previous encoding
+ //
+ void Block4x4Encoding_R11::InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric)
+ {
+ m_pencodingbitsR11 = (Block4x4EncodingBits_R11 *)a_paucEncodingBits;
+
+ // init RGB portion
+ Block4x4Encoding_RGB8::InitFromEncodingBits(a_pblockParent,
+ (unsigned char *)m_pencodingbitsR11,
+ a_pafrgbaSource,
+ a_errormetric);
+
+ // init R11 portion
+ {
+ m_mode = MODE_R11;
+ if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+ {
+ m_fRedBase = (float)(signed char)m_pencodingbitsR11->data.base;
+ }
+ else
+ {
+ m_fRedBase = (float)(unsigned char)m_pencodingbitsR11->data.base;
+ }
+ m_fRedMultiplier = (float)m_pencodingbitsR11->data.multiplier;
+ m_uiRedModifierTableIndex = m_pencodingbitsR11->data.table;
+
+ unsigned long long int ulliSelectorBits = 0;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors0 << 40;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors1 << 32;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors2 << 24;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors3 << 16;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors4 << 8;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors5;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ unsigned int uiShift = 45 - (3 * uiPixel);
+ m_auiRedSelectors[uiPixel] = (ulliSelectorBits >> uiShift) & (SELECTORS - 1);
+ }
+
+ // decode the red channel
+ // calc red error
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ float fDecodedPixelData = 0.0f;
+ if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::R11 || a_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+ {
+ fDecodedPixelData = DecodePixelRed(m_fRedBase, m_fRedMultiplier,
+ m_uiRedModifierTableIndex,
+ m_auiRedSelectors[uiPixel]);
+ }
+ else if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+ {
+ fDecodedPixelData = DecodePixelRed(m_fRedBase + 128, m_fRedMultiplier,
+ m_uiRedModifierTableIndex,
+ m_auiRedSelectors[uiPixel]);
+ }
+ else
+ {
+ assert(0);
+ }
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(fDecodedPixelData, 0.0f, 0.0f, 1.0f);
+ }
+ CalcBlockError();
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ void Block4x4Encoding_R11::PerformIteration(float a_fEffort)
+ {
+ assert(!m_boolDone);
+ m_mode = MODE_R11;
+
+ switch (m_uiEncodingIterations)
+ {
+ case 0:
+ m_fError = FLT_MAX;
+ m_fRedBlockError = FLT_MAX; // artificially high value
+ CalculateR11(8, 0.0f, 0.0f);
+ m_fError = m_fRedBlockError;
+ break;
+
+ case 1:
+ CalculateR11(8, 2.0f, 1.0f);
+ m_fError = m_fRedBlockError;
+ if (a_fEffort <= 24.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 2:
+ CalculateR11(8, 12.0f, 1.0f);
+ m_fError = m_fRedBlockError;
+ if (a_fEffort <= 49.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 3:
+ CalculateR11(7, 6.0f, 1.0f);
+ m_fError = m_fRedBlockError;
+ break;
+
+ case 4:
+ CalculateR11(6, 3.0f, 1.0f);
+ m_fError = m_fRedBlockError;
+ break;
+
+ case 5:
+ CalculateR11(5, 1.0f, 0.0f);
+ m_fError = m_fRedBlockError;
+ m_boolDone = true;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ m_uiEncodingIterations++;
+ SetDoneIfPerfect();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find the best combination of base color, multiplier and selectors
+ //
+ // a_uiSelectorsUsed limits the number of selector combinations to try
+ // a_fBaseRadius limits the range of base colors to try
+ // a_fMultiplierRadius limits the range of multipliers to try
+ //
+ void Block4x4Encoding_R11::CalculateR11(unsigned int a_uiSelectorsUsed,
+ float a_fBaseRadius, float a_fMultiplierRadius)
+ {
+ // maps from virtual (monotonic) selector to ETC selector
+ static const unsigned int auiVirtualSelectorMap[8] = {3, 2, 1, 0, 4, 5, 6, 7};
+
+ // find min/max red
+ float fMinRed = 1.0f;
+ float fMaxRed = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ // ignore border pixels
+ float fAlpha = m_pafrgbaSource[uiPixel].fA;
+ if (isnan(fAlpha))
+ {
+ continue;
+ }
+
+ float fRed = m_pafrgbaSource[uiPixel].fR;
+
+ if (fRed < fMinRed)
+ {
+ fMinRed = fRed;
+ }
+ if (fRed > fMaxRed)
+ {
+ fMaxRed = fRed;
+ }
+ }
+ assert(fMinRed <= fMaxRed);
+
+ float fRedRange = (fMaxRed - fMinRed);
+
+ // try each modifier table entry
+ for (unsigned int uiTableEntry = 0; uiTableEntry < MODIFIER_TABLE_ENTRYS; uiTableEntry++)
+ {
+ for (unsigned int uiMinVirtualSelector = 0;
+ uiMinVirtualSelector <= (8- a_uiSelectorsUsed);
+ uiMinVirtualSelector++)
+ {
+ unsigned int uiMaxVirtualSelector = uiMinVirtualSelector + a_uiSelectorsUsed - 1;
+
+ unsigned int uiMinSelector = auiVirtualSelectorMap[uiMinVirtualSelector];
+ unsigned int uiMaxSelector = auiVirtualSelectorMap[uiMaxVirtualSelector];
+
+ float fTableEntryCenter = -s_aafModifierTable[uiTableEntry][uiMinSelector];
+
+ float fTableEntryRange = s_aafModifierTable[uiTableEntry][uiMaxSelector] -
+ s_aafModifierTable[uiTableEntry][uiMinSelector];
+
+ float fCenterRatio = fTableEntryCenter / fTableEntryRange;
+
+ float fCenter = fMinRed + fCenterRatio*fRedRange;
+ fCenter = roundf(255.0f * fCenter) / 255.0f;
+
+ float fMinBase = fCenter - (a_fBaseRadius / 255.0f);
+ if (fMinBase < 0.0f)
+ {
+ fMinBase = 0.0f;
+ }
+
+ float fMaxBase = fCenter + (a_fBaseRadius / 255.0f);
+ if (fMaxBase > 1.0f)
+ {
+ fMaxBase = 1.0f;
+ }
+
+ for (float fBase = fMinBase; fBase <= fMaxBase; fBase += (0.999999f / 255.0f))
+ {
+ float fRangeMultiplier = roundf(fRedRange / fTableEntryRange);
+
+ float fMinMultiplier = fRangeMultiplier - a_fMultiplierRadius;
+ if (fMinMultiplier < 1.0f)
+ {
+ fMinMultiplier = 0.0f;
+ }
+ else if (fMinMultiplier > 15.0f)
+ {
+ fMinMultiplier = 15.0f;
+ }
+
+ float fMaxMultiplier = fRangeMultiplier + a_fMultiplierRadius;
+ if (fMaxMultiplier < 1.0f)
+ {
+ fMaxMultiplier = 1.0f;
+ }
+ else if (fMaxMultiplier > 15.0f)
+ {
+ fMaxMultiplier = 15.0f;
+ }
+
+ for (float fMultiplier = fMinMultiplier; fMultiplier <= fMaxMultiplier; fMultiplier += 1.0f)
+ {
+ // find best selector for each pixel
+ unsigned int auiBestSelectors[PIXELS];
+ float afBestRedError[PIXELS];
+ float afBestPixelRed[PIXELS];
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ float fBestPixelRedError = FLT_MAX;
+
+ for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+ {
+ float fPixelRed = DecodePixelRed(fBase * 255.0f, fMultiplier, uiTableEntry, uiSelector);
+
+ ColorFloatRGBA frgba(fPixelRed, m_pafrgbaSource[uiPixel].fG,0.0f,1.0f);
+
+ float fPixelRedError = CalcPixelError(frgba, 1.0f, m_pafrgbaSource[uiPixel]);
+
+ if (fPixelRedError < fBestPixelRedError)
+ {
+ fBestPixelRedError = fPixelRedError;
+ auiBestSelectors[uiPixel] = uiSelector;
+ afBestRedError[uiPixel] = fBestPixelRedError;
+ afBestPixelRed[uiPixel] = fPixelRed;
+ }
+ }
+ }
+ float fBlockError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ fBlockError += afBestRedError[uiPixel];
+ }
+ if (fBlockError < m_fRedBlockError)
+ {
+ m_fRedBlockError = fBlockError;
+
+ if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+ {
+ m_fRedBase = 255.0f * fBase;
+ }
+ else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+ {
+ m_fRedBase = (fBase * 255) - 128;
+ }
+ else
+ {
+ assert(0);
+ }
+ m_fRedMultiplier = fMultiplier;
+ m_uiRedModifierTableIndex = uiTableEntry;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiRedSelectors[uiPixel] = auiBestSelectors[uiPixel];
+ float fBestPixelRed = afBestPixelRed[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(fBestPixelRed, 0.0f, 0.0f, 1.0f);
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state
+ //
+ void Block4x4Encoding_R11::SetEncodingBits(void)
+ {
+ if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+ {
+ m_pencodingbitsR11->data.base = (unsigned char)roundf(m_fRedBase);
+ }
+ else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+ {
+ m_pencodingbitsR11->data.base = (signed char)roundf(m_fRedBase);
+ }
+ else
+ {
+ assert(0);
+ }
+ m_pencodingbitsR11->data.table = m_uiRedModifierTableIndex;
+ m_pencodingbitsR11->data.multiplier = (unsigned char)roundf(m_fRedMultiplier);
+
+ unsigned long long int ulliSelectorBits = 0;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ unsigned int uiShift = 45 - (3 * uiPixel);
+ ulliSelectorBits |= ((unsigned long long int)m_auiRedSelectors[uiPixel]) << uiShift;
+ }
+
+ m_pencodingbitsR11->data.selectors0 = ulliSelectorBits >> 40;
+ m_pencodingbitsR11->data.selectors1 = ulliSelectorBits >> 32;
+ m_pencodingbitsR11->data.selectors2 = ulliSelectorBits >> 24;
+ m_pencodingbitsR11->data.selectors3 = ulliSelectorBits >> 16;
+ m_pencodingbitsR11->data.selectors4 = ulliSelectorBits >> 8;
+ m_pencodingbitsR11->data.selectors5 = ulliSelectorBits;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+}
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_R11.h b/thirdparty/etc2comp/EtcBlock4x4Encoding_R11.h
new file mode 100644
index 0000000000..b40c1e0036
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_R11.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_RGB8.h"
+
+namespace Etc
+{
+ class Block4x4EncodingBits_R11;
+
+ // ################################################################################
+ // Block4x4Encoding_R11
+ // ################################################################################
+
+ class Block4x4Encoding_R11 : public Block4x4Encoding_RGB8
+ {
+ public:
+
+ Block4x4Encoding_R11(void);
+ virtual ~Block4x4Encoding_R11(void);
+
+ virtual void InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+ unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric);
+
+ virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric);
+
+ virtual void PerformIteration(float a_fEffort);
+
+ virtual void SetEncodingBits(void);
+
+ inline float GetRedBase(void) const
+ {
+ return m_fRedBase;
+ }
+
+ inline float GetRedMultiplier(void) const
+ {
+ return m_fRedMultiplier;
+ }
+
+ inline int GetRedTableIndex(void) const
+ {
+ return m_uiRedModifierTableIndex;
+ }
+
+ inline const unsigned int * GetRedSelectors(void) const
+ {
+ return m_auiRedSelectors;
+ }
+
+ protected:
+
+ static const unsigned int MODIFIER_TABLE_ENTRYS = 16;
+ static const unsigned int SELECTOR_BITS = 3;
+ static const unsigned int SELECTORS = 1 << SELECTOR_BITS;
+
+ static float s_aafModifierTable[MODIFIER_TABLE_ENTRYS][SELECTORS];
+
+ void CalculateR11(unsigned int a_uiSelectorsUsed,
+ float a_fBaseRadius, float a_fMultiplierRadius);
+
+
+
+
+ inline float DecodePixelRed(float a_fBase, float a_fMultiplier,
+ unsigned int a_uiTableIndex, unsigned int a_uiSelector)
+ {
+ float fMultiplier = a_fMultiplier;
+ if (fMultiplier <= 0.0f)
+ {
+ fMultiplier = 1.0f / 8.0f;
+ }
+
+ float fPixelRed = a_fBase * 8 + 4 +
+ 8 * fMultiplier*s_aafModifierTable[a_uiTableIndex][a_uiSelector]*255;
+ fPixelRed /= 2047.0f;
+
+ if (fPixelRed < 0.0f)
+ {
+ fPixelRed = 0.0f;
+ }
+ else if (fPixelRed > 1.0f)
+ {
+ fPixelRed = 1.0f;
+ }
+
+ return fPixelRed;
+ }
+
+ Block4x4EncodingBits_R11 *m_pencodingbitsR11;
+
+ float m_fRedBase;
+ float m_fRedMultiplier;
+ float m_fRedBlockError;
+ unsigned int m_uiRedModifierTableIndex;
+ unsigned int m_auiRedSelectors[PIXELS];
+
+
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.cpp b/thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.cpp
new file mode 100644
index 0000000000..417835db51
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.cpp
@@ -0,0 +1,447 @@
+/*
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_RG11.cpp
+
+Block4x4Encoding_RG11 is the encoder to use when targetting file format RG11 and SRG11 (signed RG11).
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_RG11.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+ // ----------------------------------------------------------------------------------------------------
+ //
+ Block4x4Encoding_RG11::Block4x4Encoding_RG11(void)
+ {
+ m_pencodingbitsRG11 = nullptr;
+ }
+
+ Block4x4Encoding_RG11::~Block4x4Encoding_RG11(void) {}
+ // ----------------------------------------------------------------------------------------------------
+ // initialization prior to encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits
+ //
+ void Block4x4Encoding_RG11::InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+ unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric)
+ {
+ Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+
+ m_pencodingbitsRG11 = (Block4x4EncodingBits_RG11 *)a_paucEncodingBits;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits of a previous encoding
+ //
+ void Block4x4Encoding_RG11::InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric)
+ {
+
+ m_pencodingbitsRG11 = (Block4x4EncodingBits_RG11 *)a_paucEncodingBits;
+
+ // init RGB portion
+ Block4x4Encoding_RGB8::InitFromEncodingBits(a_pblockParent,
+ (unsigned char *)m_pencodingbitsRG11,
+ a_pafrgbaSource,
+ a_errormetric);
+ m_fError = 0.0f;
+
+ {
+ m_mode = MODE_RG11;
+ if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+ {
+ m_fRedBase = (float)(signed char)m_pencodingbitsRG11->data.baseR;
+ m_fGrnBase = (float)(signed char)m_pencodingbitsRG11->data.baseG;
+ }
+ else
+ {
+ m_fRedBase = (float)(unsigned char)m_pencodingbitsRG11->data.baseR;
+ m_fGrnBase = (float)(unsigned char)m_pencodingbitsRG11->data.baseG;
+ }
+ m_fRedMultiplier = (float)m_pencodingbitsRG11->data.multiplierR;
+ m_fGrnMultiplier = (float)m_pencodingbitsRG11->data.multiplierG;
+ m_uiRedModifierTableIndex = m_pencodingbitsRG11->data.tableIndexR;
+ m_uiGrnModifierTableIndex = m_pencodingbitsRG11->data.tableIndexG;
+
+ unsigned long long int ulliSelectorBitsR = 0;
+ ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR0 << 40;
+ ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR1 << 32;
+ ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR2 << 24;
+ ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR3 << 16;
+ ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR4 << 8;
+ ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR5;
+
+ unsigned long long int ulliSelectorBitsG = 0;
+ ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG0 << 40;
+ ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG1 << 32;
+ ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG2 << 24;
+ ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG3 << 16;
+ ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG4 << 8;
+ ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG5;
+
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ unsigned int uiShift = 45 - (3 * uiPixel);
+ m_auiRedSelectors[uiPixel] = (ulliSelectorBitsR >> uiShift) & (SELECTORS - 1);
+ m_auiGrnSelectors[uiPixel] = (ulliSelectorBitsG >> uiShift) & (SELECTORS - 1);
+ }
+
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ float fRedDecodedData = 0.0f;
+ float fGrnDecodedData = 0.0f;
+ if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+ {
+ fRedDecodedData = DecodePixelRed(m_fRedBase, m_fRedMultiplier, m_uiRedModifierTableIndex, m_auiRedSelectors[uiPixel]);
+ fGrnDecodedData = DecodePixelRed(m_fGrnBase, m_fGrnMultiplier, m_uiGrnModifierTableIndex, m_auiGrnSelectors[uiPixel]);
+ }
+ else if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+ {
+ fRedDecodedData = DecodePixelRed(m_fRedBase + 128, m_fRedMultiplier, m_uiRedModifierTableIndex, m_auiRedSelectors[uiPixel]);
+ fGrnDecodedData = DecodePixelRed(m_fGrnBase + 128, m_fGrnMultiplier, m_uiGrnModifierTableIndex, m_auiGrnSelectors[uiPixel]);
+ }
+ else
+ {
+ assert(0);
+ }
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(fRedDecodedData, fGrnDecodedData, 0.0f, 1.0f);
+ }
+
+ }
+
+ CalcBlockError();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ void Block4x4Encoding_RG11::PerformIteration(float a_fEffort)
+ {
+ assert(!m_boolDone);
+
+ switch (m_uiEncodingIterations)
+ {
+ case 0:
+ m_fError = FLT_MAX;
+ m_fGrnBlockError = FLT_MAX; // artificially high value
+ m_fRedBlockError = FLT_MAX;
+ CalculateR11(8, 0.0f, 0.0f);
+ CalculateG11(8, 0.0f, 0.0f);
+ m_fError = (m_fGrnBlockError + m_fRedBlockError);
+ break;
+
+ case 1:
+ CalculateR11(8, 2.0f, 1.0f);
+ CalculateG11(8, 2.0f, 1.0f);
+ m_fError = (m_fGrnBlockError + m_fRedBlockError);
+ if (a_fEffort <= 24.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 2:
+ CalculateR11(8, 12.0f, 1.0f);
+ CalculateG11(8, 12.0f, 1.0f);
+ m_fError = (m_fGrnBlockError + m_fRedBlockError);
+ if (a_fEffort <= 49.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 3:
+ CalculateR11(7, 6.0f, 1.0f);
+ CalculateG11(7, 6.0f, 1.0f);
+ m_fError = (m_fGrnBlockError + m_fRedBlockError);
+ break;
+
+ case 4:
+ CalculateR11(6, 3.0f, 1.0f);
+ CalculateG11(6, 3.0f, 1.0f);
+ m_fError = (m_fGrnBlockError + m_fRedBlockError);
+ break;
+
+ case 5:
+ CalculateR11(5, 1.0f, 0.0f);
+ CalculateG11(5, 1.0f, 0.0f);
+ m_fError = (m_fGrnBlockError + m_fRedBlockError);
+ m_boolDone = true;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ m_uiEncodingIterations++;
+ SetDoneIfPerfect();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find the best combination of base color, multiplier and selectors
+ //
+ // a_uiSelectorsUsed limits the number of selector combinations to try
+ // a_fBaseRadius limits the range of base colors to try
+ // a_fMultiplierRadius limits the range of multipliers to try
+ //
+ void Block4x4Encoding_RG11::CalculateG11(unsigned int a_uiSelectorsUsed,
+ float a_fBaseRadius, float a_fMultiplierRadius)
+ {
+ // maps from virtual (monotonic) selector to etc selector
+ static const unsigned int auiVirtualSelectorMap[8] = { 3, 2, 1, 0, 4, 5, 6, 7 };
+
+ // find min/max Grn
+ float fMinGrn = 1.0f;
+ float fMaxGrn = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ // ignore border pixels
+ float fAlpha = m_pafrgbaSource[uiPixel].fA;
+ if (isnan(fAlpha))
+ {
+ continue;
+ }
+
+ float fGrn = m_pafrgbaSource[uiPixel].fG;
+
+ if (fGrn < fMinGrn)
+ {
+ fMinGrn = fGrn;
+ }
+ if (fGrn > fMaxGrn)
+ {
+ fMaxGrn = fGrn;
+ }
+ }
+ assert(fMinGrn <= fMaxGrn);
+
+ float fGrnRange = (fMaxGrn - fMinGrn);
+
+ // try each modifier table entry
+ for (unsigned int uiTableEntry = 0; uiTableEntry < MODIFIER_TABLE_ENTRYS; uiTableEntry++)
+ {
+ for (unsigned int uiMinVirtualSelector = 0;
+ uiMinVirtualSelector <= (8 - a_uiSelectorsUsed);
+ uiMinVirtualSelector++)
+ {
+ unsigned int uiMaxVirtualSelector = uiMinVirtualSelector + a_uiSelectorsUsed - 1;
+
+ unsigned int uiMinSelector = auiVirtualSelectorMap[uiMinVirtualSelector];
+ unsigned int uiMaxSelector = auiVirtualSelectorMap[uiMaxVirtualSelector];
+
+ float fTableEntryCenter = -s_aafModifierTable[uiTableEntry][uiMinSelector];
+
+ float fTableEntryRange = s_aafModifierTable[uiTableEntry][uiMaxSelector] -
+ s_aafModifierTable[uiTableEntry][uiMinSelector];
+
+ float fCenterRatio = fTableEntryCenter / fTableEntryRange;
+
+ float fCenter = fMinGrn + fCenterRatio*fGrnRange;
+ fCenter = roundf(255.0f * fCenter) / 255.0f;
+
+ float fMinBase = fCenter - (a_fBaseRadius / 255.0f);
+ if (fMinBase < 0.0f)
+ {
+ fMinBase = 0.0f;
+ }
+
+ float fMaxBase = fCenter + (a_fBaseRadius / 255.0f);
+ if (fMaxBase > 1.0f)
+ {
+ fMaxBase = 1.0f;
+ }
+
+ for (float fBase = fMinBase; fBase <= fMaxBase; fBase += (0.999999f / 255.0f))
+ {
+ float fRangeMultiplier = roundf(fGrnRange / fTableEntryRange);
+
+ float fMinMultiplier = fRangeMultiplier - a_fMultiplierRadius;
+ if (fMinMultiplier < 1.0f)
+ {
+ fMinMultiplier = 0.0f;
+ }
+ else if (fMinMultiplier > 15.0f)
+ {
+ fMinMultiplier = 15.0f;
+ }
+
+ float fMaxMultiplier = fRangeMultiplier + a_fMultiplierRadius;
+ if (fMaxMultiplier < 1.0f)
+ {
+ fMaxMultiplier = 1.0f;
+ }
+ else if (fMaxMultiplier > 15.0f)
+ {
+ fMaxMultiplier = 15.0f;
+ }
+
+ for (float fMultiplier = fMinMultiplier; fMultiplier <= fMaxMultiplier; fMultiplier += 1.0f)
+ {
+ // find best selector for each pixel
+ unsigned int auiBestSelectors[PIXELS];
+ float afBestGrnError[PIXELS];
+ float afBestPixelGrn[PIXELS];
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ float fBestPixelGrnError = FLT_MAX;
+
+ for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+ {
+ //DecodePixelRed is not red channel specific
+ float fPixelGrn = DecodePixelRed(fBase * 255.0f, fMultiplier, uiTableEntry, uiSelector);
+
+ ColorFloatRGBA frgba(m_pafrgbaSource[uiPixel].fR, fPixelGrn, 0.0f, 1.0f);
+
+ float fPixelGrnError = CalcPixelError(frgba, 1.0f, m_pafrgbaSource[uiPixel]);
+
+ if (fPixelGrnError < fBestPixelGrnError)
+ {
+ fBestPixelGrnError = fPixelGrnError;
+ auiBestSelectors[uiPixel] = uiSelector;
+ afBestGrnError[uiPixel] = fBestPixelGrnError;
+ afBestPixelGrn[uiPixel] = fPixelGrn;
+ }
+ }
+ }
+ float fBlockError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ fBlockError += afBestGrnError[uiPixel];
+ }
+
+ if (fBlockError < m_fGrnBlockError)
+ {
+ m_fGrnBlockError = fBlockError;
+
+ if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+ {
+ m_fGrnBase = 255.0f * fBase;
+ }
+ else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+ {
+ m_fGrnBase = (fBase * 255) - 128;
+ }
+ else
+ {
+ assert(0);
+ }
+ m_fGrnMultiplier = fMultiplier;
+ m_uiGrnModifierTableIndex = uiTableEntry;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiGrnSelectors[uiPixel] = auiBestSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel].fG = afBestPixelGrn[uiPixel];
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state
+ //
+ void Block4x4Encoding_RG11::SetEncodingBits(void)
+ {
+ unsigned long long int ulliSelectorBitsR = 0;
+ unsigned long long int ulliSelectorBitsG = 0;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ unsigned int uiShift = 45 - (3 * uiPixel);
+ ulliSelectorBitsR |= ((unsigned long long int)m_auiRedSelectors[uiPixel]) << uiShift;
+ ulliSelectorBitsG |= ((unsigned long long int)m_auiGrnSelectors[uiPixel]) << uiShift;
+ }
+ if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+ {
+ m_pencodingbitsRG11->data.baseR = (unsigned char)roundf(m_fRedBase);
+ }
+ else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+ {
+ m_pencodingbitsRG11->data.baseR = (signed char)roundf(m_fRedBase);
+ }
+ else
+ {
+ assert(0);
+ }
+ m_pencodingbitsRG11->data.tableIndexR = m_uiRedModifierTableIndex;
+ m_pencodingbitsRG11->data.multiplierR = (unsigned char)roundf(m_fRedMultiplier);
+
+ m_pencodingbitsRG11->data.selectorsR0 = ulliSelectorBitsR >> 40;
+ m_pencodingbitsRG11->data.selectorsR1 = ulliSelectorBitsR >> 32;
+ m_pencodingbitsRG11->data.selectorsR2 = ulliSelectorBitsR >> 24;
+ m_pencodingbitsRG11->data.selectorsR3 = ulliSelectorBitsR >> 16;
+ m_pencodingbitsRG11->data.selectorsR4 = ulliSelectorBitsR >> 8;
+ m_pencodingbitsRG11->data.selectorsR5 = ulliSelectorBitsR;
+
+ if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+ {
+ m_pencodingbitsRG11->data.baseG = (unsigned char)roundf(m_fGrnBase);
+ }
+ else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+ {
+ m_pencodingbitsRG11->data.baseG = (signed char)roundf(m_fGrnBase);
+ }
+ else
+ {
+ assert(0);
+ }
+ m_pencodingbitsRG11->data.tableIndexG = m_uiGrnModifierTableIndex;
+ m_pencodingbitsRG11->data.multiplierG = (unsigned char)roundf(m_fGrnMultiplier);
+
+ m_pencodingbitsRG11->data.selectorsG0 = ulliSelectorBitsG >> 40;
+ m_pencodingbitsRG11->data.selectorsG1 = ulliSelectorBitsG >> 32;
+ m_pencodingbitsRG11->data.selectorsG2 = ulliSelectorBitsG >> 24;
+ m_pencodingbitsRG11->data.selectorsG3 = ulliSelectorBitsG >> 16;
+ m_pencodingbitsRG11->data.selectorsG4 = ulliSelectorBitsG >> 8;
+ m_pencodingbitsRG11->data.selectorsG5 = ulliSelectorBitsG;
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+}
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.h b/thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.h
new file mode 100644
index 0000000000..d4993b8c5f
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_RGB8.h"
+#include "EtcBlock4x4Encoding_R11.h"
+
+namespace Etc
+{
+ class Block4x4EncodingBits_RG11;
+
+ // ################################################################################
+ // Block4x4Encoding_RG11
+ // ################################################################################
+
+ class Block4x4Encoding_RG11 : public Block4x4Encoding_R11
+ {
+ float m_fGrnBase;
+ float m_fGrnMultiplier;
+ float m_fGrnBlockError;
+ unsigned int m_auiGrnSelectors[PIXELS];
+ unsigned int m_uiGrnModifierTableIndex;
+ public:
+
+ Block4x4Encoding_RG11(void);
+ virtual ~Block4x4Encoding_RG11(void);
+
+ virtual void InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+
+ unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric);
+
+ virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+
+ ErrorMetric a_errormetric);
+
+ virtual void PerformIteration(float a_fEffort);
+
+ virtual void SetEncodingBits(void);
+
+ Block4x4EncodingBits_RG11 *m_pencodingbitsRG11;
+
+ void CalculateG11(unsigned int a_uiSelectorsUsed, float a_fBaseRadius, float a_fMultiplierRadius);
+
+ inline float GetGrnBase(void) const
+ {
+ return m_fGrnBase;
+ }
+
+ inline float GetGrnMultiplier(void) const
+ {
+ return m_fGrnMultiplier;
+ }
+
+ inline int GetGrnTableIndex(void) const
+ {
+ return m_uiGrnModifierTableIndex;
+ }
+
+ inline const unsigned int * GetGrnSelectors(void) const
+ {
+ return m_auiGrnSelectors;
+ }
+
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.cpp b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.cpp
new file mode 100644
index 0000000000..5656556db9
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.cpp
@@ -0,0 +1,1730 @@
+/*
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_RGB8.cpp
+
+Block4x4Encoding_RGB8 is the encoder to use for the ETC2 extensions when targetting file format RGB8.
+This encoder is also used for the ETC2 subset of file format RGBA8.
+
+Block4x4Encoding_ETC1 encodes the ETC1 subset of RGB8.
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_RGB8.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+#include "EtcMath.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+ float Block4x4Encoding_RGB8::s_afTHDistanceTable[TH_DISTANCES] =
+ {
+ 3.0f / 255.0f,
+ 6.0f / 255.0f,
+ 11.0f / 255.0f,
+ 16.0f / 255.0f,
+ 23.0f / 255.0f,
+ 32.0f / 255.0f,
+ 41.0f / 255.0f,
+ 64.0f / 255.0f
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ Block4x4Encoding_RGB8::Block4x4Encoding_RGB8(void)
+ {
+
+ m_pencodingbitsRGB8 = nullptr;
+
+ }
+
+ Block4x4Encoding_RGB8::~Block4x4Encoding_RGB8(void) {}
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits of a previous encoding
+ //
+ void Block4x4Encoding_RGB8::InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric)
+ {
+
+ // handle ETC1 modes
+ Block4x4Encoding_ETC1::InitFromEncodingBits(a_pblockParent,
+ a_paucEncodingBits, a_pafrgbaSource,a_errormetric);
+
+ m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits;
+
+ // detect if there is a T, H or Planar mode present
+ if (m_pencodingbitsRGB8->differential.diff)
+ {
+ int iRed1 = (int)m_pencodingbitsRGB8->differential.red1;
+ int iDRed2 = m_pencodingbitsRGB8->differential.dred2;
+ int iRed2 = iRed1 + iDRed2;
+
+ int iGreen1 = (int)m_pencodingbitsRGB8->differential.green1;
+ int iDGreen2 = m_pencodingbitsRGB8->differential.dgreen2;
+ int iGreen2 = iGreen1 + iDGreen2;
+
+ int iBlue1 = (int)m_pencodingbitsRGB8->differential.blue1;
+ int iDBlue2 = m_pencodingbitsRGB8->differential.dblue2;
+ int iBlue2 = iBlue1 + iDBlue2;
+
+ if (iRed2 < 0 || iRed2 > 31)
+ {
+ InitFromEncodingBits_T();
+ }
+ else if (iGreen2 < 0 || iGreen2 > 31)
+ {
+ InitFromEncodingBits_H();
+ }
+ else if (iBlue2 < 0 || iBlue2 > 31)
+ {
+ InitFromEncodingBits_Planar();
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding if T mode is detected
+ //
+ void Block4x4Encoding_RGB8::InitFromEncodingBits_T(void)
+ {
+
+ m_mode = MODE_T;
+
+ unsigned char ucRed1 = (unsigned char)((m_pencodingbitsRGB8->t.red1a << 2) +
+ m_pencodingbitsRGB8->t.red1b);
+ unsigned char ucGreen1 = m_pencodingbitsRGB8->t.green1;
+ unsigned char ucBlue1 = m_pencodingbitsRGB8->t.blue1;
+
+ unsigned char ucRed2 = m_pencodingbitsRGB8->t.red2;
+ unsigned char ucGreen2 = m_pencodingbitsRGB8->t.green2;
+ unsigned char ucBlue2 = m_pencodingbitsRGB8->t.blue2;
+
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2);
+
+ m_uiCW1 = (m_pencodingbitsRGB8->t.da << 1) + m_pencodingbitsRGB8->t.db;
+
+ Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+ DecodePixels_T();
+
+ CalcBlockError();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding if H mode is detected
+ //
+ void Block4x4Encoding_RGB8::InitFromEncodingBits_H(void)
+ {
+
+ m_mode = MODE_H;
+
+ unsigned char ucRed1 = m_pencodingbitsRGB8->h.red1;
+ unsigned char ucGreen1 = (unsigned char)((m_pencodingbitsRGB8->h.green1a << 1) +
+ m_pencodingbitsRGB8->h.green1b);
+ unsigned char ucBlue1 = (unsigned char)((m_pencodingbitsRGB8->h.blue1a << 3) +
+ (m_pencodingbitsRGB8->h.blue1b << 1) +
+ m_pencodingbitsRGB8->h.blue1c);
+
+ unsigned char ucRed2 = m_pencodingbitsRGB8->h.red2;
+ unsigned char ucGreen2 = (unsigned char)((m_pencodingbitsRGB8->h.green2a << 1) +
+ m_pencodingbitsRGB8->h.green2b);
+ unsigned char ucBlue2 = m_pencodingbitsRGB8->h.blue2;
+
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2);
+
+ // used to determine the LSB of the CW
+ unsigned int uiRGB1 = (unsigned int)(((int)ucRed1 << 16) + ((int)ucGreen1 << 8) + (int)ucBlue1);
+ unsigned int uiRGB2 = (unsigned int)(((int)ucRed2 << 16) + ((int)ucGreen2 << 8) + (int)ucBlue2);
+
+ m_uiCW1 = (m_pencodingbitsRGB8->h.da << 2) + (m_pencodingbitsRGB8->h.db << 1);
+ if (uiRGB1 >= uiRGB2)
+ {
+ m_uiCW1++;
+ }
+
+ Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+ DecodePixels_H();
+
+ CalcBlockError();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding if Planar mode is detected
+ //
+ void Block4x4Encoding_RGB8::InitFromEncodingBits_Planar(void)
+ {
+
+ m_mode = MODE_PLANAR;
+
+ unsigned char ucOriginRed = m_pencodingbitsRGB8->planar.originRed;
+ unsigned char ucOriginGreen = (unsigned char)((m_pencodingbitsRGB8->planar.originGreen1 << 6) +
+ m_pencodingbitsRGB8->planar.originGreen2);
+ unsigned char ucOriginBlue = (unsigned char)((m_pencodingbitsRGB8->planar.originBlue1 << 5) +
+ (m_pencodingbitsRGB8->planar.originBlue2 << 3) +
+ (m_pencodingbitsRGB8->planar.originBlue3 << 1) +
+ m_pencodingbitsRGB8->planar.originBlue4);
+
+ unsigned char ucHorizRed = (unsigned char)((m_pencodingbitsRGB8->planar.horizRed1 << 1) +
+ m_pencodingbitsRGB8->planar.horizRed2);
+ unsigned char ucHorizGreen = m_pencodingbitsRGB8->planar.horizGreen;
+ unsigned char ucHorizBlue = (unsigned char)((m_pencodingbitsRGB8->planar.horizBlue1 << 5) +
+ m_pencodingbitsRGB8->planar.horizBlue2);
+
+ unsigned char ucVertRed = (unsigned char)((m_pencodingbitsRGB8->planar.vertRed1 << 3) +
+ m_pencodingbitsRGB8->planar.vertRed2);
+ unsigned char ucVertGreen = (unsigned char)((m_pencodingbitsRGB8->planar.vertGreen1 << 2) +
+ m_pencodingbitsRGB8->planar.vertGreen2);
+ unsigned char ucVertBlue = m_pencodingbitsRGB8->planar.vertBlue;
+
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromR6G7B6(ucOriginRed, ucOriginGreen, ucOriginBlue);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromR6G7B6(ucHorizRed, ucHorizGreen, ucHorizBlue);
+ m_frgbaColor3 = ColorFloatRGBA::ConvertFromR6G7B6(ucVertRed, ucVertGreen, ucVertBlue);
+
+ DecodePixels_Planar();
+
+ CalcBlockError();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ void Block4x4Encoding_RGB8::PerformIteration(float a_fEffort)
+ {
+ assert(!m_boolDone);
+
+ switch (m_uiEncodingIterations)
+ {
+ case 0:
+ Block4x4Encoding_ETC1::PerformFirstIteration();
+ if (m_boolDone)
+ {
+ break;
+ }
+ TryPlanar(0);
+ SetDoneIfPerfect();
+ if (m_boolDone)
+ {
+ break;
+ }
+ TryTAndH(0);
+ break;
+
+ case 1:
+ Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 1, 0, 0);
+ break;
+
+ case 2:
+ Block4x4Encoding_ETC1::TryIndividual(m_boolMostLikelyFlip, 1);
+ break;
+
+ case 3:
+ Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0);
+ break;
+
+ case 4:
+ Block4x4Encoding_ETC1::TryIndividual(!m_boolMostLikelyFlip, 1);
+ break;
+
+ case 5:
+ TryPlanar(1);
+ if (a_fEffort <= 49.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 6:
+ TryTAndH(1);
+ if (a_fEffort <= 59.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 7:
+ Block4x4Encoding_ETC1::TryDegenerates1();
+ if (a_fEffort <= 69.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 8:
+ Block4x4Encoding_ETC1::TryDegenerates2();
+ if (a_fEffort <= 79.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 9:
+ Block4x4Encoding_ETC1::TryDegenerates3();
+ if (a_fEffort <= 89.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 10:
+ Block4x4Encoding_ETC1::TryDegenerates4();
+ m_boolDone = true;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ m_uiEncodingIterations++;
+
+ SetDoneIfPerfect();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try encoding in Planar mode
+ // save this encoding if it improves the error
+ //
+ void Block4x4Encoding_RGB8::TryPlanar(unsigned int a_uiRadius)
+ {
+ Block4x4Encoding_RGB8 encodingTry = *this;
+
+ // init "try"
+ {
+ encodingTry.m_mode = MODE_PLANAR;
+ encodingTry.m_boolDiff = true;
+ encodingTry.m_boolFlip = false;
+ }
+
+ encodingTry.CalculatePlanarCornerColors();
+
+ encodingTry.DecodePixels_Planar();
+
+ encodingTry.CalcBlockError();
+
+ if (a_uiRadius > 0)
+ {
+ encodingTry.TwiddlePlanar();
+ }
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = MODE_PLANAR;
+ m_boolDiff = true;
+ m_boolFlip = false;
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_frgbaColor3 = encodingTry.m_frgbaColor3;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try encoding in T mode or H mode
+ // save this encoding if it improves the error
+ //
+ void Block4x4Encoding_RGB8::TryTAndH(unsigned int a_uiRadius)
+ {
+
+ CalculateBaseColorsForTAndH();
+
+ TryT(a_uiRadius);
+
+ TryH(a_uiRadius);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // calculate original values for base colors
+ // store them in m_frgbaOriginalColor1 and m_frgbaOriginalColor2
+ //
+ void Block4x4Encoding_RGB8::CalculateBaseColorsForTAndH(void)
+ {
+
+ bool boolRGBX = m_pblockParent->GetImageSource()->GetErrorMetric() == ErrorMetric::RGBX;
+
+ ColorFloatRGBA frgbaBlockAverage = (m_frgbaSourceAverageLeft + m_frgbaSourceAverageRight) * 0.5f;
+
+ // find pixel farthest from average gray line
+ unsigned int uiFarthestPixel = 0;
+ float fFarthestGrayDistance2 = 0.0f;
+ unsigned int uiTransparentPixels = 0;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ // don't count transparent
+ if (m_pafrgbaSource[uiPixel].fA == 0.0f && !boolRGBX)
+ {
+ uiTransparentPixels++;
+ }
+ else
+ {
+ float fGrayDistance2 = CalcGrayDistance2(m_pafrgbaSource[uiPixel], frgbaBlockAverage);
+
+ if (fGrayDistance2 > fFarthestGrayDistance2)
+ {
+ uiFarthestPixel = uiPixel;
+ fFarthestGrayDistance2 = fGrayDistance2;
+ }
+ }
+ }
+ // a transparent block should not reach this method
+ assert(uiTransparentPixels < PIXELS);
+
+ // set the original base colors to:
+ // half way to the farthest pixel and
+ // the mirror color on the other side of the average
+ ColorFloatRGBA frgbaOffset = (m_pafrgbaSource[uiFarthestPixel] - frgbaBlockAverage) * 0.5f;
+ m_frgbaOriginalColor1_TAndH = (frgbaBlockAverage + frgbaOffset).QuantizeR4G4B4();
+ m_frgbaOriginalColor2_TAndH = (frgbaBlockAverage - frgbaOffset).ClampRGB().QuantizeR4G4B4(); // the "other side" might be out of range
+
+ // move base colors to find best fit
+ for (unsigned int uiIteration = 0; uiIteration < 10; uiIteration++)
+ {
+ // find the center of pixels closest to each color
+ float fPixelsCloserToColor1 = 0.0f;
+ ColorFloatRGBA frgbSumPixelsCloserToColor1;
+ float fPixelsCloserToColor2 = 0.0f;
+ ColorFloatRGBA frgbSumPixelsCloserToColor2;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ // don't count transparent pixels
+ if (m_pafrgbaSource[uiPixel].fA == 0.0f)
+ {
+ continue;
+ }
+
+ float fGrayDistance2ToColor1 = CalcGrayDistance2(m_pafrgbaSource[uiPixel], m_frgbaOriginalColor1_TAndH);
+ float fGrayDistance2ToColor2 = CalcGrayDistance2(m_pafrgbaSource[uiPixel], m_frgbaOriginalColor2_TAndH);
+
+ ColorFloatRGBA frgbaAlphaWeightedSource = m_pafrgbaSource[uiPixel] * m_pafrgbaSource[uiPixel].fA;
+
+ if (fGrayDistance2ToColor1 <= fGrayDistance2ToColor2)
+ {
+ fPixelsCloserToColor1 += m_pafrgbaSource[uiPixel].fA;
+ frgbSumPixelsCloserToColor1 = frgbSumPixelsCloserToColor1 + frgbaAlphaWeightedSource;
+ }
+ else
+ {
+ fPixelsCloserToColor2 += m_pafrgbaSource[uiPixel].fA;
+ frgbSumPixelsCloserToColor2 = frgbSumPixelsCloserToColor2 + frgbaAlphaWeightedSource;
+ }
+ }
+ if (fPixelsCloserToColor1 == 0.0f || fPixelsCloserToColor2 == 0.0f)
+ {
+ break;
+ }
+
+ ColorFloatRGBA frgbAvgColor1Pixels = (frgbSumPixelsCloserToColor1 * (1.0f / fPixelsCloserToColor1)).QuantizeR4G4B4();
+ ColorFloatRGBA frgbAvgColor2Pixels = (frgbSumPixelsCloserToColor2 * (1.0f / fPixelsCloserToColor2)).QuantizeR4G4B4();
+
+ if (frgbAvgColor1Pixels.fR == m_frgbaOriginalColor1_TAndH.fR &&
+ frgbAvgColor1Pixels.fG == m_frgbaOriginalColor1_TAndH.fG &&
+ frgbAvgColor1Pixels.fB == m_frgbaOriginalColor1_TAndH.fB &&
+ frgbAvgColor2Pixels.fR == m_frgbaOriginalColor2_TAndH.fR &&
+ frgbAvgColor2Pixels.fG == m_frgbaOriginalColor2_TAndH.fG &&
+ frgbAvgColor2Pixels.fB == m_frgbaOriginalColor2_TAndH.fB)
+ {
+ break;
+ }
+
+ m_frgbaOriginalColor1_TAndH = frgbAvgColor1Pixels;
+ m_frgbaOriginalColor2_TAndH = frgbAvgColor2Pixels;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try encoding in T mode
+ // save this encoding if it improves the error
+ //
+ // since pixels that use base color1 don't use the distance table, color1 and color2 can be twiddled independently
+ // better encoding can be found if TWIDDLE_RADIUS is set to 2, but it will be much slower
+ //
+ void Block4x4Encoding_RGB8::TryT(unsigned int a_uiRadius)
+ {
+ Block4x4Encoding_RGB8 encodingTry = *this;
+
+ // init "try"
+ {
+ encodingTry.m_mode = MODE_T;
+ encodingTry.m_boolDiff = true;
+ encodingTry.m_boolFlip = false;
+ encodingTry.m_fError = FLT_MAX;
+ }
+
+ int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f);
+ int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f);
+ int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f);
+
+ int iMinRed1 = iColor1Red - (int)a_uiRadius;
+ if (iMinRed1 < 0)
+ {
+ iMinRed1 = 0;
+ }
+ int iMaxRed1 = iColor1Red + (int)a_uiRadius;
+ if (iMaxRed1 > 15)
+ {
+ iMinRed1 = 15;
+ }
+
+ int iMinGreen1 = iColor1Green - (int)a_uiRadius;
+ if (iMinGreen1 < 0)
+ {
+ iMinGreen1 = 0;
+ }
+ int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
+ if (iMaxGreen1 > 15)
+ {
+ iMinGreen1 = 15;
+ }
+
+ int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
+ if (iMinBlue1 < 0)
+ {
+ iMinBlue1 = 0;
+ }
+ int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
+ if (iMaxBlue1 > 15)
+ {
+ iMinBlue1 = 15;
+ }
+
+ int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f);
+ int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f);
+ int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f);
+
+ int iMinRed2 = iColor2Red - (int)a_uiRadius;
+ if (iMinRed2 < 0)
+ {
+ iMinRed2 = 0;
+ }
+ int iMaxRed2 = iColor2Red + (int)a_uiRadius;
+ if (iMaxRed2 > 15)
+ {
+ iMinRed2 = 15;
+ }
+
+ int iMinGreen2 = iColor2Green - (int)a_uiRadius;
+ if (iMinGreen2 < 0)
+ {
+ iMinGreen2 = 0;
+ }
+ int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
+ if (iMaxGreen2 > 15)
+ {
+ iMinGreen2 = 15;
+ }
+
+ int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
+ if (iMinBlue2 < 0)
+ {
+ iMinBlue2 = 0;
+ }
+ int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
+ if (iMaxBlue2 > 15)
+ {
+ iMinBlue2 = 15;
+ }
+
+ for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++)
+ {
+ encodingTry.m_uiCW1 = uiDistance;
+
+ // twiddle m_frgbaOriginalColor2_TAndH
+ // twiddle color2 first, since it affects 3 selectors, while color1 only affects one selector
+ //
+ for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++)
+ {
+ for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++)
+ {
+ for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++)
+ {
+ for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++)
+ {
+ if (uiBaseColorSwaps == 0)
+ {
+ encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH;
+ encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+ }
+ else
+ {
+ encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+ encodingTry.m_frgbaColor2 = m_frgbaOriginalColor1_TAndH;
+ }
+
+ encodingTry.TryT_BestSelectorCombination();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = encodingTry.m_mode;
+ m_boolDiff = encodingTry.m_boolDiff;
+ m_boolFlip = encodingTry.m_boolFlip;
+
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_uiCW1 = encodingTry.m_uiCW1;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+ }
+ }
+ }
+ }
+ }
+
+ // twiddle m_frgbaOriginalColor1_TAndH
+ for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++)
+ {
+ for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++)
+ {
+ for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++)
+ {
+ for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++)
+ {
+ if (uiBaseColorSwaps == 0)
+ {
+ encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+ encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH;
+ }
+ else
+ {
+ encodingTry.m_frgbaColor1 = m_frgbaOriginalColor2_TAndH;
+ encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+ }
+
+ encodingTry.TryT_BestSelectorCombination();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = encodingTry.m_mode;
+ m_boolDiff = encodingTry.m_boolDiff;
+ m_boolFlip = encodingTry.m_boolFlip;
+
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_uiCW1 = encodingTry.m_uiCW1;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find best selector combination for TryT
+ // called on an encodingTry
+ //
+ void Block4x4Encoding_RGB8::TryT_BestSelectorCombination(void)
+ {
+
+ float fDistance = s_afTHDistanceTable[m_uiCW1];
+
+ unsigned int auiBestPixelSelectors[PIXELS];
+ float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+ ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS];
+ ColorFloatRGBA afrgbaDecodedPixel[SELECTORS];
+
+ assert(SELECTORS == 4);
+ afrgbaDecodedPixel[0] = m_frgbaColor1;
+ afrgbaDecodedPixel[1] = (m_frgbaColor2 + fDistance).ClampRGB();
+ afrgbaDecodedPixel[2] = m_frgbaColor2;
+ afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB();
+
+ // try each selector
+ for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+ {
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+
+ float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel],
+ m_pafrgbaSource[uiPixel]);
+
+ if (fPixelError < afBestPixelErrors[uiPixel])
+ {
+ afBestPixelErrors[uiPixel] = fPixelError;
+ auiBestPixelSelectors[uiPixel] = uiSelector;
+ afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector];
+ }
+ }
+ }
+
+
+ // add up all of the pixel errors
+ float fBlockError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ fBlockError += afBestPixelErrors[uiPixel];
+ }
+
+ if (fBlockError < m_fError)
+ {
+ m_fError = fBlockError;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel];
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try encoding in T mode
+ // save this encoding if it improves the error
+ //
+ // since all pixels use the distance table, color1 and color2 can NOT be twiddled independently
+ // TWIDDLE_RADIUS of 2 is WAY too slow
+ //
+ void Block4x4Encoding_RGB8::TryH(unsigned int a_uiRadius)
+ {
+ Block4x4Encoding_RGB8 encodingTry = *this;
+
+ // init "try"
+ {
+ encodingTry.m_mode = MODE_H;
+ encodingTry.m_boolDiff = true;
+ encodingTry.m_boolFlip = false;
+ encodingTry.m_fError = FLT_MAX;
+ }
+
+ int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f);
+ int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f);
+ int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f);
+
+ int iMinRed1 = iColor1Red - (int)a_uiRadius;
+ if (iMinRed1 < 0)
+ {
+ iMinRed1 = 0;
+ }
+ int iMaxRed1 = iColor1Red + (int)a_uiRadius;
+ if (iMaxRed1 > 15)
+ {
+ iMinRed1 = 15;
+ }
+
+ int iMinGreen1 = iColor1Green - (int)a_uiRadius;
+ if (iMinGreen1 < 0)
+ {
+ iMinGreen1 = 0;
+ }
+ int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
+ if (iMaxGreen1 > 15)
+ {
+ iMinGreen1 = 15;
+ }
+
+ int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
+ if (iMinBlue1 < 0)
+ {
+ iMinBlue1 = 0;
+ }
+ int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
+ if (iMaxBlue1 > 15)
+ {
+ iMinBlue1 = 15;
+ }
+
+ int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f);
+ int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f);
+ int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f);
+
+ int iMinRed2 = iColor2Red - (int)a_uiRadius;
+ if (iMinRed2 < 0)
+ {
+ iMinRed2 = 0;
+ }
+ int iMaxRed2 = iColor2Red + (int)a_uiRadius;
+ if (iMaxRed2 > 15)
+ {
+ iMinRed2 = 15;
+ }
+
+ int iMinGreen2 = iColor2Green - (int)a_uiRadius;
+ if (iMinGreen2 < 0)
+ {
+ iMinGreen2 = 0;
+ }
+ int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
+ if (iMaxGreen2 > 15)
+ {
+ iMinGreen2 = 15;
+ }
+
+ int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
+ if (iMinBlue2 < 0)
+ {
+ iMinBlue2 = 0;
+ }
+ int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
+ if (iMaxBlue2 > 15)
+ {
+ iMinBlue2 = 15;
+ }
+
+ for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++)
+ {
+ encodingTry.m_uiCW1 = uiDistance;
+
+ // twiddle m_frgbaOriginalColor1_TAndH
+ for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++)
+ {
+ for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++)
+ {
+ for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++)
+ {
+ encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+ encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH;
+
+ // if color1 == color2, H encoding issues can pop up, so abort
+ if (iRed1 == iColor2Red && iGreen1 == iColor2Green && iBlue1 == iColor2Blue)
+ {
+ continue;
+ }
+
+ encodingTry.TryH_BestSelectorCombination();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = encodingTry.m_mode;
+ m_boolDiff = encodingTry.m_boolDiff;
+ m_boolFlip = encodingTry.m_boolFlip;
+
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_uiCW1 = encodingTry.m_uiCW1;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+ }
+ }
+ }
+ }
+
+ // twiddle m_frgbaOriginalColor2_TAndH
+ for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++)
+ {
+ for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++)
+ {
+ for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++)
+ {
+ encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH;
+ encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+
+ // if color1 == color2, H encoding issues can pop up, so abort
+ if (iRed2 == iColor1Red && iGreen2 == iColor1Green && iBlue2 == iColor1Blue)
+ {
+ continue;
+ }
+
+ encodingTry.TryH_BestSelectorCombination();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = encodingTry.m_mode;
+ m_boolDiff = encodingTry.m_boolDiff;
+ m_boolFlip = encodingTry.m_boolFlip;
+
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_uiCW1 = encodingTry.m_uiCW1;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find best selector combination for TryH
+ // called on an encodingTry
+ //
+ void Block4x4Encoding_RGB8::TryH_BestSelectorCombination(void)
+ {
+
+ float fDistance = s_afTHDistanceTable[m_uiCW1];
+
+ unsigned int auiBestPixelSelectors[PIXELS];
+ float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+ ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS];
+ ColorFloatRGBA afrgbaDecodedPixel[SELECTORS];
+
+ assert(SELECTORS == 4);
+ afrgbaDecodedPixel[0] = (m_frgbaColor1 + fDistance).ClampRGB();
+ afrgbaDecodedPixel[1] = (m_frgbaColor1 - fDistance).ClampRGB();
+ afrgbaDecodedPixel[2] = (m_frgbaColor2 + fDistance).ClampRGB();
+ afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB();
+
+ // try each selector
+ for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+ {
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+
+ float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel],
+ m_pafrgbaSource[uiPixel]);
+
+ if (fPixelError < afBestPixelErrors[uiPixel])
+ {
+ afBestPixelErrors[uiPixel] = fPixelError;
+ auiBestPixelSelectors[uiPixel] = uiSelector;
+ afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector];
+ }
+ }
+ }
+
+
+ // add up all of the pixel errors
+ float fBlockError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ fBlockError += afBestPixelErrors[uiPixel];
+ }
+
+ if (fBlockError < m_fError)
+ {
+ m_fError = fBlockError;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel];
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // use linear regression to find the best fit for colors along the edges of the 4x4 block
+ //
+ void Block4x4Encoding_RGB8::CalculatePlanarCornerColors(void)
+ {
+ ColorFloatRGBA afrgbaRegression[MAX_PLANAR_REGRESSION_SIZE];
+ ColorFloatRGBA frgbaSlope;
+ ColorFloatRGBA frgbaOffset;
+
+ // top edge
+ afrgbaRegression[0] = m_pafrgbaSource[0];
+ afrgbaRegression[1] = m_pafrgbaSource[4];
+ afrgbaRegression[2] = m_pafrgbaSource[8];
+ afrgbaRegression[3] = m_pafrgbaSource[12];
+ ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset);
+ m_frgbaColor1 = frgbaOffset;
+ m_frgbaColor2 = (frgbaSlope * 4.0f) + frgbaOffset;
+
+ // left edge
+ afrgbaRegression[0] = m_pafrgbaSource[0];
+ afrgbaRegression[1] = m_pafrgbaSource[1];
+ afrgbaRegression[2] = m_pafrgbaSource[2];
+ afrgbaRegression[3] = m_pafrgbaSource[3];
+ ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset);
+ m_frgbaColor1 = (m_frgbaColor1 + frgbaOffset) * 0.5f; // average with top edge
+ m_frgbaColor3 = (frgbaSlope * 4.0f) + frgbaOffset;
+
+ // right edge
+ afrgbaRegression[0] = m_pafrgbaSource[12];
+ afrgbaRegression[1] = m_pafrgbaSource[13];
+ afrgbaRegression[2] = m_pafrgbaSource[14];
+ afrgbaRegression[3] = m_pafrgbaSource[15];
+ ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset);
+ m_frgbaColor2 = (m_frgbaColor2 + frgbaOffset) * 0.5f; // average with top edge
+
+ // bottom edge
+ afrgbaRegression[0] = m_pafrgbaSource[3];
+ afrgbaRegression[1] = m_pafrgbaSource[7];
+ afrgbaRegression[2] = m_pafrgbaSource[11];
+ afrgbaRegression[3] = m_pafrgbaSource[15];
+ ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset);
+ m_frgbaColor3 = (m_frgbaColor3 + frgbaOffset) * 0.5f; // average with left edge
+
+ // quantize corner colors to 6/7/6
+ m_frgbaColor1 = m_frgbaColor1.QuantizeR6G7B6();
+ m_frgbaColor2 = m_frgbaColor2.QuantizeR6G7B6();
+ m_frgbaColor3 = m_frgbaColor3.QuantizeR6G7B6();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try different corner colors by slightly changing R, G and B independently
+ //
+ // R, G and B decoding and errors are independent, so R, G and B twiddles can be independent
+ //
+ // return true if improvement
+ //
+ bool Block4x4Encoding_RGB8::TwiddlePlanar(void)
+ {
+ bool boolImprovement = false;
+
+ while (TwiddlePlanarR())
+ {
+ boolImprovement = true;
+ }
+
+ while (TwiddlePlanarG())
+ {
+ boolImprovement = true;
+ }
+
+ while (TwiddlePlanarB())
+ {
+ boolImprovement = true;
+ }
+
+ return boolImprovement;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try different corner colors by slightly changing R
+ //
+ bool Block4x4Encoding_RGB8::TwiddlePlanarR()
+ {
+ bool boolImprovement = false;
+
+ Block4x4Encoding_RGB8 encodingTry = *this;
+
+ // init "try"
+ {
+ encodingTry.m_mode = MODE_PLANAR;
+ encodingTry.m_boolDiff = true;
+ encodingTry.m_boolFlip = false;
+ }
+
+ int iOriginRed = encodingTry.m_frgbaColor1.IntRed(63.0f);
+ int iHorizRed = encodingTry.m_frgbaColor2.IntRed(63.0f);
+ int iVertRed = encodingTry.m_frgbaColor3.IntRed(63.0f);
+
+ for (int iTryOriginRed = iOriginRed - 1; iTryOriginRed <= iOriginRed + 1; iTryOriginRed++)
+ {
+ // check for out of range
+ if (iTryOriginRed < 0 || iTryOriginRed > 63)
+ {
+ continue;
+ }
+
+ encodingTry.m_frgbaColor1.fR = ((iTryOriginRed << 2) + (iTryOriginRed >> 4)) / 255.0f;
+
+ for (int iTryHorizRed = iHorizRed - 1; iTryHorizRed <= iHorizRed + 1; iTryHorizRed++)
+ {
+ // check for out of range
+ if (iTryHorizRed < 0 || iTryHorizRed > 63)
+ {
+ continue;
+ }
+
+ encodingTry.m_frgbaColor2.fR = ((iTryHorizRed << 2) + (iTryHorizRed >> 4)) / 255.0f;
+
+ for (int iTryVertRed = iVertRed - 1; iTryVertRed <= iVertRed + 1; iTryVertRed++)
+ {
+ // check for out of range
+ if (iTryVertRed < 0 || iTryVertRed > 63)
+ {
+ continue;
+ }
+
+ // don't bother with null twiddle
+ if (iTryOriginRed == iOriginRed && iTryHorizRed == iHorizRed && iTryVertRed == iVertRed)
+ {
+ continue;
+ }
+
+ encodingTry.m_frgbaColor3.fR = ((iTryVertRed << 2) + (iTryVertRed >> 4)) / 255.0f;
+
+ encodingTry.DecodePixels_Planar();
+
+ encodingTry.CalcBlockError();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = MODE_PLANAR;
+ m_boolDiff = true;
+ m_boolFlip = false;
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_frgbaColor3 = encodingTry.m_frgbaColor3;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+
+ boolImprovement = true;
+ }
+ }
+ }
+ }
+
+ return boolImprovement;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try different corner colors by slightly changing G
+ //
+ bool Block4x4Encoding_RGB8::TwiddlePlanarG()
+ {
+ bool boolImprovement = false;
+
+ Block4x4Encoding_RGB8 encodingTry = *this;
+
+ // init "try"
+ {
+ encodingTry.m_mode = MODE_PLANAR;
+ encodingTry.m_boolDiff = true;
+ encodingTry.m_boolFlip = false;
+ }
+
+ int iOriginGreen = encodingTry.m_frgbaColor1.IntGreen(127.0f);
+ int iHorizGreen = encodingTry.m_frgbaColor2.IntGreen(127.0f);
+ int iVertGreen = encodingTry.m_frgbaColor3.IntGreen(127.0f);
+
+ for (int iTryOriginGreen = iOriginGreen - 1; iTryOriginGreen <= iOriginGreen + 1; iTryOriginGreen++)
+ {
+ // check for out of range
+ if (iTryOriginGreen < 0 || iTryOriginGreen > 127)
+ {
+ continue;
+ }
+
+ encodingTry.m_frgbaColor1.fG = ((iTryOriginGreen << 1) + (iTryOriginGreen >> 6)) / 255.0f;
+
+ for (int iTryHorizGreen = iHorizGreen - 1; iTryHorizGreen <= iHorizGreen + 1; iTryHorizGreen++)
+ {
+ // check for out of range
+ if (iTryHorizGreen < 0 || iTryHorizGreen > 127)
+ {
+ continue;
+ }
+
+ encodingTry.m_frgbaColor2.fG = ((iTryHorizGreen << 1) + (iTryHorizGreen >> 6)) / 255.0f;
+
+ for (int iTryVertGreen = iVertGreen - 1; iTryVertGreen <= iVertGreen + 1; iTryVertGreen++)
+ {
+ // check for out of range
+ if (iTryVertGreen < 0 || iTryVertGreen > 127)
+ {
+ continue;
+ }
+
+ // don't bother with null twiddle
+ if (iTryOriginGreen == iOriginGreen &&
+ iTryHorizGreen == iHorizGreen &&
+ iTryVertGreen == iVertGreen)
+ {
+ continue;
+ }
+
+ encodingTry.m_frgbaColor3.fG = ((iTryVertGreen << 1) + (iTryVertGreen >> 6)) / 255.0f;
+
+ encodingTry.DecodePixels_Planar();
+
+ encodingTry.CalcBlockError();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = MODE_PLANAR;
+ m_boolDiff = true;
+ m_boolFlip = false;
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_frgbaColor3 = encodingTry.m_frgbaColor3;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+
+ boolImprovement = true;
+ }
+ }
+ }
+ }
+
+ return boolImprovement;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try different corner colors by slightly changing B
+ //
+ bool Block4x4Encoding_RGB8::TwiddlePlanarB()
+ {
+ bool boolImprovement = false;
+
+ Block4x4Encoding_RGB8 encodingTry = *this;
+
+ // init "try"
+ {
+ encodingTry.m_mode = MODE_PLANAR;
+ encodingTry.m_boolDiff = true;
+ encodingTry.m_boolFlip = false;
+ }
+
+ int iOriginBlue = encodingTry.m_frgbaColor1.IntBlue(63.0f);
+ int iHorizBlue = encodingTry.m_frgbaColor2.IntBlue(63.0f);
+ int iVertBlue = encodingTry.m_frgbaColor3.IntBlue(63.0f);
+
+ for (int iTryOriginBlue = iOriginBlue - 1; iTryOriginBlue <= iOriginBlue + 1; iTryOriginBlue++)
+ {
+ // check for out of range
+ if (iTryOriginBlue < 0 || iTryOriginBlue > 63)
+ {
+ continue;
+ }
+
+ encodingTry.m_frgbaColor1.fB = ((iTryOriginBlue << 2) + (iTryOriginBlue >> 4)) / 255.0f;
+
+ for (int iTryHorizBlue = iHorizBlue - 1; iTryHorizBlue <= iHorizBlue + 1; iTryHorizBlue++)
+ {
+ // check for out of range
+ if (iTryHorizBlue < 0 || iTryHorizBlue > 63)
+ {
+ continue;
+ }
+
+ encodingTry.m_frgbaColor2.fB = ((iTryHorizBlue << 2) + (iTryHorizBlue >> 4)) / 255.0f;
+
+ for (int iTryVertBlue = iVertBlue - 1; iTryVertBlue <= iVertBlue + 1; iTryVertBlue++)
+ {
+ // check for out of range
+ if (iTryVertBlue < 0 || iTryVertBlue > 63)
+ {
+ continue;
+ }
+
+ // don't bother with null twiddle
+ if (iTryOriginBlue == iOriginBlue && iTryHorizBlue == iHorizBlue && iTryVertBlue == iVertBlue)
+ {
+ continue;
+ }
+
+ encodingTry.m_frgbaColor3.fB = ((iTryVertBlue << 2) + (iTryVertBlue >> 4)) / 255.0f;
+
+ encodingTry.DecodePixels_Planar();
+
+ encodingTry.CalcBlockError();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = MODE_PLANAR;
+ m_boolDiff = true;
+ m_boolFlip = false;
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_frgbaColor3 = encodingTry.m_frgbaColor3;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+
+ boolImprovement = true;
+ }
+ }
+ }
+ }
+
+ return boolImprovement;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state
+ //
+ void Block4x4Encoding_RGB8::SetEncodingBits(void)
+ {
+
+ switch (m_mode)
+ {
+ case MODE_ETC1:
+ Block4x4Encoding_ETC1::SetEncodingBits();
+ break;
+
+ case MODE_T:
+ SetEncodingBits_T();
+ break;
+
+ case MODE_H:
+ SetEncodingBits_H();
+ break;
+
+ case MODE_PLANAR:
+ SetEncodingBits_Planar();
+ break;
+
+ default:
+ assert(false);
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state for T mode
+ //
+ void Block4x4Encoding_RGB8::SetEncodingBits_T(void)
+ {
+ static const bool SANITY_CHECK = true;
+
+ assert(m_mode == MODE_T);
+ assert(m_boolDiff == true);
+
+ unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+ unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+ unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+ unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+ unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+ unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+
+ m_pencodingbitsRGB8->t.red1a = uiRed1 >> 2;
+ m_pencodingbitsRGB8->t.red1b = uiRed1;
+ m_pencodingbitsRGB8->t.green1 = uiGreen1;
+ m_pencodingbitsRGB8->t.blue1 = uiBlue1;
+
+ m_pencodingbitsRGB8->t.red2 = uiRed2;
+ m_pencodingbitsRGB8->t.green2 = uiGreen2;
+ m_pencodingbitsRGB8->t.blue2 = uiBlue2;
+
+ m_pencodingbitsRGB8->t.da = m_uiCW1 >> 1;
+ m_pencodingbitsRGB8->t.db = m_uiCW1;
+
+ m_pencodingbitsRGB8->t.diff = 1;
+
+ Block4x4Encoding_ETC1::SetEncodingBits_Selectors();
+
+ // create an invalid R differential to trigger T mode
+ m_pencodingbitsRGB8->t.detect1 = 0;
+ m_pencodingbitsRGB8->t.detect2 = 0;
+ int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+ if (iRed2 >= 4)
+ {
+ m_pencodingbitsRGB8->t.detect1 = 7;
+ m_pencodingbitsRGB8->t.detect2 = 0;
+ }
+ else
+ {
+ m_pencodingbitsRGB8->t.detect1 = 0;
+ m_pencodingbitsRGB8->t.detect2 = 1;
+ }
+
+ if (SANITY_CHECK)
+ {
+ iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+
+ // make sure red overflows
+ assert(iRed2 < 0 || iRed2 > 31);
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state for H mode
+ //
+ // colors and selectors may need to swap in order to generate lsb of distance index
+ //
+ void Block4x4Encoding_RGB8::SetEncodingBits_H(void)
+ {
+ static const bool SANITY_CHECK = true;
+
+ assert(m_mode == MODE_H);
+ assert(m_boolDiff == true);
+
+ unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+ unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+ unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+ unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+ unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+ unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+
+ unsigned int uiColor1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1;
+ unsigned int uiColor2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2;
+
+ bool boolOddDistance = m_uiCW1 & 1;
+ bool boolSwapColors = (uiColor1 < uiColor2) ^ !boolOddDistance;
+
+ if (boolSwapColors)
+ {
+ m_pencodingbitsRGB8->h.red1 = uiRed2;
+ m_pencodingbitsRGB8->h.green1a = uiGreen2 >> 1;
+ m_pencodingbitsRGB8->h.green1b = uiGreen2;
+ m_pencodingbitsRGB8->h.blue1a = uiBlue2 >> 3;
+ m_pencodingbitsRGB8->h.blue1b = uiBlue2 >> 1;
+ m_pencodingbitsRGB8->h.blue1c = uiBlue2;
+
+ m_pencodingbitsRGB8->h.red2 = uiRed1;
+ m_pencodingbitsRGB8->h.green2a = uiGreen1 >> 1;
+ m_pencodingbitsRGB8->h.green2b = uiGreen1;
+ m_pencodingbitsRGB8->h.blue2 = uiBlue1;
+
+ m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2;
+ m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1;
+ }
+ else
+ {
+ m_pencodingbitsRGB8->h.red1 = uiRed1;
+ m_pencodingbitsRGB8->h.green1a = uiGreen1 >> 1;
+ m_pencodingbitsRGB8->h.green1b = uiGreen1;
+ m_pencodingbitsRGB8->h.blue1a = uiBlue1 >> 3;
+ m_pencodingbitsRGB8->h.blue1b = uiBlue1 >> 1;
+ m_pencodingbitsRGB8->h.blue1c = uiBlue1;
+
+ m_pencodingbitsRGB8->h.red2 = uiRed2;
+ m_pencodingbitsRGB8->h.green2a = uiGreen2 >> 1;
+ m_pencodingbitsRGB8->h.green2b = uiGreen2;
+ m_pencodingbitsRGB8->h.blue2 = uiBlue2;
+
+ m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2;
+ m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1;
+ }
+
+ m_pencodingbitsRGB8->h.diff = 1;
+
+ Block4x4Encoding_ETC1::SetEncodingBits_Selectors();
+
+ if (boolSwapColors)
+ {
+ m_pencodingbitsRGB8->h.selectors ^= 0x0000FFFF;
+ }
+
+ // create an invalid R differential to trigger T mode
+ m_pencodingbitsRGB8->h.detect1 = 0;
+ m_pencodingbitsRGB8->h.detect2 = 0;
+ m_pencodingbitsRGB8->h.detect3 = 0;
+ int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+ int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+ if (iRed2 < 0 || iRed2 > 31)
+ {
+ m_pencodingbitsRGB8->h.detect1 = 1;
+ }
+ if (iGreen2 >= 4)
+ {
+ m_pencodingbitsRGB8->h.detect2 = 7;
+ m_pencodingbitsRGB8->h.detect3 = 0;
+ }
+ else
+ {
+ m_pencodingbitsRGB8->h.detect2 = 0;
+ m_pencodingbitsRGB8->h.detect3 = 1;
+ }
+
+ if (SANITY_CHECK)
+ {
+ iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+ iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+
+ // make sure red doesn't overflow and green does
+ assert(iRed2 >= 0 && iRed2 <= 31);
+ assert(iGreen2 < 0 || iGreen2 > 31);
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state for Planar mode
+ //
+ void Block4x4Encoding_RGB8::SetEncodingBits_Planar(void)
+ {
+ static const bool SANITY_CHECK = true;
+
+ assert(m_mode == MODE_PLANAR);
+ assert(m_boolDiff == true);
+
+ unsigned int uiOriginRed = (unsigned int)m_frgbaColor1.IntRed(63.0f);
+ unsigned int uiOriginGreen = (unsigned int)m_frgbaColor1.IntGreen(127.0f);
+ unsigned int uiOriginBlue = (unsigned int)m_frgbaColor1.IntBlue(63.0f);
+
+ unsigned int uiHorizRed = (unsigned int)m_frgbaColor2.IntRed(63.0f);
+ unsigned int uiHorizGreen = (unsigned int)m_frgbaColor2.IntGreen(127.0f);
+ unsigned int uiHorizBlue = (unsigned int)m_frgbaColor2.IntBlue(63.0f);
+
+ unsigned int uiVertRed = (unsigned int)m_frgbaColor3.IntRed(63.0f);
+ unsigned int uiVertGreen = (unsigned int)m_frgbaColor3.IntGreen(127.0f);
+ unsigned int uiVertBlue = (unsigned int)m_frgbaColor3.IntBlue(63.0f);
+
+ m_pencodingbitsRGB8->planar.originRed = uiOriginRed;
+ m_pencodingbitsRGB8->planar.originGreen1 = uiOriginGreen >> 6;
+ m_pencodingbitsRGB8->planar.originGreen2 = uiOriginGreen;
+ m_pencodingbitsRGB8->planar.originBlue1 = uiOriginBlue >> 5;
+ m_pencodingbitsRGB8->planar.originBlue2 = uiOriginBlue >> 3;
+ m_pencodingbitsRGB8->planar.originBlue3 = uiOriginBlue >> 1;
+ m_pencodingbitsRGB8->planar.originBlue4 = uiOriginBlue;
+
+ m_pencodingbitsRGB8->planar.horizRed1 = uiHorizRed >> 1;
+ m_pencodingbitsRGB8->planar.horizRed2 = uiHorizRed;
+ m_pencodingbitsRGB8->planar.horizGreen = uiHorizGreen;
+ m_pencodingbitsRGB8->planar.horizBlue1 = uiHorizBlue >> 5;
+ m_pencodingbitsRGB8->planar.horizBlue2 = uiHorizBlue;
+
+ m_pencodingbitsRGB8->planar.vertRed1 = uiVertRed >> 3;
+ m_pencodingbitsRGB8->planar.vertRed2 = uiVertRed;
+ m_pencodingbitsRGB8->planar.vertGreen1 = uiVertGreen >> 2;
+ m_pencodingbitsRGB8->planar.vertGreen2 = uiVertGreen;
+ m_pencodingbitsRGB8->planar.vertBlue = uiVertBlue;
+
+ m_pencodingbitsRGB8->planar.diff = 1;
+
+ // create valid RG differentials and an invalid B differential to trigger planar mode
+ m_pencodingbitsRGB8->planar.detect1 = 0;
+ m_pencodingbitsRGB8->planar.detect2 = 0;
+ m_pencodingbitsRGB8->planar.detect3 = 0;
+ m_pencodingbitsRGB8->planar.detect4 = 0;
+ int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+ int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+ int iBlue2 = (int)m_pencodingbitsRGB8->differential.blue1 + (int)m_pencodingbitsRGB8->differential.dblue2;
+ if (iRed2 < 0 || iRed2 > 31)
+ {
+ m_pencodingbitsRGB8->planar.detect1 = 1;
+ }
+ if (iGreen2 < 0 || iGreen2 > 31)
+ {
+ m_pencodingbitsRGB8->planar.detect2 = 1;
+ }
+ if (iBlue2 >= 4)
+ {
+ m_pencodingbitsRGB8->planar.detect3 = 7;
+ m_pencodingbitsRGB8->planar.detect4 = 0;
+ }
+ else
+ {
+ m_pencodingbitsRGB8->planar.detect3 = 0;
+ m_pencodingbitsRGB8->planar.detect4 = 1;
+ }
+
+ if (SANITY_CHECK)
+ {
+ iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+ iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+ iBlue2 = (int)m_pencodingbitsRGB8->differential.blue1 + (int)m_pencodingbitsRGB8->differential.dblue2;
+
+ // make sure red and green don't overflow and blue does
+ assert(iRed2 >= 0 && iRed2 <= 31);
+ assert(iGreen2 >= 0 && iGreen2 <= 31);
+ assert(iBlue2 < 0 || iBlue2 > 31);
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the decoded colors and decoded alpha based on the encoding state for T mode
+ //
+ void Block4x4Encoding_RGB8::DecodePixels_T(void)
+ {
+
+ float fDistance = s_afTHDistanceTable[m_uiCW1];
+ ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f);
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ switch (m_auiSelectors[uiPixel])
+ {
+ case 0:
+ m_afrgbaDecodedColors[uiPixel] = m_frgbaColor1;
+ break;
+
+ case 1:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB();
+ break;
+
+ case 2:
+ m_afrgbaDecodedColors[uiPixel] = m_frgbaColor2;
+ break;
+
+ case 3:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB();
+ break;
+ }
+
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the decoded colors and decoded alpha based on the encoding state for H mode
+ //
+ void Block4x4Encoding_RGB8::DecodePixels_H(void)
+ {
+
+ float fDistance = s_afTHDistanceTable[m_uiCW1];
+ ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f);
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ switch (m_auiSelectors[uiPixel])
+ {
+ case 0:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 + frgbaDistance).ClampRGB();
+ break;
+
+ case 1:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 - frgbaDistance).ClampRGB();
+ break;
+
+ case 2:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB();
+ break;
+
+ case 3:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB();
+ break;
+ }
+
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the decoded colors and decoded alpha based on the encoding state for Planar mode
+ //
+ void Block4x4Encoding_RGB8::DecodePixels_Planar(void)
+ {
+
+ int iRO = (int)roundf(m_frgbaColor1.fR * 255.0f);
+ int iGO = (int)roundf(m_frgbaColor1.fG * 255.0f);
+ int iBO = (int)roundf(m_frgbaColor1.fB * 255.0f);
+
+ int iRH = (int)roundf(m_frgbaColor2.fR * 255.0f);
+ int iGH = (int)roundf(m_frgbaColor2.fG * 255.0f);
+ int iBH = (int)roundf(m_frgbaColor2.fB * 255.0f);
+
+ int iRV = (int)roundf(m_frgbaColor3.fR * 255.0f);
+ int iGV = (int)roundf(m_frgbaColor3.fG * 255.0f);
+ int iBV = (int)roundf(m_frgbaColor3.fB * 255.0f);
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ int iX = (int)(uiPixel >> 2);
+ int iY = (int)(uiPixel & 3);
+
+ int iR = (iX*(iRH - iRO) + iY*(iRV - iRO) + 4*iRO + 2) >> 2;
+ int iG = (iX*(iGH - iGO) + iY*(iGV - iGO) + 4*iGO + 2) >> 2;
+ int iB = (iX*(iBH - iBO) + iY*(iBV - iBO) + 4*iBO + 2) >> 2;
+
+ ColorFloatRGBA frgba;
+ frgba.fR = (float)iR / 255.0f;
+ frgba.fG = (float)iG / 255.0f;
+ frgba.fB = (float)iB / 255.0f;
+ frgba.fA = 1.0f;
+
+ m_afrgbaDecodedColors[uiPixel] = frgba.ClampRGB();
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a linear regression for the a_uiPixels in a_pafrgbaPixels[]
+ //
+ // output the closest color line using a_pfrgbaSlope and a_pfrgbaOffset
+ //
+ void Block4x4Encoding_RGB8::ColorRegression(ColorFloatRGBA *a_pafrgbaPixels, unsigned int a_uiPixels,
+ ColorFloatRGBA *a_pfrgbaSlope, ColorFloatRGBA *a_pfrgbaOffset)
+ {
+ typedef struct
+ {
+ float f[4];
+ } Float4;
+
+ Float4 *paf4Pixels = (Float4 *)(a_pafrgbaPixels);
+ Float4 *pf4Slope = (Float4 *)(a_pfrgbaSlope);
+ Float4 *pf4Offset = (Float4 *)(a_pfrgbaOffset);
+
+ float afX[MAX_PLANAR_REGRESSION_SIZE];
+ float afY[MAX_PLANAR_REGRESSION_SIZE];
+
+ // handle r, g and b separately. don't bother with a
+ for (unsigned int uiComponent = 0; uiComponent < 3; uiComponent++)
+ {
+ for (unsigned int uiPixel = 0; uiPixel < a_uiPixels; uiPixel++)
+ {
+ afX[uiPixel] = (float)uiPixel;
+ afY[uiPixel] = paf4Pixels[uiPixel].f[uiComponent];
+
+ }
+ Etc::Regression(afX, afY, a_uiPixels,
+ &(pf4Slope->f[uiComponent]), &(pf4Offset->f[uiComponent]));
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+}
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.h b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.h
new file mode 100644
index 0000000000..03754d5e3b
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_ETC1.h"
+
+namespace Etc
+{
+
+ class Block4x4Encoding_RGB8 : public Block4x4Encoding_ETC1
+ {
+ public:
+
+ Block4x4Encoding_RGB8(void);
+ virtual ~Block4x4Encoding_RGB8(void);
+
+ virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+
+ ErrorMetric a_errormetric);
+
+ virtual void PerformIteration(float a_fEffort);
+
+ virtual void SetEncodingBits(void);
+
+ inline ColorFloatRGBA GetColor3(void) const
+ {
+ return m_frgbaColor3;
+ }
+
+ protected:
+
+ static const unsigned int PLANAR_CORNER_COLORS = 3;
+ static const unsigned int MAX_PLANAR_REGRESSION_SIZE = 4;
+ static const unsigned int TH_DISTANCES = 8;
+
+ static float s_afTHDistanceTable[TH_DISTANCES];
+
+ void TryPlanar(unsigned int a_uiRadius);
+ void TryTAndH(unsigned int a_uiRadius);
+
+ void InitFromEncodingBits_Planar(void);
+
+ ColorFloatRGBA m_frgbaColor3; // used for planar
+
+ void SetEncodingBits_T(void);
+ void SetEncodingBits_H(void);
+ void SetEncodingBits_Planar(void);
+
+ // state shared between iterations
+ ColorFloatRGBA m_frgbaOriginalColor1_TAndH;
+ ColorFloatRGBA m_frgbaOriginalColor2_TAndH;
+
+ void CalculateBaseColorsForTAndH(void);
+ void TryT(unsigned int a_uiRadius);
+ void TryT_BestSelectorCombination(void);
+ void TryH(unsigned int a_uiRadius);
+ void TryH_BestSelectorCombination(void);
+
+ private:
+
+ void InitFromEncodingBits_T(void);
+ void InitFromEncodingBits_H(void);
+
+ void CalculatePlanarCornerColors(void);
+
+ void ColorRegression(ColorFloatRGBA *a_pafrgbaPixels, unsigned int a_uiPixels,
+ ColorFloatRGBA *a_pfrgbaSlope, ColorFloatRGBA *a_pfrgbaOffset);
+
+ bool TwiddlePlanar(void);
+ bool TwiddlePlanarR();
+ bool TwiddlePlanarG();
+ bool TwiddlePlanarB();
+
+ void DecodePixels_T(void);
+ void DecodePixels_H(void);
+ void DecodePixels_Planar(void);
+
+ };
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.cpp b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.cpp
new file mode 100644
index 0000000000..ba2b42fb05
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.cpp
@@ -0,0 +1,1819 @@
+/*
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_RGB8A1.cpp contains:
+ Block4x4Encoding_RGB8A1
+ Block4x4Encoding_RGB8A1_Opaque
+ Block4x4Encoding_RGB8A1_Transparent
+
+These encoders are used when targetting file format RGB8A1.
+
+Block4x4Encoding_RGB8A1_Opaque is used when all pixels in the 4x4 block are opaque
+Block4x4Encoding_RGB8A1_Transparent is used when all pixels in the 4x4 block are transparent
+Block4x4Encoding_RGB8A1 is used when there is a mixture of alphas in the 4x4 block
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_RGB8A1.h"
+
+#include "EtcBlock4x4.h"
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4Encoding_RGB8.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+namespace Etc
+{
+
+ // ####################################################################################################
+ // Block4x4Encoding_RGB8A1
+ // ####################################################################################################
+
+ float Block4x4Encoding_RGB8A1::s_aafCwOpaqueUnsetTable[CW_RANGES][SELECTORS] =
+ {
+ { 0.0f / 255.0f, 8.0f / 255.0f, 0.0f / 255.0f, -8.0f / 255.0f },
+ { 0.0f / 255.0f, 17.0f / 255.0f, 0.0f / 255.0f, -17.0f / 255.0f },
+ { 0.0f / 255.0f, 29.0f / 255.0f, 0.0f / 255.0f, -29.0f / 255.0f },
+ { 0.0f / 255.0f, 42.0f / 255.0f, 0.0f / 255.0f, -42.0f / 255.0f },
+ { 0.0f / 255.0f, 60.0f / 255.0f, 0.0f / 255.0f, -60.0f / 255.0f },
+ { 0.0f / 255.0f, 80.0f / 255.0f, 0.0f / 255.0f, -80.0f / 255.0f },
+ { 0.0f / 255.0f, 106.0f / 255.0f, 0.0f / 255.0f, -106.0f / 255.0f },
+ { 0.0f / 255.0f, 183.0f / 255.0f, 0.0f / 255.0f, -183.0f / 255.0f }
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ Block4x4Encoding_RGB8A1::Block4x4Encoding_RGB8A1(void)
+ {
+ m_pencodingbitsRGB8 = nullptr;
+ m_boolOpaque = false;
+ m_boolTransparent = false;
+ m_boolPunchThroughPixels = true;
+
+ }
+ Block4x4Encoding_RGB8A1::~Block4x4Encoding_RGB8A1(void) {}
+ // ----------------------------------------------------------------------------------------------------
+ // initialization prior to encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits
+ //
+ void Block4x4Encoding_RGB8A1::InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+ unsigned char *a_paucEncodingBits,
+ ErrorMetric a_errormetric)
+ {
+
+ Block4x4Encoding_RGB8::InitFromSource(a_pblockParent,
+ a_pafrgbaSource,
+ a_paucEncodingBits,
+ a_errormetric);
+
+ m_boolOpaque = a_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::OPAQUE;
+ m_boolTransparent = a_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::TRANSPARENT;
+ m_boolPunchThroughPixels = a_pblockParent->HasPunchThroughPixels();
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ if (m_pafrgbaSource[uiPixel].fA >= 0.5f)
+ {
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+ else
+ {
+ m_afDecodedAlphas[uiPixel] = 0.0f;
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits of a previous encoding
+ //
+ void Block4x4Encoding_RGB8A1::InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric)
+ {
+
+
+ InitFromEncodingBits_ETC1(a_pblockParent,
+ a_paucEncodingBits,
+ a_pafrgbaSource,
+ a_errormetric);
+
+ m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits;
+
+ // detect if there is a T, H or Planar mode present
+ int iRed1 = m_pencodingbitsRGB8->differential.red1;
+ int iDRed2 = m_pencodingbitsRGB8->differential.dred2;
+ int iRed2 = iRed1 + iDRed2;
+
+ int iGreen1 = m_pencodingbitsRGB8->differential.green1;
+ int iDGreen2 = m_pencodingbitsRGB8->differential.dgreen2;
+ int iGreen2 = iGreen1 + iDGreen2;
+
+ int iBlue1 = m_pencodingbitsRGB8->differential.blue1;
+ int iDBlue2 = m_pencodingbitsRGB8->differential.dblue2;
+ int iBlue2 = iBlue1 + iDBlue2;
+
+ if (iRed2 < 0 || iRed2 > 31)
+ {
+ InitFromEncodingBits_T();
+ }
+ else if (iGreen2 < 0 || iGreen2 > 31)
+ {
+ InitFromEncodingBits_H();
+ }
+ else if (iBlue2 < 0 || iBlue2 > 31)
+ {
+ Block4x4Encoding_RGB8::InitFromEncodingBits_Planar();
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding assuming the encoding is an ETC1 mode.
+ // if it isn't an ETC1 mode, this will be overwritten later
+ //
+ void Block4x4Encoding_RGB8A1::InitFromEncodingBits_ETC1(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric)
+ {
+ Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,
+ a_errormetric);
+
+ m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits;
+
+ m_mode = MODE_ETC1;
+ m_boolDiff = true;
+ m_boolFlip = m_pencodingbitsRGB8->differential.flip;
+ m_boolOpaque = m_pencodingbitsRGB8->differential.diff;
+
+ int iR2 = m_pencodingbitsRGB8->differential.red1 + m_pencodingbitsRGB8->differential.dred2;
+ if (iR2 < 0)
+ {
+ iR2 = 0;
+ }
+ else if (iR2 > 31)
+ {
+ iR2 = 31;
+ }
+
+ int iG2 = m_pencodingbitsRGB8->differential.green1 + m_pencodingbitsRGB8->differential.dgreen2;
+ if (iG2 < 0)
+ {
+ iG2 = 0;
+ }
+ else if (iG2 > 31)
+ {
+ iG2 = 31;
+ }
+
+ int iB2 = m_pencodingbitsRGB8->differential.blue1 + m_pencodingbitsRGB8->differential.dblue2;
+ if (iB2 < 0)
+ {
+ iB2 = 0;
+ }
+ else if (iB2 > 31)
+ {
+ iB2 = 31;
+ }
+
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5(m_pencodingbitsRGB8->differential.red1, m_pencodingbitsRGB8->differential.green1, m_pencodingbitsRGB8->differential.blue1);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iR2, (unsigned char)iG2, (unsigned char)iB2);
+
+ m_uiCW1 = m_pencodingbitsRGB8->differential.cw1;
+ m_uiCW2 = m_pencodingbitsRGB8->differential.cw2;
+
+ Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+ Decode_ETC1();
+
+ CalcBlockError();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding if T mode is detected
+ //
+ void Block4x4Encoding_RGB8A1::InitFromEncodingBits_T(void)
+ {
+ m_mode = MODE_T;
+
+ unsigned char ucRed1 = (unsigned char)((m_pencodingbitsRGB8->t.red1a << 2) +
+ m_pencodingbitsRGB8->t.red1b);
+ unsigned char ucGreen1 = m_pencodingbitsRGB8->t.green1;
+ unsigned char ucBlue1 = m_pencodingbitsRGB8->t.blue1;
+
+ unsigned char ucRed2 = m_pencodingbitsRGB8->t.red2;
+ unsigned char ucGreen2 = m_pencodingbitsRGB8->t.green2;
+ unsigned char ucBlue2 = m_pencodingbitsRGB8->t.blue2;
+
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2);
+
+ m_uiCW1 = (m_pencodingbitsRGB8->t.da << 1) + m_pencodingbitsRGB8->t.db;
+
+ Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+ DecodePixels_T();
+
+ CalcBlockError();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding if H mode is detected
+ //
+ void Block4x4Encoding_RGB8A1::InitFromEncodingBits_H(void)
+ {
+ m_mode = MODE_H;
+
+ unsigned char ucRed1 = m_pencodingbitsRGB8->h.red1;
+ unsigned char ucGreen1 = (unsigned char)((m_pencodingbitsRGB8->h.green1a << 1) +
+ m_pencodingbitsRGB8->h.green1b);
+ unsigned char ucBlue1 = (unsigned char)((m_pencodingbitsRGB8->h.blue1a << 3) +
+ (m_pencodingbitsRGB8->h.blue1b << 1) +
+ m_pencodingbitsRGB8->h.blue1c);
+
+ unsigned char ucRed2 = m_pencodingbitsRGB8->h.red2;
+ unsigned char ucGreen2 = (unsigned char)((m_pencodingbitsRGB8->h.green2a << 1) +
+ m_pencodingbitsRGB8->h.green2b);
+ unsigned char ucBlue2 = m_pencodingbitsRGB8->h.blue2;
+
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2);
+
+ // used to determine the LSB of the CW
+ unsigned int uiRGB1 = (unsigned int)(((int)ucRed1 << 16) + ((int)ucGreen1 << 8) + (int)ucBlue1);
+ unsigned int uiRGB2 = (unsigned int)(((int)ucRed2 << 16) + ((int)ucGreen2 << 8) + (int)ucBlue2);
+
+ m_uiCW1 = (m_pencodingbitsRGB8->h.da << 2) + (m_pencodingbitsRGB8->h.db << 1);
+ if (uiRGB1 >= uiRGB2)
+ {
+ m_uiCW1++;
+ }
+
+ Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+ DecodePixels_H();
+
+ CalcBlockError();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // for ETC1 modes, set the decoded colors and decoded alpha based on the encoding state
+ //
+ void Block4x4Encoding_RGB8A1::Decode_ETC1(void)
+ {
+
+ const unsigned int *pauiPixelOrder = m_boolFlip ? s_auiPixelOrderFlip1 : s_auiPixelOrderFlip0;
+
+ for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS; uiPixelOrder++)
+ {
+ ColorFloatRGBA *pfrgbaCenter = uiPixelOrder < 8 ? &m_frgbaColor1 : &m_frgbaColor2;
+ unsigned int uiCW = uiPixelOrder < 8 ? m_uiCW1 : m_uiCW2;
+
+ unsigned int uiPixel = pauiPixelOrder[uiPixelOrder];
+
+ float fDelta;
+ if (m_boolOpaque)
+ fDelta = Block4x4Encoding_ETC1::s_aafCwTable[uiCW][m_auiSelectors[uiPixel]];
+ else
+ fDelta = s_aafCwOpaqueUnsetTable[uiCW][m_auiSelectors[uiPixel]];
+
+ if (m_boolOpaque == false && m_auiSelectors[uiPixel] == TRANSPARENT_SELECTOR)
+ {
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+ m_afDecodedAlphas[uiPixel] = 0.0f;
+ }
+ else
+ {
+ m_afrgbaDecodedColors[uiPixel] = (*pfrgbaCenter + fDelta).ClampRGB();
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // for T mode, set the decoded colors and decoded alpha based on the encoding state
+ //
+ void Block4x4Encoding_RGB8A1::DecodePixels_T(void)
+ {
+
+ float fDistance = s_afTHDistanceTable[m_uiCW1];
+ ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f);
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ switch (m_auiSelectors[uiPixel])
+ {
+ case 0:
+ m_afrgbaDecodedColors[uiPixel] = m_frgbaColor1;
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ break;
+
+ case 1:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB();
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ break;
+
+ case 2:
+ if (m_boolOpaque == false)
+ {
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+ m_afDecodedAlphas[uiPixel] = 0.0f;
+ }
+ else
+ {
+ m_afrgbaDecodedColors[uiPixel] = m_frgbaColor2;
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+ break;
+
+ case 3:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB();
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ break;
+ }
+
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // for H mode, set the decoded colors and decoded alpha based on the encoding state
+ //
+ void Block4x4Encoding_RGB8A1::DecodePixels_H(void)
+ {
+
+ float fDistance = s_afTHDistanceTable[m_uiCW1];
+ ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f);
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ switch (m_auiSelectors[uiPixel])
+ {
+ case 0:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 + frgbaDistance).ClampRGB();
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ break;
+
+ case 1:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 - frgbaDistance).ClampRGB();
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ break;
+
+ case 2:
+ if (m_boolOpaque == false)
+ {
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+ m_afDecodedAlphas[uiPixel] = 0.0f;
+ }
+ else
+ {
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB();
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+ break;
+
+ case 3:
+ m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB();
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ break;
+ }
+
+ }
+
+ }
+
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ // RGB8A1 can't use individual mode
+ // RGB8A1 with transparent pixels can't use planar mode
+ //
+ void Block4x4Encoding_RGB8A1::PerformIteration(float a_fEffort)
+ {
+ assert(!m_boolOpaque);
+ assert(!m_boolTransparent);
+ assert(!m_boolDone);
+
+ switch (m_uiEncodingIterations)
+ {
+ case 0:
+ PerformFirstIteration();
+ break;
+
+ case 1:
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, 0);
+ break;
+
+ case 2:
+ TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0);
+ if (a_fEffort <= 39.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 3:
+ Block4x4Encoding_RGB8::CalculateBaseColorsForTAndH();
+ TryT(1);
+ TryH(1);
+ if (a_fEffort <= 49.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 4:
+ TryDegenerates1();
+ if (a_fEffort <= 59.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 5:
+ TryDegenerates2();
+ if (a_fEffort <= 69.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 6:
+ TryDegenerates3();
+ if (a_fEffort <= 79.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 7:
+ TryDegenerates4();
+ m_boolDone = true;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ m_uiEncodingIterations++;
+
+ SetDoneIfPerfect();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find best initial encoding to ensure block has a valid encoding
+ //
+ void Block4x4Encoding_RGB8A1::PerformFirstIteration(void)
+ {
+ Block4x4Encoding_ETC1::CalculateMostLikelyFlip();
+
+ m_fError = FLT_MAX;
+
+ TryDifferential(m_boolMostLikelyFlip, 0, 0, 0);
+ SetDoneIfPerfect();
+ if (m_boolDone)
+ {
+ return;
+ }
+ TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0);
+ SetDoneIfPerfect();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // mostly copied from ETC1
+ // differences:
+ // Block4x4Encoding_RGB8A1 encodingTry = *this;
+ //
+ void Block4x4Encoding_RGB8A1::TryDifferential(bool a_boolFlip, unsigned int a_uiRadius,
+ int a_iGrayOffset1, int a_iGrayOffset2)
+ {
+
+ ColorFloatRGBA frgbaColor1;
+ ColorFloatRGBA frgbaColor2;
+
+ const unsigned int *pauiPixelMapping1;
+ const unsigned int *pauiPixelMapping2;
+
+ if (a_boolFlip)
+ {
+ frgbaColor1 = m_frgbaSourceAverageTop;
+ frgbaColor2 = m_frgbaSourceAverageBottom;
+
+ pauiPixelMapping1 = s_auiTopPixelMapping;
+ pauiPixelMapping2 = s_auiBottomPixelMapping;
+ }
+ else
+ {
+ frgbaColor1 = m_frgbaSourceAverageLeft;
+ frgbaColor2 = m_frgbaSourceAverageRight;
+
+ pauiPixelMapping1 = s_auiLeftPixelMapping;
+ pauiPixelMapping2 = s_auiRightPixelMapping;
+ }
+
+ DifferentialTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2,
+ a_uiRadius, a_iGrayOffset1, a_iGrayOffset2);
+
+ Block4x4Encoding_RGB8A1 encodingTry = *this;
+ encodingTry.m_boolFlip = a_boolFlip;
+
+ encodingTry.TryDifferentialHalf(&trys.m_half1);
+ encodingTry.TryDifferentialHalf(&trys.m_half2);
+
+ // find best halves that are within differential range
+ DifferentialTrys::Try *ptryBest1 = nullptr;
+ DifferentialTrys::Try *ptryBest2 = nullptr;
+ encodingTry.m_fError = FLT_MAX;
+
+ // see if the best of each half are in differential range
+ int iDRed = trys.m_half2.m_ptryBest->m_iRed - trys.m_half1.m_ptryBest->m_iRed;
+ int iDGreen = trys.m_half2.m_ptryBest->m_iGreen - trys.m_half1.m_ptryBest->m_iGreen;
+ int iDBlue = trys.m_half2.m_ptryBest->m_iBlue - trys.m_half1.m_ptryBest->m_iBlue;
+ if (iDRed >= -4 && iDRed <= 3 && iDGreen >= -4 && iDGreen <= 3 && iDBlue >= -4 && iDBlue <= 3)
+ {
+ ptryBest1 = trys.m_half1.m_ptryBest;
+ ptryBest2 = trys.m_half2.m_ptryBest;
+ encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError;
+ }
+ else
+ {
+ // else, find the next best halves that are in differential range
+ for (DifferentialTrys::Try *ptry1 = &trys.m_half1.m_atry[0];
+ ptry1 < &trys.m_half1.m_atry[trys.m_half1.m_uiTrys];
+ ptry1++)
+ {
+ for (DifferentialTrys::Try *ptry2 = &trys.m_half2.m_atry[0];
+ ptry2 < &trys.m_half2.m_atry[trys.m_half2.m_uiTrys];
+ ptry2++)
+ {
+ iDRed = ptry2->m_iRed - ptry1->m_iRed;
+ bool boolValidRedDelta = iDRed <= 3 && iDRed >= -4;
+ iDGreen = ptry2->m_iGreen - ptry1->m_iGreen;
+ bool boolValidGreenDelta = iDGreen <= 3 && iDGreen >= -4;
+ iDBlue = ptry2->m_iBlue - ptry1->m_iBlue;
+ bool boolValidBlueDelta = iDBlue <= 3 && iDBlue >= -4;
+
+ if (boolValidRedDelta && boolValidGreenDelta && boolValidBlueDelta)
+ {
+ float fError = ptry1->m_fError + ptry2->m_fError;
+
+ if (fError < encodingTry.m_fError)
+ {
+ encodingTry.m_fError = fError;
+
+ ptryBest1 = ptry1;
+ ptryBest2 = ptry2;
+ }
+ }
+
+ }
+ }
+ assert(encodingTry.m_fError < FLT_MAX);
+ assert(ptryBest1 != nullptr);
+ assert(ptryBest2 != nullptr);
+ }
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = MODE_ETC1;
+ m_boolDiff = true;
+ m_boolFlip = encodingTry.m_boolFlip;
+ m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue);
+ m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue);
+ m_uiCW1 = ptryBest1->m_uiCW;
+ m_uiCW2 = ptryBest2->m_uiCW;
+
+ m_fError = 0.0f;
+ for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++)
+ {
+ unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder];
+ unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder];
+
+ unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder];
+ unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+ m_auiSelectors[uiPixel1] = uiSelector1;
+ m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+ if (uiSelector1 == TRANSPARENT_SELECTOR)
+ {
+ m_afrgbaDecodedColors[uiPixel1] = ColorFloatRGBA();
+ m_afDecodedAlphas[uiPixel1] = 0.0f;
+ }
+ else
+ {
+ float fDeltaRGB1 = s_aafCwOpaqueUnsetTable[m_uiCW1][uiSelector1];
+ m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB();
+ m_afDecodedAlphas[uiPixel1] = 1.0f;
+ }
+
+ if (uiSelector2 == TRANSPARENT_SELECTOR)
+ {
+ m_afrgbaDecodedColors[uiPixel2] = ColorFloatRGBA();
+ m_afDecodedAlphas[uiPixel2] = 0.0f;
+ }
+ else
+ {
+ float fDeltaRGB2 = s_aafCwOpaqueUnsetTable[m_uiCW2][uiSelector2];
+ m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB();
+ m_afDecodedAlphas[uiPixel2] = 1.0f;
+ }
+
+ float fDeltaA1 = m_afDecodedAlphas[uiPixel1] - m_pafrgbaSource[uiPixel1].fA;
+ m_fError += fDeltaA1 * fDeltaA1;
+ float fDeltaA2 = m_afDecodedAlphas[uiPixel2] - m_pafrgbaSource[uiPixel2].fA;
+ m_fError += fDeltaA2 * fDeltaA2;
+ }
+
+ m_fError1 = ptryBest1->m_fError;
+ m_fError2 = ptryBest2->m_fError;
+ m_boolSeverelyBentDifferentialColors = trys.m_boolSeverelyBentColors;
+ m_fError = m_fError1 + m_fError2;
+
+ // sanity check
+ {
+ int iRed1 = m_frgbaColor1.IntRed(31.0f);
+ int iGreen1 = m_frgbaColor1.IntGreen(31.0f);
+ int iBlue1 = m_frgbaColor1.IntBlue(31.0f);
+
+ int iRed2 = m_frgbaColor2.IntRed(31.0f);
+ int iGreen2 = m_frgbaColor2.IntGreen(31.0f);
+ int iBlue2 = m_frgbaColor2.IntBlue(31.0f);
+
+ iDRed = iRed2 - iRed1;
+ iDGreen = iGreen2 - iGreen1;
+ iDBlue = iBlue2 - iBlue1;
+
+ assert(iDRed >= -4 && iDRed < 4);
+ assert(iDGreen >= -4 && iDGreen < 4);
+ assert(iDBlue >= -4 && iDBlue < 4);
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // mostly copied from ETC1
+ // differences:
+ // uses s_aafCwOpaqueUnsetTable
+ // color for selector set to 0,0,0,0
+ //
+ void Block4x4Encoding_RGB8A1::TryDifferentialHalf(DifferentialTrys::Half *a_phalf)
+ {
+
+ a_phalf->m_ptryBest = nullptr;
+ float fBestTryError = FLT_MAX;
+
+ a_phalf->m_uiTrys = 0;
+ for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius;
+ iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius;
+ iRed++)
+ {
+ assert(iRed >= 0 && iRed <= 31);
+
+ for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius;
+ iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius;
+ iGreen++)
+ {
+ assert(iGreen >= 0 && iGreen <= 31);
+
+ for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius;
+ iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius;
+ iBlue++)
+ {
+ assert(iBlue >= 0 && iBlue <= 31);
+
+ DifferentialTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys];
+ assert(ptry < &a_phalf->m_atry[DifferentialTrys::Half::MAX_TRYS]);
+
+ ptry->m_iRed = iRed;
+ ptry->m_iGreen = iGreen;
+ ptry->m_iBlue = iBlue;
+ ptry->m_fError = FLT_MAX;
+ ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue);
+
+ // try each CW
+ for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++)
+ {
+ unsigned int auiPixelSelectors[PIXELS / 2];
+ ColorFloatRGBA afrgbaDecodedColors[PIXELS / 2];
+ float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+ // pre-compute decoded pixels for each selector
+ ColorFloatRGBA afrgbaSelectors[SELECTORS];
+ assert(SELECTORS == 4);
+ afrgbaSelectors[0] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][0]).ClampRGB();
+ afrgbaSelectors[1] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][1]).ClampRGB();
+ afrgbaSelectors[2] = ColorFloatRGBA();
+ afrgbaSelectors[3] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][3]).ClampRGB();
+
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]];
+ ColorFloatRGBA frgbaDecodedPixel;
+
+ for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+ {
+ if (pfrgbaSourcePixel->fA < 0.5f)
+ {
+ uiSelector = TRANSPARENT_SELECTOR;
+ }
+ else if (uiSelector == TRANSPARENT_SELECTOR)
+ {
+ continue;
+ }
+
+ frgbaDecodedPixel = afrgbaSelectors[uiSelector];
+
+ float fPixelError;
+
+ fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]],
+ *pfrgbaSourcePixel);
+
+ if (fPixelError < afPixelErrors[uiPixel])
+ {
+ auiPixelSelectors[uiPixel] = uiSelector;
+ afrgbaDecodedColors[uiPixel] = frgbaDecodedPixel;
+ afPixelErrors[uiPixel] = fPixelError;
+ }
+
+ if (uiSelector == TRANSPARENT_SELECTOR)
+ {
+ break;
+ }
+ }
+ }
+
+ // add up all pixel errors
+ float fCWError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ fCWError += afPixelErrors[uiPixel];
+ }
+
+ // if best CW so far
+ if (fCWError < ptry->m_fError)
+ {
+ ptry->m_uiCW = uiCW;
+ for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+ {
+ ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel];
+ }
+ ptry->m_fError = fCWError;
+ }
+
+ }
+
+ if (ptry->m_fError < fBestTryError)
+ {
+ a_phalf->m_ptryBest = ptry;
+ fBestTryError = ptry->m_fError;
+ }
+
+ assert(ptry->m_fError < FLT_MAX);
+
+ a_phalf->m_uiTrys++;
+ }
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try encoding in T mode
+ // save this encoding if it improves the error
+ //
+ // since pixels that use base color1 don't use the distance table, color1 and color2 can be twiddled independently
+ // better encoding can be found if TWIDDLE_RADIUS is set to 2, but it will be much slower
+ //
+ void Block4x4Encoding_RGB8A1::TryT(unsigned int a_uiRadius)
+ {
+ Block4x4Encoding_RGB8A1 encodingTry = *this;
+
+ // init "try"
+ {
+ encodingTry.m_mode = MODE_T;
+ encodingTry.m_boolDiff = true;
+ encodingTry.m_boolFlip = false;
+ encodingTry.m_fError = FLT_MAX;
+ }
+
+ int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f);
+ int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f);
+ int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f);
+
+ int iMinRed1 = iColor1Red - (int)a_uiRadius;
+ if (iMinRed1 < 0)
+ {
+ iMinRed1 = 0;
+ }
+ int iMaxRed1 = iColor1Red + (int)a_uiRadius;
+ if (iMaxRed1 > 15)
+ {
+ iMinRed1 = 15;
+ }
+
+ int iMinGreen1 = iColor1Green - (int)a_uiRadius;
+ if (iMinGreen1 < 0)
+ {
+ iMinGreen1 = 0;
+ }
+ int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
+ if (iMaxGreen1 > 15)
+ {
+ iMinGreen1 = 15;
+ }
+
+ int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
+ if (iMinBlue1 < 0)
+ {
+ iMinBlue1 = 0;
+ }
+ int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
+ if (iMaxBlue1 > 15)
+ {
+ iMinBlue1 = 15;
+ }
+
+ int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f);
+ int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f);
+ int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f);
+
+ int iMinRed2 = iColor2Red - (int)a_uiRadius;
+ if (iMinRed2 < 0)
+ {
+ iMinRed2 = 0;
+ }
+ int iMaxRed2 = iColor2Red + (int)a_uiRadius;
+ if (iMaxRed2 > 15)
+ {
+ iMinRed2 = 15;
+ }
+
+ int iMinGreen2 = iColor2Green - (int)a_uiRadius;
+ if (iMinGreen2 < 0)
+ {
+ iMinGreen2 = 0;
+ }
+ int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
+ if (iMaxGreen2 > 15)
+ {
+ iMinGreen2 = 15;
+ }
+
+ int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
+ if (iMinBlue2 < 0)
+ {
+ iMinBlue2 = 0;
+ }
+ int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
+ if (iMaxBlue2 > 15)
+ {
+ iMinBlue2 = 15;
+ }
+
+ for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++)
+ {
+ encodingTry.m_uiCW1 = uiDistance;
+
+ // twiddle m_frgbaOriginalColor2_TAndH
+ // twiddle color2 first, since it affects 3 selectors, while color1 only affects one selector
+ //
+ for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++)
+ {
+ for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++)
+ {
+ for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++)
+ {
+ for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++)
+ {
+ if (uiBaseColorSwaps == 0)
+ {
+ encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH;
+ encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+ }
+ else
+ {
+ encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+ encodingTry.m_frgbaColor2 = m_frgbaOriginalColor1_TAndH;
+ }
+
+ encodingTry.TryT_BestSelectorCombination();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = encodingTry.m_mode;
+ m_boolDiff = encodingTry.m_boolDiff;
+ m_boolFlip = encodingTry.m_boolFlip;
+
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_uiCW1 = encodingTry.m_uiCW1;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+ }
+ }
+ }
+ }
+ }
+
+ // twiddle m_frgbaOriginalColor1_TAndH
+ for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++)
+ {
+ for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++)
+ {
+ for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++)
+ {
+ for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++)
+ {
+ if (uiBaseColorSwaps == 0)
+ {
+ encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+ encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH;
+ }
+ else
+ {
+ encodingTry.m_frgbaColor1 = m_frgbaOriginalColor2_TAndH;
+ encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+ }
+
+ encodingTry.TryT_BestSelectorCombination();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = encodingTry.m_mode;
+ m_boolDiff = encodingTry.m_boolDiff;
+ m_boolFlip = encodingTry.m_boolFlip;
+
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_uiCW1 = encodingTry.m_uiCW1;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find best selector combination for TryT
+ // called on an encodingTry
+ //
+ void Block4x4Encoding_RGB8A1::TryT_BestSelectorCombination(void)
+ {
+
+ float fDistance = s_afTHDistanceTable[m_uiCW1];
+
+ unsigned int auiBestPixelSelectors[PIXELS];
+ float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+ ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS];
+ ColorFloatRGBA afrgbaDecodedPixel[SELECTORS];
+
+ assert(SELECTORS == 4);
+ afrgbaDecodedPixel[0] = m_frgbaColor1;
+ afrgbaDecodedPixel[1] = (m_frgbaColor2 + fDistance).ClampRGB();
+ afrgbaDecodedPixel[2] = ColorFloatRGBA();
+ afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB();
+
+ // try each selector
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ unsigned int uiMinSelector = 0;
+ unsigned int uiMaxSelector = SELECTORS - 1;
+
+ if (m_pafrgbaSource[uiPixel].fA < 0.5f)
+ {
+ uiMinSelector = 2;
+ uiMaxSelector = 2;
+ }
+
+ for (unsigned int uiSelector = uiMinSelector; uiSelector <= uiMaxSelector; uiSelector++)
+ {
+ float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel],
+ m_pafrgbaSource[uiPixel]);
+
+ if (fPixelError < afBestPixelErrors[uiPixel])
+ {
+ afBestPixelErrors[uiPixel] = fPixelError;
+ auiBestPixelSelectors[uiPixel] = uiSelector;
+ afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector];
+ }
+ }
+ }
+
+
+ // add up all of the pixel errors
+ float fBlockError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ fBlockError += afBestPixelErrors[uiPixel];
+ }
+
+ if (fBlockError < m_fError)
+ {
+ m_fError = fBlockError;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel];
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try encoding in H mode
+ // save this encoding if it improves the error
+ //
+ // since all pixels use the distance table, color1 and color2 can NOT be twiddled independently
+ // TWIDDLE_RADIUS of 2 is WAY too slow
+ //
+ void Block4x4Encoding_RGB8A1::TryH(unsigned int a_uiRadius)
+ {
+ Block4x4Encoding_RGB8A1 encodingTry = *this;
+
+ // init "try"
+ {
+ encodingTry.m_mode = MODE_H;
+ encodingTry.m_boolDiff = true;
+ encodingTry.m_boolFlip = false;
+ encodingTry.m_fError = FLT_MAX;
+ }
+
+ int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f);
+ int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f);
+ int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f);
+
+ int iMinRed1 = iColor1Red - (int)a_uiRadius;
+ if (iMinRed1 < 0)
+ {
+ iMinRed1 = 0;
+ }
+ int iMaxRed1 = iColor1Red + (int)a_uiRadius;
+ if (iMaxRed1 > 15)
+ {
+ iMinRed1 = 15;
+ }
+
+ int iMinGreen1 = iColor1Green - (int)a_uiRadius;
+ if (iMinGreen1 < 0)
+ {
+ iMinGreen1 = 0;
+ }
+ int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
+ if (iMaxGreen1 > 15)
+ {
+ iMinGreen1 = 15;
+ }
+
+ int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
+ if (iMinBlue1 < 0)
+ {
+ iMinBlue1 = 0;
+ }
+ int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
+ if (iMaxBlue1 > 15)
+ {
+ iMinBlue1 = 15;
+ }
+
+ int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f);
+ int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f);
+ int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f);
+
+ int iMinRed2 = iColor2Red - (int)a_uiRadius;
+ if (iMinRed2 < 0)
+ {
+ iMinRed2 = 0;
+ }
+ int iMaxRed2 = iColor2Red + (int)a_uiRadius;
+ if (iMaxRed2 > 15)
+ {
+ iMinRed2 = 15;
+ }
+
+ int iMinGreen2 = iColor2Green - (int)a_uiRadius;
+ if (iMinGreen2 < 0)
+ {
+ iMinGreen2 = 0;
+ }
+ int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
+ if (iMaxGreen2 > 15)
+ {
+ iMinGreen2 = 15;
+ }
+
+ int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
+ if (iMinBlue2 < 0)
+ {
+ iMinBlue2 = 0;
+ }
+ int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
+ if (iMaxBlue2 > 15)
+ {
+ iMinBlue2 = 15;
+ }
+
+ for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++)
+ {
+ encodingTry.m_uiCW1 = uiDistance;
+
+ // twiddle m_frgbaOriginalColor1_TAndH
+ for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++)
+ {
+ for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++)
+ {
+ for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++)
+ {
+ encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+ encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH;
+
+ // if color1 == color2, H encoding issues can pop up, so abort
+ if (iRed1 == iColor2Red && iGreen1 == iColor2Green && iBlue1 == iColor2Blue)
+ {
+ continue;
+ }
+
+ encodingTry.TryH_BestSelectorCombination();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = encodingTry.m_mode;
+ m_boolDiff = encodingTry.m_boolDiff;
+ m_boolFlip = encodingTry.m_boolFlip;
+
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_uiCW1 = encodingTry.m_uiCW1;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+ }
+ }
+ }
+ }
+
+ // twiddle m_frgbaOriginalColor2_TAndH
+ for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++)
+ {
+ for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++)
+ {
+ for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++)
+ {
+ encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH;
+ encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+
+ // if color1 == color2, H encoding issues can pop up, so abort
+ if (iRed2 == iColor1Red && iGreen2 == iColor1Green && iBlue2 == iColor1Blue)
+ {
+ continue;
+ }
+
+ encodingTry.TryH_BestSelectorCombination();
+
+ if (encodingTry.m_fError < m_fError)
+ {
+ m_mode = encodingTry.m_mode;
+ m_boolDiff = encodingTry.m_boolDiff;
+ m_boolFlip = encodingTry.m_boolFlip;
+
+ m_frgbaColor1 = encodingTry.m_frgbaColor1;
+ m_frgbaColor2 = encodingTry.m_frgbaColor2;
+ m_uiCW1 = encodingTry.m_uiCW1;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+ }
+
+ m_fError = encodingTry.m_fError;
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find best selector combination for TryH
+ // called on an encodingTry
+ //
+ void Block4x4Encoding_RGB8A1::TryH_BestSelectorCombination(void)
+ {
+
+ // abort if colors and CW will pose an encoding problem
+ {
+ unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(255.0f);
+ unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(255.0f);
+ unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(255.0f);
+ unsigned int uiColorValue1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1;
+
+ unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(255.0f);
+ unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(255.0f);
+ unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(255.0f);
+ unsigned int uiColorValue2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2;
+
+ unsigned int uiCWLsb = m_uiCW1 & 1;
+
+ if ((uiColorValue1 >= (uiColorValue2 & uiCWLsb)) == 0 ||
+ (uiColorValue1 < (uiColorValue2 & uiCWLsb)) == 1)
+ {
+ return;
+ }
+ }
+
+ float fDistance = s_afTHDistanceTable[m_uiCW1];
+
+ unsigned int auiBestPixelSelectors[PIXELS];
+ float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+ ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS];
+ ColorFloatRGBA afrgbaDecodedPixel[SELECTORS];
+
+ assert(SELECTORS == 4);
+ afrgbaDecodedPixel[0] = (m_frgbaColor1 + fDistance).ClampRGB();
+ afrgbaDecodedPixel[1] = (m_frgbaColor1 - fDistance).ClampRGB();
+ afrgbaDecodedPixel[2] = ColorFloatRGBA();;
+ afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB();
+
+
+ // try each selector
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ unsigned int uiMinSelector = 0;
+ unsigned int uiMaxSelector = SELECTORS - 1;
+
+ if (m_pafrgbaSource[uiPixel].fA < 0.5f)
+ {
+ uiMinSelector = 2;
+ uiMaxSelector = 2;
+ }
+
+ for (unsigned int uiSelector = uiMinSelector; uiSelector <= uiMaxSelector; uiSelector++)
+ {
+ float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel],
+ m_pafrgbaSource[uiPixel]);
+
+ if (fPixelError < afBestPixelErrors[uiPixel])
+ {
+ afBestPixelErrors[uiPixel] = fPixelError;
+ auiBestPixelSelectors[uiPixel] = uiSelector;
+ afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector];
+ }
+ }
+ }
+
+
+ // add up all of the pixel errors
+ float fBlockError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ fBlockError += afBestPixelErrors[uiPixel];
+ }
+
+ if (fBlockError < m_fError)
+ {
+ m_fError = fBlockError;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel];
+ m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel];
+ }
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try version 1 of the degenerate search
+ // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+ // each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+ // be successfull
+ //
+ void Block4x4Encoding_RGB8A1::TryDegenerates1(void)
+ {
+
+ TryDifferential(m_boolMostLikelyFlip, 1, -2, 0);
+ TryDifferential(m_boolMostLikelyFlip, 1, 2, 0);
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, 2);
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, -2);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try version 2 of the degenerate search
+ // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+ // each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+ // be successfull
+ //
+ void Block4x4Encoding_RGB8A1::TryDegenerates2(void)
+ {
+
+ TryDifferential(!m_boolMostLikelyFlip, 1, -2, 0);
+ TryDifferential(!m_boolMostLikelyFlip, 1, 2, 0);
+ TryDifferential(!m_boolMostLikelyFlip, 1, 0, 2);
+ TryDifferential(!m_boolMostLikelyFlip, 1, 0, -2);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try version 3 of the degenerate search
+ // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+ // each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+ // be successfull
+ //
+ void Block4x4Encoding_RGB8A1::TryDegenerates3(void)
+ {
+
+ TryDifferential(m_boolMostLikelyFlip, 1, -2, -2);
+ TryDifferential(m_boolMostLikelyFlip, 1, -2, 2);
+ TryDifferential(m_boolMostLikelyFlip, 1, 2, -2);
+ TryDifferential(m_boolMostLikelyFlip, 1, 2, 2);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // try version 4 of the degenerate search
+ // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+ // each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+ // be successfull
+ //
+ void Block4x4Encoding_RGB8A1::TryDegenerates4(void)
+ {
+
+ TryDifferential(m_boolMostLikelyFlip, 1, -4, 0);
+ TryDifferential(m_boolMostLikelyFlip, 1, 4, 0);
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, 4);
+ TryDifferential(m_boolMostLikelyFlip, 1, 0, -4);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state
+ //
+ void Block4x4Encoding_RGB8A1::SetEncodingBits(void)
+ {
+ switch (m_mode)
+ {
+ case MODE_ETC1:
+ SetEncodingBits_ETC1();
+ break;
+
+ case MODE_T:
+ SetEncodingBits_T();
+ break;
+
+ case MODE_H:
+ SetEncodingBits_H();
+ break;
+
+ case MODE_PLANAR:
+ Block4x4Encoding_RGB8::SetEncodingBits_Planar();
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state if ETC1 mode
+ //
+ void Block4x4Encoding_RGB8A1::SetEncodingBits_ETC1(void)
+ {
+
+ // there is no individual mode in RGB8A1
+ assert(m_boolDiff);
+
+ int iRed1 = m_frgbaColor1.IntRed(31.0f);
+ int iGreen1 = m_frgbaColor1.IntGreen(31.0f);
+ int iBlue1 = m_frgbaColor1.IntBlue(31.0f);
+
+ int iRed2 = m_frgbaColor2.IntRed(31.0f);
+ int iGreen2 = m_frgbaColor2.IntGreen(31.0f);
+ int iBlue2 = m_frgbaColor2.IntBlue(31.0f);
+
+ int iDRed2 = iRed2 - iRed1;
+ int iDGreen2 = iGreen2 - iGreen1;
+ int iDBlue2 = iBlue2 - iBlue1;
+
+ assert(iDRed2 >= -4 && iDRed2 < 4);
+ assert(iDGreen2 >= -4 && iDGreen2 < 4);
+ assert(iDBlue2 >= -4 && iDBlue2 < 4);
+
+ m_pencodingbitsRGB8->differential.red1 = iRed1;
+ m_pencodingbitsRGB8->differential.green1 = iGreen1;
+ m_pencodingbitsRGB8->differential.blue1 = iBlue1;
+
+ m_pencodingbitsRGB8->differential.dred2 = iDRed2;
+ m_pencodingbitsRGB8->differential.dgreen2 = iDGreen2;
+ m_pencodingbitsRGB8->differential.dblue2 = iDBlue2;
+
+ m_pencodingbitsRGB8->individual.cw1 = m_uiCW1;
+ m_pencodingbitsRGB8->individual.cw2 = m_uiCW2;
+
+ SetEncodingBits_Selectors();
+
+ // in RGB8A1 encoding bits, opaque replaces differential
+ m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels;
+
+ m_pencodingbitsRGB8->individual.flip = m_boolFlip;
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state if T mode
+ //
+ void Block4x4Encoding_RGB8A1::SetEncodingBits_T(void)
+ {
+ static const bool SANITY_CHECK = true;
+
+ assert(m_mode == MODE_T);
+ assert(m_boolDiff == true);
+
+ unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+ unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+ unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+ unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+ unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+ unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+
+ m_pencodingbitsRGB8->t.red1a = uiRed1 >> 2;
+ m_pencodingbitsRGB8->t.red1b = uiRed1;
+ m_pencodingbitsRGB8->t.green1 = uiGreen1;
+ m_pencodingbitsRGB8->t.blue1 = uiBlue1;
+
+ m_pencodingbitsRGB8->t.red2 = uiRed2;
+ m_pencodingbitsRGB8->t.green2 = uiGreen2;
+ m_pencodingbitsRGB8->t.blue2 = uiBlue2;
+
+ m_pencodingbitsRGB8->t.da = m_uiCW1 >> 1;
+ m_pencodingbitsRGB8->t.db = m_uiCW1;
+
+ // in RGB8A1 encoding bits, opaque replaces differential
+ m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels;
+
+ Block4x4Encoding_ETC1::SetEncodingBits_Selectors();
+
+ // create an invalid R differential to trigger T mode
+ m_pencodingbitsRGB8->t.detect1 = 0;
+ m_pencodingbitsRGB8->t.detect2 = 0;
+ int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+ if (iRed2 >= 4)
+ {
+ m_pencodingbitsRGB8->t.detect1 = 7;
+ m_pencodingbitsRGB8->t.detect2 = 0;
+ }
+ else
+ {
+ m_pencodingbitsRGB8->t.detect1 = 0;
+ m_pencodingbitsRGB8->t.detect2 = 1;
+ }
+
+ if (SANITY_CHECK)
+ {
+ iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+
+ // make sure red overflows
+ assert(iRed2 < 0 || iRed2 > 31);
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state if H mode
+ //
+ // colors and selectors may need to swap in order to generate lsb of distance index
+ //
+ void Block4x4Encoding_RGB8A1::SetEncodingBits_H(void)
+ {
+ static const bool SANITY_CHECK = true;
+
+ assert(m_mode == MODE_H);
+ assert(m_boolDiff == true);
+
+ unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+ unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+ unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+ unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+ unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+ unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+
+ unsigned int uiColor1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1;
+ unsigned int uiColor2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2;
+
+ bool boolOddDistance = m_uiCW1 & 1;
+ bool boolSwapColors = (uiColor1 < uiColor2) ^ !boolOddDistance;
+
+ if (boolSwapColors)
+ {
+ m_pencodingbitsRGB8->h.red1 = uiRed2;
+ m_pencodingbitsRGB8->h.green1a = uiGreen2 >> 1;
+ m_pencodingbitsRGB8->h.green1b = uiGreen2;
+ m_pencodingbitsRGB8->h.blue1a = uiBlue2 >> 3;
+ m_pencodingbitsRGB8->h.blue1b = uiBlue2 >> 1;
+ m_pencodingbitsRGB8->h.blue1c = uiBlue2;
+
+ m_pencodingbitsRGB8->h.red2 = uiRed1;
+ m_pencodingbitsRGB8->h.green2a = uiGreen1 >> 1;
+ m_pencodingbitsRGB8->h.green2b = uiGreen1;
+ m_pencodingbitsRGB8->h.blue2 = uiBlue1;
+
+ m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2;
+ m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1;
+ }
+ else
+ {
+ m_pencodingbitsRGB8->h.red1 = uiRed1;
+ m_pencodingbitsRGB8->h.green1a = uiGreen1 >> 1;
+ m_pencodingbitsRGB8->h.green1b = uiGreen1;
+ m_pencodingbitsRGB8->h.blue1a = uiBlue1 >> 3;
+ m_pencodingbitsRGB8->h.blue1b = uiBlue1 >> 1;
+ m_pencodingbitsRGB8->h.blue1c = uiBlue1;
+
+ m_pencodingbitsRGB8->h.red2 = uiRed2;
+ m_pencodingbitsRGB8->h.green2a = uiGreen2 >> 1;
+ m_pencodingbitsRGB8->h.green2b = uiGreen2;
+ m_pencodingbitsRGB8->h.blue2 = uiBlue2;
+
+ m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2;
+ m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1;
+ }
+
+ // in RGB8A1 encoding bits, opaque replaces differential
+ m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels;
+
+ Block4x4Encoding_ETC1::SetEncodingBits_Selectors();
+
+ if (boolSwapColors)
+ {
+ m_pencodingbitsRGB8->h.selectors ^= 0x0000FFFF;
+ }
+
+ // create an invalid R differential to trigger T mode
+ m_pencodingbitsRGB8->h.detect1 = 0;
+ m_pencodingbitsRGB8->h.detect2 = 0;
+ m_pencodingbitsRGB8->h.detect3 = 0;
+ int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+ int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+ if (iRed2 < 0 || iRed2 > 31)
+ {
+ m_pencodingbitsRGB8->h.detect1 = 1;
+ }
+ if (iGreen2 >= 4)
+ {
+ m_pencodingbitsRGB8->h.detect2 = 7;
+ m_pencodingbitsRGB8->h.detect3 = 0;
+ }
+ else
+ {
+ m_pencodingbitsRGB8->h.detect2 = 0;
+ m_pencodingbitsRGB8->h.detect3 = 1;
+ }
+
+ if (SANITY_CHECK)
+ {
+ iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+ iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+
+ // make sure red doesn't overflow and green does
+ assert(iRed2 >= 0 && iRed2 <= 31);
+ assert(iGreen2 < 0 || iGreen2 > 31);
+ }
+
+ }
+
+ // ####################################################################################################
+ // Block4x4Encoding_RGB8A1_Opaque
+ // ####################################################################################################
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ void Block4x4Encoding_RGB8A1_Opaque::PerformIteration(float a_fEffort)
+ {
+ assert(!m_boolPunchThroughPixels);
+ assert(!m_boolTransparent);
+ assert(!m_boolDone);
+
+ switch (m_uiEncodingIterations)
+ {
+ case 0:
+ PerformFirstIteration();
+ break;
+
+ case 1:
+ Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 1, 0, 0);
+ break;
+
+ case 2:
+ Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0);
+ break;
+
+ case 3:
+ Block4x4Encoding_RGB8::TryPlanar(1);
+ break;
+
+ case 4:
+ Block4x4Encoding_RGB8::TryTAndH(1);
+ if (a_fEffort <= 49.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 5:
+ Block4x4Encoding_ETC1::TryDegenerates1();
+ if (a_fEffort <= 59.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 6:
+ Block4x4Encoding_ETC1::TryDegenerates2();
+ if (a_fEffort <= 69.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 7:
+ Block4x4Encoding_ETC1::TryDegenerates3();
+ if (a_fEffort <= 79.5f)
+ {
+ m_boolDone = true;
+ }
+ break;
+
+ case 8:
+ Block4x4Encoding_ETC1::TryDegenerates4();
+ m_boolDone = true;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ m_uiEncodingIterations++;
+ SetDoneIfPerfect();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find best initial encoding to ensure block has a valid encoding
+ //
+ void Block4x4Encoding_RGB8A1_Opaque::PerformFirstIteration(void)
+ {
+
+ // set decoded alphas
+ // calculate alpha error
+ m_fError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+
+ float fDeltaA = 1.0f - m_pafrgbaSource[uiPixel].fA;
+ m_fError += fDeltaA * fDeltaA;
+ }
+
+ CalculateMostLikelyFlip();
+
+ m_fError = FLT_MAX;
+
+ Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 0, 0, 0);
+ SetDoneIfPerfect();
+ if (m_boolDone)
+ {
+ return;
+ }
+ Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0);
+ SetDoneIfPerfect();
+ if (m_boolDone)
+ {
+ return;
+ }
+ Block4x4Encoding_RGB8::TryPlanar(0);
+ SetDoneIfPerfect();
+ if (m_boolDone)
+ {
+ return;
+ }
+ Block4x4Encoding_RGB8::TryTAndH(0);
+ SetDoneIfPerfect();
+ }
+
+ // ####################################################################################################
+ // Block4x4Encoding_RGB8A1_Transparent
+ // ####################################################################################################
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ void Block4x4Encoding_RGB8A1_Transparent::PerformIteration(float )
+ {
+ assert(!m_boolOpaque);
+ assert(m_boolTransparent);
+ assert(!m_boolDone);
+ assert(m_uiEncodingIterations == 0);
+
+ m_mode = MODE_ETC1;
+ m_boolDiff = true;
+ m_boolFlip = false;
+
+ m_uiCW1 = 0;
+ m_uiCW2 = 0;
+
+ m_frgbaColor1 = ColorFloatRGBA();
+ m_frgbaColor2 = ColorFloatRGBA();
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiSelectors[uiPixel] = TRANSPARENT_SELECTOR;
+
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+ m_afDecodedAlphas[uiPixel] = 0.0f;
+ }
+
+ CalcBlockError();
+
+ m_boolDone = true;
+ m_uiEncodingIterations++;
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+}
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.h b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.h
new file mode 100644
index 0000000000..ff26e462f8
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.h
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_RGB8.h"
+#include "EtcErrorMetric.h"
+#include "EtcBlock4x4EncodingBits.h"
+
+namespace Etc
+{
+
+ // ################################################################################
+ // Block4x4Encoding_RGB8A1
+ // RGB8A1 if not completely opaque or transparent
+ // ################################################################################
+
+ class Block4x4Encoding_RGB8A1 : public Block4x4Encoding_RGB8
+ {
+ public:
+
+ static const unsigned int TRANSPARENT_SELECTOR = 2;
+
+ Block4x4Encoding_RGB8A1(void);
+ virtual ~Block4x4Encoding_RGB8A1(void);
+
+ virtual void InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+ unsigned char *a_paucEncodingBits,
+ ErrorMetric a_errormetric);
+
+ virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric);
+
+ virtual void PerformIteration(float a_fEffort);
+
+ virtual void SetEncodingBits(void);
+
+ void InitFromEncodingBits_ETC1(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric);
+
+ void InitFromEncodingBits_T(void);
+ void InitFromEncodingBits_H(void);
+
+ void PerformFirstIteration(void);
+
+ void Decode_ETC1(void);
+ void DecodePixels_T(void);
+ void DecodePixels_H(void);
+ void SetEncodingBits_ETC1(void);
+ void SetEncodingBits_T(void);
+ void SetEncodingBits_H(void);
+
+ protected:
+
+ bool m_boolOpaque; // all source pixels have alpha >= 0.5
+ bool m_boolTransparent; // all source pixels have alpha < 0.5
+ bool m_boolPunchThroughPixels; // some source pixels have alpha < 0.5
+
+ static float s_aafCwOpaqueUnsetTable[CW_RANGES][SELECTORS];
+
+ private:
+
+ void TryDifferential(bool a_boolFlip, unsigned int a_uiRadius,
+ int a_iGrayOffset1, int a_iGrayOffset2);
+ void TryDifferentialHalf(DifferentialTrys::Half *a_phalf);
+
+ void TryT(unsigned int a_uiRadius);
+ void TryT_BestSelectorCombination(void);
+ void TryH(unsigned int a_uiRadius);
+ void TryH_BestSelectorCombination(void);
+
+ void TryDegenerates1(void);
+ void TryDegenerates2(void);
+ void TryDegenerates3(void);
+ void TryDegenerates4(void);
+
+ };
+
+ // ################################################################################
+ // Block4x4Encoding_RGB8A1_Opaque
+ // RGB8A1 if all pixels have alpha==1
+ // ################################################################################
+
+ class Block4x4Encoding_RGB8A1_Opaque : public Block4x4Encoding_RGB8A1
+ {
+ public:
+
+ virtual void PerformIteration(float a_fEffort);
+
+ void PerformFirstIteration(void);
+
+ private:
+
+ };
+
+ // ################################################################################
+ // Block4x4Encoding_RGB8A1_Transparent
+ // RGB8A1 if all pixels have alpha==0
+ // ################################################################################
+
+ class Block4x4Encoding_RGB8A1_Transparent : public Block4x4Encoding_RGB8A1
+ {
+ public:
+
+ virtual void PerformIteration(float a_fEffort);
+
+ private:
+
+ };
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.cpp b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.cpp
new file mode 100644
index 0000000000..600c7ab405
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.cpp
@@ -0,0 +1,474 @@
+/*
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_RGBA8.cpp contains:
+ Block4x4Encoding_RGBA8
+ Block4x4Encoding_RGBA8_Opaque
+ Block4x4Encoding_RGBA8_Transparent
+
+These encoders are used when targetting file format RGBA8.
+
+Block4x4Encoding_RGBA8_Opaque is used when all pixels in the 4x4 block are opaque
+Block4x4Encoding_RGBA8_Transparent is used when all pixels in the 4x4 block are transparent
+Block4x4Encoding_RGBA8 is used when there is a mixture of alphas in the 4x4 block
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_RGBA8.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+
+ // ####################################################################################################
+ // Block4x4Encoding_RGBA8
+ // ####################################################################################################
+
+ float Block4x4Encoding_RGBA8::s_aafModifierTable[MODIFIER_TABLE_ENTRYS][ALPHA_SELECTORS]
+ {
+ { -3.0f / 255.0f, -6.0f / 255.0f, -9.0f / 255.0f, -15.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 8.0f / 255.0f, 14.0f / 255.0f },
+ { -3.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, -13.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f, 12.0f / 255.0f },
+ { -2.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 12.0f / 255.0f },
+ { -2.0f / 255.0f, -4.0f / 255.0f, -6.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 12.0f / 255.0f },
+
+ { -3.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -12.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 11.0f / 255.0f },
+ { -3.0f / 255.0f, -7.0f / 255.0f, -9.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f, 10.0f / 255.0f },
+ { -4.0f / 255.0f, -7.0f / 255.0f, -8.0f / 255.0f, -11.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f },
+ { -3.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f },
+
+ { -2.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f },
+ { -2.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f },
+ { -2.0f / 255.0f, -4.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f },
+ { -2.0f / 255.0f, -5.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f },
+
+ { -3.0f / 255.0f, -4.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f },
+ { -1.0f / 255.0f, -2.0f / 255.0f, -3.0f / 255.0f, -10.0f / 255.0f, 0.0f / 255.0f, 1.0f / 255.0f, 2.0f / 255.0f, 9.0f / 255.0f },
+ { -4.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -9.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 8.0f / 255.0f },
+ { -3.0f / 255.0f, -5.0f / 255.0f, -7.0f / 255.0f, -9.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f }
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ Block4x4Encoding_RGBA8::Block4x4Encoding_RGBA8(void)
+ {
+
+ m_pencodingbitsA8 = nullptr;
+
+ }
+ Block4x4Encoding_RGBA8::~Block4x4Encoding_RGBA8(void) {}
+ // ----------------------------------------------------------------------------------------------------
+ // initialization prior to encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits
+ //
+ void Block4x4Encoding_RGBA8::InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+ unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric)
+ {
+ Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+
+ m_pencodingbitsA8 = (Block4x4EncodingBits_A8 *)a_paucEncodingBits;
+ m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)(a_paucEncodingBits + sizeof(Block4x4EncodingBits_A8));
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // initialization from the encoding bits of a previous encoding
+ // a_pblockParent points to the block associated with this encoding
+ // a_errormetric is used to choose the best encoding
+ // a_pafrgbaSource points to a 4x4 block subset of the source image
+ // a_paucEncodingBits points to the final encoding bits of a previous encoding
+ //
+ void Block4x4Encoding_RGBA8::InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric)
+ {
+
+ m_pencodingbitsA8 = (Block4x4EncodingBits_A8 *)a_paucEncodingBits;
+ m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)(a_paucEncodingBits + sizeof(Block4x4EncodingBits_A8));
+
+ // init RGB portion
+ Block4x4Encoding_RGB8::InitFromEncodingBits(a_pblockParent,
+ (unsigned char *) m_pencodingbitsRGB8,
+ a_pafrgbaSource,
+ a_errormetric);
+
+ // init A8 portion
+ // has to be done after InitFromEncodingBits()
+ {
+ m_fBase = m_pencodingbitsA8->data.base / 255.0f;
+ m_fMultiplier = (float)m_pencodingbitsA8->data.multiplier;
+ m_uiModifierTableIndex = m_pencodingbitsA8->data.table;
+
+ unsigned long long int ulliSelectorBits = 0;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors0 << 40;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors1 << 32;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors2 << 24;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors3 << 16;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors4 << 8;
+ ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors5;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ unsigned int uiShift = 45 - (3 * uiPixel);
+ m_auiAlphaSelectors[uiPixel] = (ulliSelectorBits >> uiShift) & (ALPHA_SELECTORS - 1);
+ }
+
+ // decode the alphas
+ // calc alpha error
+ m_fError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afDecodedAlphas[uiPixel] = DecodePixelAlpha(m_fBase, m_fMultiplier,
+ m_uiModifierTableIndex,
+ m_auiAlphaSelectors[uiPixel]);
+
+ float fDeltaAlpha = m_afDecodedAlphas[uiPixel] - m_pafrgbaSource[uiPixel].fA;
+ m_fError += fDeltaAlpha * fDeltaAlpha;
+ }
+ }
+
+ // redo error calc to include alpha
+ CalcBlockError();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ // similar to Block4x4Encoding_RGB8_Base::Encode_RGB8(), but with alpha added
+ //
+ void Block4x4Encoding_RGBA8::PerformIteration(float a_fEffort)
+ {
+ assert(!m_boolDone);
+
+ if (m_uiEncodingIterations == 0)
+ {
+ if (a_fEffort < 24.9f)
+ {
+ CalculateA8(0.0f);
+ }
+ else if (a_fEffort < 49.9f)
+ {
+ CalculateA8(1.0f);
+ }
+ else
+ {
+ CalculateA8(2.0f);
+ }
+ }
+
+ Block4x4Encoding_RGB8::PerformIteration(a_fEffort);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // find the best combination of base alpga, multiplier and selectors
+ //
+ // a_fRadius limits the range of base alpha to try
+ //
+ void Block4x4Encoding_RGBA8::CalculateA8(float a_fRadius)
+ {
+
+ // find min/max alpha
+ float fMinAlpha = 1.0f;
+ float fMaxAlpha = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ float fAlpha = m_pafrgbaSource[uiPixel].fA;
+
+ // ignore border pixels
+ if (isnan(fAlpha))
+ {
+ continue;
+ }
+
+ if (fAlpha < fMinAlpha)
+ {
+ fMinAlpha = fAlpha;
+ }
+ if (fAlpha > fMaxAlpha)
+ {
+ fMaxAlpha = fAlpha;
+ }
+ }
+ assert(fMinAlpha <= fMaxAlpha);
+
+ float fAlphaRange = fMaxAlpha - fMinAlpha;
+
+ // try each modifier table entry
+ m_fError = FLT_MAX; // artificially high value
+ for (unsigned int uiTableEntry = 0; uiTableEntry < MODIFIER_TABLE_ENTRYS; uiTableEntry++)
+ {
+ static const unsigned int MIN_VALUE_SELECTOR = 3;
+ static const unsigned int MAX_VALUE_SELECTOR = 7;
+
+ float fTableEntryCenter = -s_aafModifierTable[uiTableEntry][MIN_VALUE_SELECTOR];
+
+ float fTableEntryRange = s_aafModifierTable[uiTableEntry][MAX_VALUE_SELECTOR] -
+ s_aafModifierTable[uiTableEntry][MIN_VALUE_SELECTOR];
+
+ float fCenterRatio = fTableEntryCenter / fTableEntryRange;
+
+ float fCenter = fMinAlpha + fCenterRatio*fAlphaRange;
+ fCenter = roundf(255.0f * fCenter) / 255.0f;
+
+ float fMinBase = fCenter - (a_fRadius / 255.0f);
+ if (fMinBase < 0.0f)
+ {
+ fMinBase = 0.0f;
+ }
+
+ float fMaxBase = fCenter + (a_fRadius / 255.0f);
+ if (fMaxBase > 1.0f)
+ {
+ fMaxBase = 1.0f;
+ }
+
+ for (float fBase = fMinBase; fBase <= fMaxBase; fBase += (0.999999f / 255.0f))
+ {
+
+ float fRangeMultiplier = roundf(fAlphaRange / fTableEntryRange);
+
+ float fMinMultiplier = fRangeMultiplier - a_fRadius;
+ if (fMinMultiplier < 1.0f)
+ {
+ fMinMultiplier = 1.0f;
+ }
+ else if (fMinMultiplier > 15.0f)
+ {
+ fMinMultiplier = 15.0f;
+ }
+
+ float fMaxMultiplier = fRangeMultiplier + a_fRadius;
+ if (fMaxMultiplier < 1.0f)
+ {
+ fMaxMultiplier = 1.0f;
+ }
+ else if (fMaxMultiplier > 15.0f)
+ {
+ fMaxMultiplier = 15.0f;
+ }
+
+ for (float fMultiplier = fMinMultiplier; fMultiplier <= fMaxMultiplier; fMultiplier += 1.0f)
+ {
+ // find best selector for each pixel
+ unsigned int auiBestSelectors[PIXELS];
+ float afBestAlphaError[PIXELS];
+ float afBestDecodedAlphas[PIXELS];
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ float fBestPixelAlphaError = FLT_MAX;
+ for (unsigned int uiSelector = 0; uiSelector < ALPHA_SELECTORS; uiSelector++)
+ {
+ float fDecodedAlpha = DecodePixelAlpha(fBase, fMultiplier, uiTableEntry, uiSelector);
+
+ // border pixels (NAN) should have zero error
+ float fPixelDeltaAlpha = isnan(m_pafrgbaSource[uiPixel].fA) ?
+ 0.0f :
+ fDecodedAlpha - m_pafrgbaSource[uiPixel].fA;
+
+ float fPixelAlphaError = fPixelDeltaAlpha * fPixelDeltaAlpha;
+
+ if (fPixelAlphaError < fBestPixelAlphaError)
+ {
+ fBestPixelAlphaError = fPixelAlphaError;
+ auiBestSelectors[uiPixel] = uiSelector;
+ afBestAlphaError[uiPixel] = fBestPixelAlphaError;
+ afBestDecodedAlphas[uiPixel] = fDecodedAlpha;
+ }
+ }
+ }
+
+ float fBlockError = 0.0f;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ fBlockError += afBestAlphaError[uiPixel];
+ }
+
+ if (fBlockError < m_fError)
+ {
+ m_fError = fBlockError;
+
+ m_fBase = fBase;
+ m_fMultiplier = fMultiplier;
+ m_uiModifierTableIndex = uiTableEntry;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_auiAlphaSelectors[uiPixel] = auiBestSelectors[uiPixel];
+ m_afDecodedAlphas[uiPixel] = afBestDecodedAlphas[uiPixel];
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state
+ //
+ void Block4x4Encoding_RGBA8::SetEncodingBits(void)
+ {
+
+ // set the RGB8 portion
+ Block4x4Encoding_RGB8::SetEncodingBits();
+
+ // set the A8 portion
+ {
+ m_pencodingbitsA8->data.base = (unsigned char)roundf(255.0f * m_fBase);
+ m_pencodingbitsA8->data.table = m_uiModifierTableIndex;
+ m_pencodingbitsA8->data.multiplier = (unsigned char)roundf(m_fMultiplier);
+
+ unsigned long long int ulliSelectorBits = 0;
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ unsigned int uiShift = 45 - (3 * uiPixel);
+ ulliSelectorBits |= ((unsigned long long int)m_auiAlphaSelectors[uiPixel]) << uiShift;
+ }
+
+ m_pencodingbitsA8->data.selectors0 = ulliSelectorBits >> 40;
+ m_pencodingbitsA8->data.selectors1 = ulliSelectorBits >> 32;
+ m_pencodingbitsA8->data.selectors2 = ulliSelectorBits >> 24;
+ m_pencodingbitsA8->data.selectors3 = ulliSelectorBits >> 16;
+ m_pencodingbitsA8->data.selectors4 = ulliSelectorBits >> 8;
+ m_pencodingbitsA8->data.selectors5 = ulliSelectorBits;
+ }
+
+ }
+
+ // ####################################################################################################
+ // Block4x4Encoding_RGBA8_Opaque
+ // ####################################################################################################
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ void Block4x4Encoding_RGBA8_Opaque::PerformIteration(float a_fEffort)
+ {
+ assert(!m_boolDone);
+
+ if (m_uiEncodingIterations == 0)
+ {
+ m_fError = 0.0f;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afDecodedAlphas[uiPixel] = 1.0f;
+ }
+ }
+
+ Block4x4Encoding_RGB8::PerformIteration(a_fEffort);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state
+ //
+ void Block4x4Encoding_RGBA8_Opaque::SetEncodingBits(void)
+ {
+
+ // set the RGB8 portion
+ Block4x4Encoding_RGB8::SetEncodingBits();
+
+ // set the A8 portion
+ m_pencodingbitsA8->data.base = 255;
+ m_pencodingbitsA8->data.table = 15;
+ m_pencodingbitsA8->data.multiplier = 15;
+ m_pencodingbitsA8->data.selectors0 = 0xFF;
+ m_pencodingbitsA8->data.selectors1 = 0xFF;
+ m_pencodingbitsA8->data.selectors2 = 0xFF;
+ m_pencodingbitsA8->data.selectors3 = 0xFF;
+ m_pencodingbitsA8->data.selectors4 = 0xFF;
+ m_pencodingbitsA8->data.selectors5 = 0xFF;
+
+ }
+
+ // ####################################################################################################
+ // Block4x4Encoding_RGBA8_Transparent
+ // ####################################################################################################
+
+ // ----------------------------------------------------------------------------------------------------
+ // perform a single encoding iteration
+ // replace the encoding if a better encoding was found
+ // subsequent iterations generally take longer for each iteration
+ // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+ //
+ void Block4x4Encoding_RGBA8_Transparent::PerformIteration(float )
+ {
+ assert(!m_boolDone);
+ assert(m_uiEncodingIterations == 0);
+
+ m_mode = MODE_ETC1;
+ m_boolDiff = true;
+ m_boolFlip = false;
+
+ for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+ {
+ m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+ m_afDecodedAlphas[uiPixel] = 0.0f;
+ }
+
+ m_fError = 0.0f;
+
+ m_boolDone = true;
+ m_uiEncodingIterations++;
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // set the encoding bits based on encoding state
+ //
+ void Block4x4Encoding_RGBA8_Transparent::SetEncodingBits(void)
+ {
+
+ Block4x4Encoding_RGB8::SetEncodingBits();
+
+ // set the A8 portion
+ m_pencodingbitsA8->data.base = 0;
+ m_pencodingbitsA8->data.table = 0;
+ m_pencodingbitsA8->data.multiplier = 1;
+ m_pencodingbitsA8->data.selectors0 = 0;
+ m_pencodingbitsA8->data.selectors1 = 0;
+ m_pencodingbitsA8->data.selectors2 = 0;
+ m_pencodingbitsA8->data.selectors3 = 0;
+ m_pencodingbitsA8->data.selectors4 = 0;
+ m_pencodingbitsA8->data.selectors5 = 0;
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+}
diff --git a/thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.h b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.h
new file mode 100644
index 0000000000..5765d36b90
--- /dev/null
+++ b/thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.h
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_RGB8.h"
+
+namespace Etc
+{
+ class Block4x4EncodingBits_A8;
+
+ // ################################################################################
+ // Block4x4Encoding_RGBA8
+ // RGBA8 if not completely opaque or transparent
+ // ################################################################################
+
+ class Block4x4Encoding_RGBA8 : public Block4x4Encoding_RGB8
+ {
+ public:
+
+ Block4x4Encoding_RGBA8(void);
+ virtual ~Block4x4Encoding_RGBA8(void);
+
+ virtual void InitFromSource(Block4x4 *a_pblockParent,
+ ColorFloatRGBA *a_pafrgbaSource,
+ unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric);
+
+ virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+ unsigned char *a_paucEncodingBits,
+ ColorFloatRGBA *a_pafrgbaSource,
+ ErrorMetric a_errormetric);
+
+ virtual void PerformIteration(float a_fEffort);
+
+ virtual void SetEncodingBits(void);
+
+ protected:
+
+ static const unsigned int MODIFIER_TABLE_ENTRYS = 16;
+ static const unsigned int ALPHA_SELECTOR_BITS = 3;
+ static const unsigned int ALPHA_SELECTORS = 1 << ALPHA_SELECTOR_BITS;
+
+ static float s_aafModifierTable[MODIFIER_TABLE_ENTRYS][ALPHA_SELECTORS];
+
+ void CalculateA8(float a_fRadius);
+
+ Block4x4EncodingBits_A8 *m_pencodingbitsA8; // A8 portion of Block4x4EncodingBits_RGBA8
+
+ float m_fBase;
+ float m_fMultiplier;
+ unsigned int m_uiModifierTableIndex;
+ unsigned int m_auiAlphaSelectors[PIXELS];
+
+ private:
+
+ inline float DecodePixelAlpha(float a_fBase, float a_fMultiplier,
+ unsigned int a_uiTableIndex, unsigned int a_uiSelector)
+ {
+ float fPixelAlpha = a_fBase +
+ a_fMultiplier*s_aafModifierTable[a_uiTableIndex][a_uiSelector];
+ if (fPixelAlpha < 0.0f)
+ {
+ fPixelAlpha = 0.0f;
+ }
+ else if (fPixelAlpha > 1.0f)
+ {
+ fPixelAlpha = 1.0f;
+ }
+
+ return fPixelAlpha;
+ }
+
+ };
+
+ // ################################################################################
+ // Block4x4Encoding_RGBA8_Opaque
+ // RGBA8 if all pixels have alpha==1
+ // ################################################################################
+
+ class Block4x4Encoding_RGBA8_Opaque : public Block4x4Encoding_RGBA8
+ {
+ public:
+
+ virtual void PerformIteration(float a_fEffort);
+
+ virtual void SetEncodingBits(void);
+
+ };
+
+ // ################################################################################
+ // Block4x4Encoding_RGBA8_Transparent
+ // RGBA8 if all pixels have alpha==0
+ // ################################################################################
+
+ class Block4x4Encoding_RGBA8_Transparent : public Block4x4Encoding_RGBA8
+ {
+ public:
+
+ virtual void PerformIteration(float a_fEffort);
+
+ virtual void SetEncodingBits(void);
+
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcColor.h b/thirdparty/etc2comp/EtcColor.h
new file mode 100644
index 0000000000..7ceae05b65
--- /dev/null
+++ b/thirdparty/etc2comp/EtcColor.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math.h>
+
+namespace Etc
+{
+
+ inline float LogToLinear(float a_fLog)
+ {
+ static const float ALPHA = 0.055f;
+ static const float ONE_PLUS_ALPHA = 1.0f + ALPHA;
+
+ if (a_fLog <= 0.04045f)
+ {
+ return a_fLog / 12.92f;
+ }
+ else
+ {
+ return powf((a_fLog + ALPHA) / ONE_PLUS_ALPHA, 2.4f);
+ }
+ }
+
+ inline float LinearToLog(float &a_fLinear)
+ {
+ static const float ALPHA = 0.055f;
+ static const float ONE_PLUS_ALPHA = 1.0f + ALPHA;
+
+ if (a_fLinear <= 0.0031308f)
+ {
+ return 12.92f * a_fLinear;
+ }
+ else
+ {
+ return ONE_PLUS_ALPHA * powf(a_fLinear, (1.0f/2.4f)) - ALPHA;
+ }
+ }
+
+ class ColorR8G8B8A8
+ {
+ public:
+
+ unsigned char ucR;
+ unsigned char ucG;
+ unsigned char ucB;
+ unsigned char ucA;
+
+ };
+}
diff --git a/thirdparty/etc2comp/EtcColorFloatRGBA.h b/thirdparty/etc2comp/EtcColorFloatRGBA.h
new file mode 100644
index 0000000000..f2ca2c1f71
--- /dev/null
+++ b/thirdparty/etc2comp/EtcColorFloatRGBA.h
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcConfig.h"
+#include "EtcColor.h"
+
+#include <math.h>
+
+namespace Etc
+{
+
+ class ColorFloatRGBA
+ {
+ public:
+
+ ColorFloatRGBA(void)
+ {
+ fR = fG = fB = fA = 0.0f;
+ }
+
+ ColorFloatRGBA(float a_fR, float a_fG, float a_fB, float a_fA)
+ {
+ fR = a_fR;
+ fG = a_fG;
+ fB = a_fB;
+ fA = a_fA;
+ }
+
+ inline ColorFloatRGBA operator+(ColorFloatRGBA& a_rfrgba)
+ {
+ ColorFloatRGBA frgba;
+ frgba.fR = fR + a_rfrgba.fR;
+ frgba.fG = fG + a_rfrgba.fG;
+ frgba.fB = fB + a_rfrgba.fB;
+ frgba.fA = fA + a_rfrgba.fA;
+ return frgba;
+ }
+
+ inline ColorFloatRGBA operator+(float a_f)
+ {
+ ColorFloatRGBA frgba;
+ frgba.fR = fR + a_f;
+ frgba.fG = fG + a_f;
+ frgba.fB = fB + a_f;
+ frgba.fA = fA;
+ return frgba;
+ }
+
+ inline ColorFloatRGBA operator-(float a_f)
+ {
+ ColorFloatRGBA frgba;
+ frgba.fR = fR - a_f;
+ frgba.fG = fG - a_f;
+ frgba.fB = fB - a_f;
+ frgba.fA = fA;
+ return frgba;
+ }
+
+ inline ColorFloatRGBA operator-(ColorFloatRGBA& a_rfrgba)
+ {
+ ColorFloatRGBA frgba;
+ frgba.fR = fR - a_rfrgba.fR;
+ frgba.fG = fG - a_rfrgba.fG;
+ frgba.fB = fB - a_rfrgba.fB;
+ frgba.fA = fA - a_rfrgba.fA;
+ return frgba;
+ }
+
+ inline ColorFloatRGBA operator*(float a_f)
+ {
+ ColorFloatRGBA frgba;
+ frgba.fR = fR * a_f;
+ frgba.fG = fG * a_f;
+ frgba.fB = fB * a_f;
+ frgba.fA = fA;
+
+ return frgba;
+ }
+
+ inline ColorFloatRGBA ScaleRGB(float a_f)
+ {
+ ColorFloatRGBA frgba;
+ frgba.fR = a_f * fR;
+ frgba.fG = a_f * fG;
+ frgba.fB = a_f * fB;
+ frgba.fA = fA;
+
+ return frgba;
+ }
+
+ inline ColorFloatRGBA RoundRGB(void)
+ {
+ ColorFloatRGBA frgba;
+ frgba.fR = roundf(fR);
+ frgba.fG = roundf(fG);
+ frgba.fB = roundf(fB);
+
+ return frgba;
+ }
+
+ inline ColorFloatRGBA ToLinear()
+ {
+ ColorFloatRGBA frgbaLinear;
+ frgbaLinear.fR = LogToLinear(fR);
+ frgbaLinear.fG = LogToLinear(fG);
+ frgbaLinear.fB = LogToLinear(fB);
+ frgbaLinear.fA = fA;
+
+ return frgbaLinear;
+ }
+
+ inline ColorFloatRGBA ToLog(void)
+ {
+ ColorFloatRGBA frgbaLog;
+ frgbaLog.fR = LinearToLog(fR);
+ frgbaLog.fG = LinearToLog(fG);
+ frgbaLog.fB = LinearToLog(fB);
+ frgbaLog.fA = fA;
+
+ return frgbaLog;
+ }
+
+ inline static ColorFloatRGBA ConvertFromRGBA8(unsigned char a_ucR,
+ unsigned char a_ucG, unsigned char a_ucB, unsigned char a_ucA)
+ {
+ ColorFloatRGBA frgba;
+
+ frgba.fR = (float)a_ucR / 255.0f;
+ frgba.fG = (float)a_ucG / 255.0f;
+ frgba.fB = (float)a_ucB / 255.0f;
+ frgba.fA = (float)a_ucA / 255.0f;
+
+ return frgba;
+ }
+
+ inline static ColorFloatRGBA ConvertFromRGB4(unsigned char a_ucR4,
+ unsigned char a_ucG4,
+ unsigned char a_ucB4)
+ {
+ ColorFloatRGBA frgba;
+
+ unsigned char ucR8 = (unsigned char)((a_ucR4 << 4) + a_ucR4);
+ unsigned char ucG8 = (unsigned char)((a_ucG4 << 4) + a_ucG4);
+ unsigned char ucB8 = (unsigned char)((a_ucB4 << 4) + a_ucB4);
+
+ frgba.fR = (float)ucR8 / 255.0f;
+ frgba.fG = (float)ucG8 / 255.0f;
+ frgba.fB = (float)ucB8 / 255.0f;
+ frgba.fA = 1.0f;
+
+ return frgba;
+ }
+
+ inline static ColorFloatRGBA ConvertFromRGB5(unsigned char a_ucR5,
+ unsigned char a_ucG5,
+ unsigned char a_ucB5)
+ {
+ ColorFloatRGBA frgba;
+
+ unsigned char ucR8 = (unsigned char)((a_ucR5 << 3) + (a_ucR5 >> 2));
+ unsigned char ucG8 = (unsigned char)((a_ucG5 << 3) + (a_ucG5 >> 2));
+ unsigned char ucB8 = (unsigned char)((a_ucB5 << 3) + (a_ucB5 >> 2));
+
+ frgba.fR = (float)ucR8 / 255.0f;
+ frgba.fG = (float)ucG8 / 255.0f;
+ frgba.fB = (float)ucB8 / 255.0f;
+ frgba.fA = 1.0f;
+
+ return frgba;
+ }
+
+ inline static ColorFloatRGBA ConvertFromR6G7B6(unsigned char a_ucR6,
+ unsigned char a_ucG7,
+ unsigned char a_ucB6)
+ {
+ ColorFloatRGBA frgba;
+
+ unsigned char ucR8 = (unsigned char)((a_ucR6 << 2) + (a_ucR6 >> 4));
+ unsigned char ucG8 = (unsigned char)((a_ucG7 << 1) + (a_ucG7 >> 6));
+ unsigned char ucB8 = (unsigned char)((a_ucB6 << 2) + (a_ucB6 >> 4));
+
+ frgba.fR = (float)ucR8 / 255.0f;
+ frgba.fG = (float)ucG8 / 255.0f;
+ frgba.fB = (float)ucB8 / 255.0f;
+ frgba.fA = 1.0f;
+
+ return frgba;
+ }
+
+ // quantize to 4 bits, expand to 8 bits
+ inline ColorFloatRGBA QuantizeR4G4B4(void) const
+ {
+ ColorFloatRGBA frgba = *this;
+
+ // quantize to 4 bits
+ frgba = frgba.ClampRGB().ScaleRGB(15.0f).RoundRGB();
+ unsigned int uiR4 = (unsigned int)frgba.fR;
+ unsigned int uiG4 = (unsigned int)frgba.fG;
+ unsigned int uiB4 = (unsigned int)frgba.fB;
+
+ // expand to 8 bits
+ frgba.fR = (float) ((uiR4 << 4) + uiR4);
+ frgba.fG = (float) ((uiG4 << 4) + uiG4);
+ frgba.fB = (float) ((uiB4 << 4) + uiB4);
+
+ frgba = frgba.ScaleRGB(1.0f/255.0f);
+
+ return frgba;
+ }
+
+ // quantize to 5 bits, expand to 8 bits
+ inline ColorFloatRGBA QuantizeR5G5B5(void) const
+ {
+ ColorFloatRGBA frgba = *this;
+
+ // quantize to 5 bits
+ frgba = frgba.ClampRGB().ScaleRGB(31.0f).RoundRGB();
+ unsigned int uiR5 = (unsigned int)frgba.fR;
+ unsigned int uiG5 = (unsigned int)frgba.fG;
+ unsigned int uiB5 = (unsigned int)frgba.fB;
+
+ // expand to 8 bits
+ frgba.fR = (float)((uiR5 << 3) + (uiR5 >> 2));
+ frgba.fG = (float)((uiG5 << 3) + (uiG5 >> 2));
+ frgba.fB = (float)((uiB5 << 3) + (uiB5 >> 2));
+
+ frgba = frgba.ScaleRGB(1.0f / 255.0f);
+
+ return frgba;
+ }
+
+ // quantize to 6/7/6 bits, expand to 8 bits
+ inline ColorFloatRGBA QuantizeR6G7B6(void) const
+ {
+ ColorFloatRGBA frgba = *this;
+
+ // quantize to 6/7/6 bits
+ ColorFloatRGBA frgba6 = frgba.ClampRGB().ScaleRGB(63.0f).RoundRGB();
+ ColorFloatRGBA frgba7 = frgba.ClampRGB().ScaleRGB(127.0f).RoundRGB();
+ unsigned int uiR6 = (unsigned int)frgba6.fR;
+ unsigned int uiG7 = (unsigned int)frgba7.fG;
+ unsigned int uiB6 = (unsigned int)frgba6.fB;
+
+ // expand to 8 bits
+ frgba.fR = (float)((uiR6 << 2) + (uiR6 >> 4));
+ frgba.fG = (float)((uiG7 << 1) + (uiG7 >> 6));
+ frgba.fB = (float)((uiB6 << 2) + (uiB6 >> 4));
+
+ frgba = frgba.ScaleRGB(1.0f / 255.0f);
+
+ return frgba;
+ }
+
+ inline ColorFloatRGBA ClampRGB(void)
+ {
+ ColorFloatRGBA frgba = *this;
+ if (frgba.fR < 0.0f) { frgba.fR = 0.0f; }
+ if (frgba.fR > 1.0f) { frgba.fR = 1.0f; }
+ if (frgba.fG < 0.0f) { frgba.fG = 0.0f; }
+ if (frgba.fG > 1.0f) { frgba.fG = 1.0f; }
+ if (frgba.fB < 0.0f) { frgba.fB = 0.0f; }
+ if (frgba.fB > 1.0f) { frgba.fB = 1.0f; }
+
+ return frgba;
+ }
+
+ inline ColorFloatRGBA ClampRGBA(void)
+ {
+ ColorFloatRGBA frgba = *this;
+ if (frgba.fR < 0.0f) { frgba.fR = 0.0f; }
+ if (frgba.fR > 1.0f) { frgba.fR = 1.0f; }
+ if (frgba.fG < 0.0f) { frgba.fG = 0.0f; }
+ if (frgba.fG > 1.0f) { frgba.fG = 1.0f; }
+ if (frgba.fB < 0.0f) { frgba.fB = 0.0f; }
+ if (frgba.fB > 1.0f) { frgba.fB = 1.0f; }
+ if (frgba.fA < 0.0f) { frgba.fA = 0.0f; }
+ if (frgba.fA > 1.0f) { frgba.fA = 1.0f; }
+
+ return frgba;
+ }
+
+ inline int IntRed(float a_fScale)
+ {
+ return (int)roundf(fR * a_fScale);
+ }
+
+ inline int IntGreen(float a_fScale)
+ {
+ return (int)roundf(fG * a_fScale);
+ }
+
+ inline int IntBlue(float a_fScale)
+ {
+ return (int)roundf(fB * a_fScale);
+ }
+
+ inline int IntAlpha(float a_fScale)
+ {
+ return (int)roundf(fA * a_fScale);
+ }
+
+ float fR, fG, fB, fA;
+ };
+
+}
+
diff --git a/thirdparty/etc2comp/EtcConfig.h b/thirdparty/etc2comp/EtcConfig.h
new file mode 100644
index 0000000000..3bfe1d99a8
--- /dev/null
+++ b/thirdparty/etc2comp/EtcConfig.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#ifdef _WIN32
+#define ETC_WINDOWS (1)
+#else
+#define ETC_WINDOWS (0)
+#endif
+
+#if __APPLE__
+#define ETC_OSX (1)
+#else
+#define ETC_OSX (0)
+#endif
+
+#if __unix__
+#define ETC_UNIX (1)
+#else
+#define ETC_UNIX (0)
+#endif
+
+
+// short names for common types
+#include <stdint.h>
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef float f32;
+typedef double f64;
+
+// Keep asserts enabled in release builds during development
+#undef NDEBUG
+
+// 0=disable. stb_image can be used if you need to compress
+//other image formats like jpg
+#define USE_STB_IMAGE_LOAD 0
+
+#if ETC_WINDOWS
+#include <sdkddkver.h>
+#define _CRT_SECURE_NO_WARNINGS (1)
+#include <tchar.h>
+#endif
+
+#include <stdio.h>
+
diff --git a/thirdparty/etc2comp/EtcDifferentialTrys.cpp b/thirdparty/etc2comp/EtcDifferentialTrys.cpp
new file mode 100644
index 0000000000..ef4cd103d9
--- /dev/null
+++ b/thirdparty/etc2comp/EtcDifferentialTrys.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+/*
+EtcDifferentialTrys.cpp
+
+Gathers the results of the various encoding trys for both halves of a 4x4 block for Differential mode
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcDifferentialTrys.h"
+
+#include <assert.h>
+
+namespace Etc
+{
+
+ // ----------------------------------------------------------------------------------------------------
+ // construct a list of trys (encoding attempts)
+ //
+ // a_frgbaColor1 is the basecolor for the first half
+ // a_frgbaColor2 is the basecolor for the second half
+ // a_pauiPixelMapping1 is the pixel order for the first half
+ // a_pauiPixelMapping2 is the pixel order for the second half
+ // a_uiRadius is the amount to vary the base colors
+ //
+ DifferentialTrys::DifferentialTrys(ColorFloatRGBA a_frgbaColor1, ColorFloatRGBA a_frgbaColor2,
+ const unsigned int *a_pauiPixelMapping1,
+ const unsigned int *a_pauiPixelMapping2,
+ unsigned int a_uiRadius,
+ int a_iGrayOffset1, int a_iGrayOffset2)
+ {
+ assert(a_uiRadius <= MAX_RADIUS);
+
+ m_boolSeverelyBentColors = false;
+
+ ColorFloatRGBA frgbaQuantizedColor1 = a_frgbaColor1.QuantizeR5G5B5();
+ ColorFloatRGBA frgbaQuantizedColor2 = a_frgbaColor2.QuantizeR5G5B5();
+
+ // quantize base colors
+ // ensure that trys with a_uiRadius don't overflow
+ int iRed1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntRed(31.0f)+a_iGrayOffset1, a_uiRadius);
+ int iGreen1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntGreen(31.0f) + a_iGrayOffset1, a_uiRadius);
+ int iBlue1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntBlue(31.0f) + a_iGrayOffset1, a_uiRadius);
+ int iRed2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntRed(31.0f) + a_iGrayOffset2, a_uiRadius);
+ int iGreen2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntGreen(31.0f) + a_iGrayOffset2, a_uiRadius);
+ int iBlue2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntBlue(31.0f) + a_iGrayOffset2, a_uiRadius);
+
+ int iDeltaRed = iRed2 - iRed1;
+ int iDeltaGreen = iGreen2 - iGreen1;
+ int iDeltaBlue = iBlue2 - iBlue1;
+
+ // make sure components are within range
+ {
+ if (iDeltaRed > 3)
+ {
+ if (iDeltaRed > 7)
+ {
+ m_boolSeverelyBentColors = true;
+ }
+
+ iRed1 += (iDeltaRed - 3) / 2;
+ iRed2 = iRed1 + 3;
+ iDeltaRed = 3;
+ }
+ else if (iDeltaRed < -4)
+ {
+ if (iDeltaRed < -8)
+ {
+ m_boolSeverelyBentColors = true;
+ }
+
+ iRed1 += (iDeltaRed + 4) / 2;
+ iRed2 = iRed1 - 4;
+ iDeltaRed = -4;
+ }
+ assert(iRed1 >= (signed)(0 + a_uiRadius) && iRed1 <= (signed)(31 - a_uiRadius));
+ assert(iRed2 >= (signed)(0 + a_uiRadius) && iRed2 <= (signed)(31 - a_uiRadius));
+ assert(iDeltaRed >= -4 && iDeltaRed <= 3);
+
+ if (iDeltaGreen > 3)
+ {
+ if (iDeltaGreen > 7)
+ {
+ m_boolSeverelyBentColors = true;
+ }
+
+ iGreen1 += (iDeltaGreen - 3) / 2;
+ iGreen2 = iGreen1 + 3;
+ iDeltaGreen = 3;
+ }
+ else if (iDeltaGreen < -4)
+ {
+ if (iDeltaGreen < -8)
+ {
+ m_boolSeverelyBentColors = true;
+ }
+
+ iGreen1 += (iDeltaGreen + 4) / 2;
+ iGreen2 = iGreen1 - 4;
+ iDeltaGreen = -4;
+ }
+ assert(iGreen1 >= (signed)(0 + a_uiRadius) && iGreen1 <= (signed)(31 - a_uiRadius));
+ assert(iGreen2 >= (signed)(0 + a_uiRadius) && iGreen2 <= (signed)(31 - a_uiRadius));
+ assert(iDeltaGreen >= -4 && iDeltaGreen <= 3);
+
+ if (iDeltaBlue > 3)
+ {
+ if (iDeltaBlue > 7)
+ {
+ m_boolSeverelyBentColors = true;
+ }
+
+ iBlue1 += (iDeltaBlue - 3) / 2;
+ iBlue2 = iBlue1 + 3;
+ iDeltaBlue = 3;
+ }
+ else if (iDeltaBlue < -4)
+ {
+ if (iDeltaBlue < -8)
+ {
+ m_boolSeverelyBentColors = true;
+ }
+
+ iBlue1 += (iDeltaBlue + 4) / 2;
+ iBlue2 = iBlue1 - 4;
+ iDeltaBlue = -4;
+ }
+ assert(iBlue1 >= (signed)(0+a_uiRadius) && iBlue1 <= (signed)(31 - a_uiRadius));
+ assert(iBlue2 >= (signed)(0 + a_uiRadius) && iBlue2 <= (signed)(31 - a_uiRadius));
+ assert(iDeltaBlue >= -4 && iDeltaBlue <= 3);
+ }
+
+ m_half1.Init(iRed1, iGreen1, iBlue1, a_pauiPixelMapping1, a_uiRadius);
+ m_half2.Init(iRed2, iGreen2, iBlue2, a_pauiPixelMapping2, a_uiRadius);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ void DifferentialTrys::Half::Init(int a_iRed, int a_iGreen, int a_iBlue,
+ const unsigned int *a_pauiPixelMapping, unsigned int a_uiRadius)
+ {
+
+ m_iRed = a_iRed;
+ m_iGreen = a_iGreen;
+ m_iBlue = a_iBlue;
+
+ m_pauiPixelMapping = a_pauiPixelMapping;
+ m_uiRadius = a_uiRadius;
+
+ m_uiTrys = 0;
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcDifferentialTrys.h b/thirdparty/etc2comp/EtcDifferentialTrys.h
new file mode 100644
index 0000000000..71860908ff
--- /dev/null
+++ b/thirdparty/etc2comp/EtcDifferentialTrys.h
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColorFloatRGBA.h"
+
+namespace Etc
+{
+
+ class DifferentialTrys
+ {
+ public:
+
+ static const unsigned int MAX_RADIUS = 2;
+
+ DifferentialTrys(ColorFloatRGBA a_frgbaColor1,
+ ColorFloatRGBA a_frgbaColor2,
+ const unsigned int *a_pauiPixelMapping1,
+ const unsigned int *a_pauiPixelMapping2,
+ unsigned int a_uiRadius,
+ int a_iGrayOffset1, int a_iGrayOffset2);
+
+ inline static int MoveAwayFromEdge(int a_i, int a_iDistance)
+ {
+ if (a_i < (0+ a_iDistance))
+ {
+ return (0 + a_iDistance);
+ }
+ else if (a_i > (31- a_iDistance))
+ {
+ return (31 - a_iDistance);
+ }
+
+ return a_i;
+ }
+
+ class Try
+ {
+ public :
+ static const unsigned int SELECTORS = 8; // per half
+
+ int m_iRed;
+ int m_iGreen;
+ int m_iBlue;
+ unsigned int m_uiCW;
+ unsigned int m_auiSelectors[SELECTORS];
+ float m_fError;
+ };
+
+ class Half
+ {
+ public:
+
+ static const unsigned int MAX_TRYS = 125;
+
+ void Init(int a_iRed, int a_iGreen, int a_iBlue,
+ const unsigned int *a_pauiPixelMapping,
+ unsigned int a_uiRadius);
+
+ // center of trys
+ int m_iRed;
+ int m_iGreen;
+ int m_iBlue;
+
+ const unsigned int *m_pauiPixelMapping;
+ unsigned int m_uiRadius;
+
+ unsigned int m_uiTrys;
+ Try m_atry[MAX_TRYS];
+
+ Try *m_ptryBest;
+ };
+
+ Half m_half1;
+ Half m_half2;
+
+ bool m_boolSeverelyBentColors;
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcErrorMetric.h b/thirdparty/etc2comp/EtcErrorMetric.h
new file mode 100644
index 0000000000..df4dcab4fb
--- /dev/null
+++ b/thirdparty/etc2comp/EtcErrorMetric.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace Etc
+{
+
+ enum ErrorMetric
+ {
+ RGBA,
+ RGBX,
+ REC709,
+ NUMERIC,
+ NORMALXYZ,
+ //
+ ERROR_METRICS,
+ //
+ BT709 = REC709
+ };
+
+ inline const char *ErrorMetricToString(ErrorMetric errorMetric)
+ {
+ switch (errorMetric)
+ {
+ case RGBA:
+ return "RGBA";
+ case RGBX:
+ return "RGBX";
+ case REC709:
+ return "REC709";
+ case NUMERIC:
+ return "NUMERIC";
+ case NORMALXYZ:
+ return "NORMALXYZ";
+ case ERROR_METRICS:
+ default:
+ return "UNKNOWN";
+ }
+ }
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcFile.cpp b/thirdparty/etc2comp/EtcFile.cpp
new file mode 100644
index 0000000000..831a3aac45
--- /dev/null
+++ b/thirdparty/etc2comp/EtcFile.cpp
@@ -0,0 +1,390 @@
+/*
+ * 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.
+ */
+
+#ifdef _WIN32
+#define _CRT_SECURE_NO_WARNINGS (1)
+#endif
+
+#include "EtcConfig.h"
+
+
+#include "EtcFile.h"
+
+#include "EtcFileHeader.h"
+#include "EtcColor.h"
+#include "Etc.h"
+#include "EtcBlock4x4EncodingBits.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+using namespace Etc;
+
+// ----------------------------------------------------------------------------------------------------
+//
+File::File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat,
+ unsigned char *a_paucEncodingBits, unsigned int a_uiEncodingBitsBytes,
+ unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight,
+ unsigned int a_uiExtendedWidth, unsigned int a_uiExtendedHeight)
+{
+ if (a_pstrFilename == nullptr)
+ {
+ m_pstrFilename = const_cast<char *>("");
+ }
+ else
+ {
+ m_pstrFilename = new char[strlen(a_pstrFilename) + 1];
+ strcpy(m_pstrFilename, a_pstrFilename);
+ }
+
+ m_fileformat = a_fileformat;
+ if (m_fileformat == Format::INFER_FROM_FILE_EXTENSION)
+ {
+ // ***** TODO: add this later *****
+ m_fileformat = Format::KTX;
+ }
+
+ m_imageformat = a_imageformat;
+
+ m_uiNumMipmaps = 1;
+ m_pMipmapImages = new RawImage[m_uiNumMipmaps];
+ m_pMipmapImages[0].paucEncodingBits = std::shared_ptr<unsigned char>(a_paucEncodingBits, [](unsigned char *p) { delete[] p; } );
+ m_pMipmapImages[0].uiEncodingBitsBytes = a_uiEncodingBitsBytes;
+ m_pMipmapImages[0].uiExtendedWidth = a_uiExtendedWidth;
+ m_pMipmapImages[0].uiExtendedHeight = a_uiExtendedHeight;
+
+ m_uiSourceWidth = a_uiSourceWidth;
+ m_uiSourceHeight = a_uiSourceHeight;
+
+ switch (m_fileformat)
+ {
+ case Format::PKM:
+ m_pheader = new FileHeader_Pkm(this);
+ break;
+
+ case Format::KTX:
+ m_pheader = new FileHeader_Ktx(this);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+}
+
+// ----------------------------------------------------------------------------------------------------
+//
+File::File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat,
+ unsigned int a_uiNumMipmaps, RawImage *a_pMipmapImages,
+ unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight)
+{
+ if (a_pstrFilename == nullptr)
+ {
+ m_pstrFilename = const_cast<char *>("");
+ }
+ else
+ {
+ m_pstrFilename = new char[strlen(a_pstrFilename) + 1];
+ strcpy(m_pstrFilename, a_pstrFilename);
+ }
+
+ m_fileformat = a_fileformat;
+ if (m_fileformat == Format::INFER_FROM_FILE_EXTENSION)
+ {
+ // ***** TODO: add this later *****
+ m_fileformat = Format::KTX;
+ }
+
+ m_imageformat = a_imageformat;
+
+ m_uiNumMipmaps = a_uiNumMipmaps;
+ m_pMipmapImages = new RawImage[m_uiNumMipmaps];
+
+ for(unsigned int mip = 0; mip < m_uiNumMipmaps; mip++)
+ {
+ m_pMipmapImages[mip] = a_pMipmapImages[mip];
+ }
+
+ m_uiSourceWidth = a_uiSourceWidth;
+ m_uiSourceHeight = a_uiSourceHeight;
+
+ switch (m_fileformat)
+ {
+ case Format::PKM:
+ m_pheader = new FileHeader_Pkm(this);
+ break;
+
+ case Format::KTX:
+ m_pheader = new FileHeader_Ktx(this);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+}
+
+// ----------------------------------------------------------------------------------------------------
+//
+File::File(const char *a_pstrFilename, Format a_fileformat)
+{
+ if (a_pstrFilename == nullptr)
+ {
+ return;
+ }
+ else
+ {
+ m_pstrFilename = new char[strlen(a_pstrFilename) + 1];
+ strcpy(m_pstrFilename, a_pstrFilename);
+ }
+
+ m_fileformat = a_fileformat;
+ if (m_fileformat == Format::INFER_FROM_FILE_EXTENSION)
+ {
+ // ***** TODO: add this later *****
+ m_fileformat = Format::KTX;
+ }
+
+ FILE *pfile = fopen(m_pstrFilename, "rb");
+ if (pfile == nullptr)
+ {
+ printf("ERROR: Couldn't open %s", m_pstrFilename);
+ exit(1);
+ }
+ fseek(pfile, 0, SEEK_END);
+ unsigned int fileSize = ftell(pfile);
+ fseek(pfile, 0, SEEK_SET);
+ size_t szResult;
+
+ m_pheader = new FileHeader_Ktx(this);
+ szResult = fread( ((FileHeader_Ktx*)m_pheader)->GetData(), 1, sizeof(FileHeader_Ktx::Data), pfile);
+ assert(szResult > 0);
+
+ m_uiNumMipmaps = 1;
+ m_pMipmapImages = new RawImage[m_uiNumMipmaps];
+
+ if (((FileHeader_Ktx*)m_pheader)->GetData()->m_u32BytesOfKeyValueData > 0)
+ fseek(pfile, ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32BytesOfKeyValueData, SEEK_CUR);
+ szResult = fread(&m_pMipmapImages->uiEncodingBitsBytes, 1, sizeof(unsigned int), pfile);
+ assert(szResult > 0);
+
+ m_pMipmapImages->paucEncodingBits = std::shared_ptr<unsigned char>(new unsigned char[m_pMipmapImages->uiEncodingBitsBytes], [](unsigned char *p) { delete[] p; } );
+ assert(ftell(pfile) + m_pMipmapImages->uiEncodingBitsBytes <= fileSize);
+ szResult = fread(m_pMipmapImages->paucEncodingBits.get(), 1, m_pMipmapImages->uiEncodingBitsBytes, pfile);
+ assert(szResult == m_pMipmapImages->uiEncodingBitsBytes);
+
+ uint32_t uiInternalFormat = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32GlInternalFormat;
+ uint32_t uiBaseInternalFormat = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32GlBaseInternalFormat;
+
+ if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC1_RGB8 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC1_RGB8)
+ {
+ m_imageformat = Image::Format::ETC1;
+ }
+ else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RGB8 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RGB8)
+ {
+ m_imageformat = Image::Format::RGB8;
+ }
+ else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RGB8A1 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RGB8A1)
+ {
+ m_imageformat = Image::Format::RGB8A1;
+ }
+ else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RGBA8 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RGBA8)
+ {
+ m_imageformat = Image::Format::RGBA8;
+ }
+ else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_R11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_R11)
+ {
+ m_imageformat = Image::Format::R11;
+ }
+ else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_SIGNED_R11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_R11)
+ {
+ m_imageformat = Image::Format::SIGNED_R11;
+ }
+ else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RG11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RG11)
+ {
+ m_imageformat = Image::Format::RG11;
+ }
+ else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_SIGNED_RG11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RG11)
+ {
+ m_imageformat = Image::Format::SIGNED_RG11;
+ }
+ else
+ {
+ m_imageformat = Image::Format::UNKNOWN;
+ }
+
+ m_uiSourceWidth = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32PixelWidth;
+ m_uiSourceHeight = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32PixelHeight;
+ m_pMipmapImages->uiExtendedWidth = Image::CalcExtendedDimension((unsigned short)m_uiSourceWidth);
+ m_pMipmapImages->uiExtendedHeight = Image::CalcExtendedDimension((unsigned short)m_uiSourceHeight);
+
+ unsigned int uiBlocks = m_pMipmapImages->uiExtendedWidth * m_pMipmapImages->uiExtendedHeight / 16;
+ Block4x4EncodingBits::Format encodingbitsformat = Image::DetermineEncodingBitsFormat(m_imageformat);
+ unsigned int expectedbytes = uiBlocks * Block4x4EncodingBits::GetBytesPerBlock(encodingbitsformat);
+ assert(expectedbytes == m_pMipmapImages->uiEncodingBitsBytes);
+
+ fclose(pfile);
+}
+
+File::~File()
+{
+ if (m_pMipmapImages != nullptr)
+ {
+ delete [] m_pMipmapImages;
+ }
+
+ if(m_pstrFilename != nullptr)
+ {
+ delete[] m_pstrFilename;
+ m_pstrFilename = nullptr;
+ }
+ if (m_pheader != nullptr)
+ {
+ delete m_pheader;
+ m_pheader = nullptr;
+ }
+}
+
+void File::UseSingleBlock(int a_iPixelX, int a_iPixelY)
+{
+ if (a_iPixelX <= -1 || a_iPixelY <= -1)
+ return;
+ if (a_iPixelX >(int) m_uiSourceWidth)
+ {
+ //if we are using a ktx thats the size of a single block or less
+ //then make sure we use the 4x4 image as the single block
+ if (m_uiSourceWidth <= 4)
+ {
+ a_iPixelX = 0;
+ }
+ else
+ {
+ printf("blockAtHV: H coordinate out of range, capped to image width\n");
+ a_iPixelX = m_uiSourceWidth - 1;
+ }
+ }
+ if (a_iPixelY >(int) m_uiSourceHeight)
+ {
+ //if we are using a ktx thats the size of a single block or less
+ //then make sure we use the 4x4 image as the single block
+ if (m_uiSourceHeight <= 4)
+ {
+ a_iPixelY= 0;
+ }
+ else
+ {
+ printf("blockAtHV: V coordinate out of range, capped to image height\n");
+ a_iPixelY = m_uiSourceHeight - 1;
+ }
+ }
+
+ unsigned int origWidth = m_uiSourceWidth;
+ unsigned int origHeight = m_uiSourceHeight;
+
+ m_uiSourceWidth = 4;
+ m_uiSourceHeight = 4;
+
+ Block4x4EncodingBits::Format encodingbitsformat = Image::DetermineEncodingBitsFormat(m_imageformat);
+ unsigned int uiEncodingBitsBytesPerBlock = Block4x4EncodingBits::GetBytesPerBlock(encodingbitsformat);
+
+ int numMipmaps = 1;
+ RawImage* pMipmapImages = new RawImage[numMipmaps];
+ pMipmapImages[0].uiExtendedWidth = Image::CalcExtendedDimension((unsigned short)m_uiSourceWidth);
+ pMipmapImages[0].uiExtendedHeight = Image::CalcExtendedDimension((unsigned short)m_uiSourceHeight);
+ pMipmapImages[0].uiEncodingBitsBytes = 0;
+ pMipmapImages[0].paucEncodingBits = std::shared_ptr<unsigned char>(new unsigned char[uiEncodingBitsBytesPerBlock], [](unsigned char *p) { delete[] p; });
+
+ //block position in pixels
+ // remove the bottom 2 bits to get the block coordinates
+ unsigned int iBlockPosX = (a_iPixelX & 0xFFFFFFFC);
+ unsigned int iBlockPosY = (a_iPixelY & 0xFFFFFFFC);
+
+ int numXBlocks = (origWidth / 4);
+ int numYBlocks = (origHeight / 4);
+
+
+ // block location
+ //int iBlockX = (a_iPixelX % 4) == 0 ? a_iPixelX / 4.0f : (a_iPixelX / 4) + 1;
+ //int iBlockY = (a_iPixelY % 4) == 0 ? a_iPixelY / 4.0f : (a_iPixelY / 4) + 1;
+ //m_paucEncodingBits += ((iBlockY * numXBlocks) + iBlockX) * uiEncodingBitsBytesPerBlock;
+
+
+ unsigned int num = numXBlocks*numYBlocks;
+ unsigned int uiH = 0, uiV = 0;
+ unsigned char* pEncodingBits = m_pMipmapImages[0].paucEncodingBits.get();
+ for (unsigned int uiBlock = 0; uiBlock < num; uiBlock++)
+ {
+ if (uiH == iBlockPosX && uiV == iBlockPosY)
+ {
+ memcpy(pMipmapImages[0].paucEncodingBits.get(),pEncodingBits, uiEncodingBitsBytesPerBlock);
+ break;
+ }
+ pEncodingBits += uiEncodingBitsBytesPerBlock;
+ uiH += 4;
+
+ if (uiH >= origWidth)
+ {
+ uiH = 0;
+ uiV += 4;
+ }
+ }
+
+ delete [] m_pMipmapImages;
+ m_pMipmapImages = pMipmapImages;
+}
+// ----------------------------------------------------------------------------------------------------
+//
+void File::Write()
+{
+
+ FILE *pfile = fopen(m_pstrFilename, "wb");
+ if (pfile == nullptr)
+ {
+ printf("Error: couldn't open Etc file (%s)\n", m_pstrFilename);
+ exit(1);
+ }
+
+ m_pheader->Write(pfile);
+
+ for(unsigned int mip = 0; mip < m_uiNumMipmaps; mip++)
+ {
+ if(m_fileformat == Format::KTX)
+ {
+ // Write u32 image size
+ uint32_t u32ImageSize = m_pMipmapImages[mip].uiEncodingBitsBytes;
+ uint32_t szBytesWritten = fwrite(&u32ImageSize, 1, sizeof(u32ImageSize), pfile);
+ assert(szBytesWritten == sizeof(u32ImageSize));
+ }
+
+ unsigned int iResult = (int)fwrite(m_pMipmapImages[mip].paucEncodingBits.get(), 1, m_pMipmapImages[mip].uiEncodingBitsBytes, pfile);
+ if (iResult != m_pMipmapImages[mip].uiEncodingBitsBytes)
+ {
+ printf("Error: couldn't write Etc file (%s)\n", m_pstrFilename);
+ exit(1);
+ }
+ }
+
+ fclose(pfile);
+
+}
+
+// ----------------------------------------------------------------------------------------------------
+//
+
diff --git a/thirdparty/etc2comp/EtcFile.h b/thirdparty/etc2comp/EtcFile.h
new file mode 100644
index 0000000000..69bf3b2d3a
--- /dev/null
+++ b/thirdparty/etc2comp/EtcFile.h
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColorFloatRGBA.h"
+#include "EtcImage.h"
+#include "Etc.h"
+
+namespace Etc
+{
+ class FileHeader;
+ class SourceImage;
+
+ class File
+ {
+ public:
+
+ enum class Format
+ {
+ INFER_FROM_FILE_EXTENSION,
+ PKM,
+ KTX,
+ };
+
+ File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat,
+ unsigned char *a_paucEncodingBits, unsigned int a_uiEncodingBitsBytes,
+ unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight,
+ unsigned int a_uiExtendedWidth, unsigned int a_uiExtendedHeight);
+
+ File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat,
+ unsigned int a_uiNumMipmaps, RawImage *pMipmapImages,
+ unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight );
+
+ File(const char *a_pstrFilename, Format a_fileformat);
+ ~File();
+ const char *GetFilename(void) { return m_pstrFilename; }
+
+ void Read(const char *a_pstrFilename);
+ void Write(void);
+
+ inline unsigned int GetSourceWidth(void)
+ {
+ return m_uiSourceWidth;
+ }
+
+ inline unsigned int GetSourceHeight(void)
+ {
+ return m_uiSourceHeight;
+ }
+
+ inline unsigned int GetExtendedWidth(unsigned int mipmapIndex = 0)
+ {
+ if (mipmapIndex < m_uiNumMipmaps)
+ {
+ return m_pMipmapImages[mipmapIndex].uiExtendedWidth;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ inline unsigned int GetExtendedHeight(unsigned int mipmapIndex = 0)
+ {
+ if (mipmapIndex < m_uiNumMipmaps)
+ {
+ return m_pMipmapImages[mipmapIndex].uiExtendedHeight;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ inline Image::Format GetImageFormat()
+ {
+ return m_imageformat;
+ }
+
+ inline unsigned int GetEncodingBitsBytes(unsigned int mipmapIndex = 0)
+ {
+ if (mipmapIndex < m_uiNumMipmaps)
+ {
+ return m_pMipmapImages[mipmapIndex].uiEncodingBitsBytes;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ inline unsigned char* GetEncodingBits(unsigned int mipmapIndex = 0)
+ {
+ if( mipmapIndex < m_uiNumMipmaps)
+ {
+ return m_pMipmapImages[mipmapIndex].paucEncodingBits.get();
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
+
+ inline unsigned int GetNumMipmaps()
+ {
+ return m_uiNumMipmaps;
+ }
+
+ void UseSingleBlock(int a_iPixelX = -1, int a_iPixelY = -1);
+ private:
+
+ char *m_pstrFilename; // includes directory path and file extension
+ Format m_fileformat;
+ Image::Format m_imageformat;
+ FileHeader *m_pheader;
+ unsigned int m_uiNumMipmaps;
+ RawImage* m_pMipmapImages;
+ unsigned int m_uiSourceWidth;
+ unsigned int m_uiSourceHeight;
+ };
+
+}
diff --git a/thirdparty/etc2comp/EtcFileHeader.cpp b/thirdparty/etc2comp/EtcFileHeader.cpp
new file mode 100644
index 0000000000..f02fcab011
--- /dev/null
+++ b/thirdparty/etc2comp/EtcFileHeader.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#include "EtcFileHeader.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+
+#include <assert.h>
+
+namespace Etc
+{
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ FileHeader_Pkm::FileHeader_Pkm(File *a_pfile)
+ {
+ m_pfile = a_pfile;
+
+ static const char s_acMagicNumberData[4] = { 'P', 'K', 'M', ' ' };
+ static const char s_acVersionData[2] = { '1', '0' };
+
+ for (unsigned int ui = 0; ui < sizeof(s_acMagicNumberData); ui++)
+ {
+ m_data.m_acMagicNumber[ui] = s_acMagicNumberData[ui];
+ }
+
+ for (unsigned int ui = 0; ui < sizeof(s_acVersionData); ui++)
+ {
+ m_data.m_acVersion[ui] = s_acVersionData[ui];
+ }
+
+ m_data.m_ucDataType_msb = 0; // ETC1_RGB_NO_MIPMAPS
+ m_data.m_ucDataType_lsb = 0;
+
+ m_data.m_ucOriginalWidth_msb = (unsigned char)(m_pfile->GetSourceWidth() >> 8);
+ m_data.m_ucOriginalWidth_lsb = m_pfile->GetSourceWidth() & 0xFF;
+ m_data.m_ucOriginalHeight_msb = (unsigned char)(m_pfile->GetSourceHeight() >> 8);
+ m_data.m_ucOriginalHeight_lsb = m_pfile->GetSourceHeight() & 0xFF;
+
+ m_data.m_ucExtendedWidth_msb = (unsigned char)(m_pfile->GetExtendedWidth() >> 8);
+ m_data.m_ucExtendedWidth_lsb = m_pfile->GetExtendedWidth() & 0xFF;
+ m_data.m_ucExtendedHeight_msb = (unsigned char)(m_pfile->GetExtendedHeight() >> 8);
+ m_data.m_ucExtendedHeight_lsb = m_pfile->GetExtendedHeight() & 0xFF;
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ void FileHeader_Pkm::Write(FILE *a_pfile)
+ {
+
+ fwrite(&m_data, sizeof(Data), 1, a_pfile);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ FileHeader_Ktx::FileHeader_Ktx(File *a_pfile)
+ {
+ m_pfile = a_pfile;
+
+ static const uint8_t s_au8Itentfier[12] =
+ {
+ 0xAB, 0x4B, 0x54, 0x58, // first four bytes of Byte[12] identifier
+ 0x20, 0x31, 0x31, 0xBB, // next four bytes of Byte[12] identifier
+ 0x0D, 0x0A, 0x1A, 0x0A // final four bytes of Byte[12] identifier
+ };
+
+ for (unsigned int ui = 0; ui < sizeof(s_au8Itentfier); ui++)
+ {
+ m_data.m_au8Identifier[ui] = s_au8Itentfier[ui];
+ }
+
+ m_data.m_u32Endianness = 0x04030201;
+ m_data.m_u32GlType = 0;
+ m_data.m_u32GlTypeSize = 1;
+ m_data.m_u32GlFormat = 0;
+
+ switch (m_pfile->GetImageFormat())
+ {
+ case Image::Format::RGB8:
+ case Image::Format::SRGB8:
+ m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RGB8;
+ m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RGB8;
+ break;
+
+ case Image::Format::RGBA8:
+ case Image::Format::SRGBA8:
+ m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RGBA8;
+ m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RGBA8;
+ break;
+
+ case Image::Format::RGB8A1:
+ case Image::Format::SRGB8A1:
+ m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RGB8A1;
+ m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RGB8A1;
+ break;
+
+ case Image::Format::R11:
+ m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_R11;
+ m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_R11;
+ break;
+
+ case Image::Format::SIGNED_R11:
+ m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_SIGNED_R11;
+ m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_R11;
+ break;
+
+ case Image::Format::RG11:
+ m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RG11;
+ m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RG11;
+ break;
+
+ case Image::Format::SIGNED_RG11:
+ m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_SIGNED_RG11;
+ m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RG11;
+ break;
+
+ default:
+ m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC1_RGB8;
+ m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC1_RGB8;
+ break;
+ }
+
+ m_data.m_u32PixelWidth = 0;
+ m_data.m_u32PixelHeight = 0;
+ m_data.m_u32PixelDepth = 0;
+ m_data.m_u32NumberOfArrayElements = 0;
+ m_data.m_u32NumberOfFaces = 0;
+ m_data.m_u32BytesOfKeyValueData = 0;
+
+ m_pkeyvaluepair = nullptr;
+
+ m_u32Images = 0;
+ m_u32KeyValuePairs = 0;
+
+ m_data.m_u32PixelWidth = m_pfile->GetSourceWidth();
+ m_data.m_u32PixelHeight = m_pfile->GetSourceHeight();
+ m_data.m_u32PixelDepth = 0;
+ m_data.m_u32NumberOfArrayElements = 0;
+ m_data.m_u32NumberOfFaces = 1;
+ m_data.m_u32NumberOfMipmapLevels = m_pfile->GetNumMipmaps();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ void FileHeader_Ktx::Write(FILE *a_pfile)
+ {
+ size_t szBytesWritten;
+
+ // Write header
+ szBytesWritten = fwrite(&m_data, 1, sizeof(Data), a_pfile);
+ assert(szBytesWritten == sizeof(Data));
+
+ // Write KeyAndValuePairs
+ if (m_u32KeyValuePairs)
+ {
+ fwrite(m_pkeyvaluepair, m_pkeyvaluepair->u32KeyAndValueByteSize, 1, a_pfile);
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ FileHeader_Ktx::Data *FileHeader_Ktx::GetData()
+ {
+ return &m_data;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcFileHeader.h b/thirdparty/etc2comp/EtcFileHeader.h
new file mode 100644
index 0000000000..55a9cb5d9d
--- /dev/null
+++ b/thirdparty/etc2comp/EtcFileHeader.h
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcFile.h"
+#include <stdio.h>
+#include <inttypes.h>
+
+namespace Etc
+{
+
+ class Image;
+
+ class FileHeader
+ {
+ public:
+
+ virtual void Write(FILE *a_pfile) = 0;
+ File GetFile();
+ virtual ~FileHeader(void) {}
+ protected:
+
+ File *m_pfile;
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ class FileHeader_Pkm : public FileHeader
+ {
+ public:
+
+ FileHeader_Pkm(File *a_pfile);
+
+ virtual void Write(FILE *a_pfile);
+ virtual ~FileHeader_Pkm(void) {}
+ private:
+
+ typedef struct
+ {
+ char m_acMagicNumber[4];
+ char m_acVersion[2];
+ unsigned char m_ucDataType_msb; // e.g. ETC1_RGB_NO_MIPMAPS
+ unsigned char m_ucDataType_lsb;
+ unsigned char m_ucExtendedWidth_msb; // padded to 4x4 blocks
+ unsigned char m_ucExtendedWidth_lsb;
+ unsigned char m_ucExtendedHeight_msb; // padded to 4x4 blocks
+ unsigned char m_ucExtendedHeight_lsb;
+ unsigned char m_ucOriginalWidth_msb;
+ unsigned char m_ucOriginalWidth_lsb;
+ unsigned char m_ucOriginalHeight_msb;
+ unsigned char m_ucOriginalHeight_lsb;
+ } Data;
+
+ Data m_data;
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ class FileHeader_Ktx : public FileHeader
+ {
+ public:
+
+ typedef struct
+ {
+ uint32_t u32KeyAndValueByteSize;
+ } KeyValuePair;
+
+ typedef struct
+ {
+ uint8_t m_au8Identifier[12];
+ uint32_t m_u32Endianness;
+ uint32_t m_u32GlType;
+ uint32_t m_u32GlTypeSize;
+ uint32_t m_u32GlFormat;
+ uint32_t m_u32GlInternalFormat;
+ uint32_t m_u32GlBaseInternalFormat;
+ uint32_t m_u32PixelWidth;
+ uint32_t m_u32PixelHeight;
+ uint32_t m_u32PixelDepth;
+ uint32_t m_u32NumberOfArrayElements;
+ uint32_t m_u32NumberOfFaces;
+ uint32_t m_u32NumberOfMipmapLevels;
+ uint32_t m_u32BytesOfKeyValueData;
+ } Data;
+
+ enum class InternalFormat
+ {
+ ETC1_RGB8 = 0x8D64,
+ ETC1_ALPHA8 = ETC1_RGB8,
+ //
+ ETC2_R11 = 0x9270,
+ ETC2_SIGNED_R11 = 0x9271,
+ ETC2_RG11 = 0x9272,
+ ETC2_SIGNED_RG11 = 0x9273,
+ ETC2_RGB8 = 0x9274,
+ ETC2_SRGB8 = 0x9275,
+ ETC2_RGB8A1 = 0x9276,
+ ETC2_SRGB8_PUNCHTHROUGH_ALPHA1 = 0x9277,
+ ETC2_RGBA8 = 0x9278
+ };
+
+ enum class BaseInternalFormat
+ {
+ ETC2_R11 = 0x1903,
+ ETC2_RG11 = 0x8227,
+ ETC1_RGB8 = 0x1907,
+ ETC1_ALPHA8 = ETC1_RGB8,
+ //
+ ETC2_RGB8 = 0x1907,
+ ETC2_RGB8A1 = 0x1908,
+ ETC2_RGBA8 = 0x1908,
+ };
+
+ FileHeader_Ktx(File *a_pfile);
+
+ virtual void Write(FILE *a_pfile);
+ virtual ~FileHeader_Ktx(void) {}
+
+ void AddKeyAndValue(KeyValuePair *a_pkeyvaluepair);
+
+ Data* GetData();
+
+ private:
+
+ Data m_data;
+ KeyValuePair *m_pkeyvaluepair;
+
+ uint32_t m_u32Images;
+ uint32_t m_u32KeyValuePairs;
+ };
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcFilter.cpp b/thirdparty/etc2comp/EtcFilter.cpp
new file mode 100644
index 0000000000..bc899a533e
--- /dev/null
+++ b/thirdparty/etc2comp/EtcFilter.cpp
@@ -0,0 +1,401 @@
+#include <stdlib.h>
+#include <math.h>
+#include "EtcFilter.h"
+
+
+namespace Etc
+{
+
+static const double PiConst = 3.14159265358979323846;
+
+inline double sinc(double x)
+{
+ if ( x == 0.0 )
+ {
+ return 1.0;
+ }
+
+ return sin(PiConst * x) / (PiConst * x);
+}
+
+//inline float sincf( float x )
+//{
+// x *= F_PI;
+// if (x < 0.01f && x > -0.01f)
+// {
+// return 1.0f + x*x*(-1.0f/6.0f + x*x*1.0f/120.0f);
+// }
+//
+// return sinf(x)/x;
+//}
+//
+//double bessel0(double x)
+//{
+// const double EPSILON_RATIO = 1E-16;
+// double xh, sum, pow, ds;
+// int k;
+//
+// xh = 0.5 * x;
+// sum = 1.0;
+// pow = 1.0;
+// k = 0;
+// ds = 1.0;
+// while (ds > sum * EPSILON_RATIO)
+// {
+// ++k;
+// pow = pow * (xh / k);
+// ds = pow * pow;
+// sum = sum + ds;
+// }
+//
+// return sum;
+//}
+
+//**--------------------------------------------------------------------------
+//** Name: kaiser(double alpha, double half_width, double x)
+//** Returns:
+//** Description: Alpha controls shape of filter. We are using 4.
+//**--------------------------------------------------------------------------
+//inline double kaiser(double alpha, double half_width, double x)
+//{
+// double ratio = (x / half_width);
+// return bessel0(alpha * sqrt(1 - ratio * ratio)) / bessel0(alpha);
+//}
+//
+//float Filter_Lanczos4Sinc(float x)
+//{
+// if (x <= -4.0f || x >= 4.0f) // half-width of 4
+// {
+// return 0.0;
+// }
+//
+// return sinc(0.875f * x) * sinc(0.25f * x);
+//}
+//
+//double Filter_Kaiser4( double t )
+//{
+// return kaiser( 4.0, 3.0, t);
+//}
+//
+//double Filter_KaiserOptimal( double t )
+//{
+// return kaiser( 8.93, 3.0f, t);
+//}
+
+double FilterLanczos3( double t )
+{
+ if ( t <= -3.0 || t >= 3.0 )
+ {
+ return 0.0;
+ }
+
+ return sinc( t ) * sinc( t / 3.0 );
+}
+
+double FilterBox( double t )
+{
+ return ( t > -0.5 && t < 0.5) ? 1.0 : 0.0;
+}
+
+double FilterLinear( double t )
+{
+ if (t < 0.0) t = -t;
+
+ return (t < 1.0) ? (1.0 - t) : 0.0;
+}
+
+
+//**--------------------------------------------------------------------------
+//** Name: CalcContributions( int srcSize,
+//** int destSize,
+//** double filterSize,
+//** bool wrap,
+//** double (*FilterProc)(double),
+//** FilterWeights contrib[] )
+//** Returns: void
+//** Description:
+//**--------------------------------------------------------------------------
+void CalcContributions( int srcSize, int destSize, double filterSize, bool wrap, double (*FilterProc)(double), FilterWeights contrib[] )
+{
+ double scale;
+ double filterScale;
+ double center;
+ double totalWeight;
+ double weight;
+ int iRight;
+ int iLeft;
+ int iDest;
+
+ scale = (double)destSize / srcSize;
+ if ( scale < 1.0 )
+ {
+ filterSize = filterSize / scale;
+ filterScale = scale;
+ }
+ else
+ {
+ filterScale = 1.0;
+ }
+
+ if ( filterSize > (double)MaxFilterSize )
+ {
+ filterSize = (double)MaxFilterSize;
+ }
+
+ for ( iDest = 0; iDest < destSize; ++iDest )
+ {
+ center = (double)iDest / scale;
+
+ iLeft = (int)ceil(center - filterSize);
+ iRight = (int)floor(center + filterSize);
+
+ if ( !wrap )
+ {
+ if ( iLeft < 0 )
+ {
+ iLeft = 0;
+ }
+
+ if ( iRight >= srcSize )
+ {
+ iRight = srcSize - 1;
+ }
+ }
+
+ int numWeights = iRight - iLeft + 1;
+
+ contrib[iDest].first = iLeft;
+ contrib[iDest].numWeights = numWeights;
+
+ totalWeight = 0;
+ double t = ((double)iLeft - center) * filterScale;
+ for (int i = 0; i < numWeights; i++)
+ {
+ weight = (*FilterProc)(t) * filterScale;
+ totalWeight += weight;
+ contrib[iDest].weight[i] = weight;
+ t += filterScale;
+ }
+
+ //**--------------------------------------------------------
+ //** Normalize weights by dividing by the sum of the weights
+ //**--------------------------------------------------------
+ if ( totalWeight > 0.0 )
+ {
+ for ( int i = 0; i < numWeights; i++)
+ {
+ contrib[iDest].weight[i] /= totalWeight;
+ }
+ }
+ }
+}
+
+//**-------------------------------------------------------------------------
+//** Name: Filter_TwoPass( RGBCOLOR *pSrcImage,
+//** int srcWidth, int srcHeight,
+//** RGBCOLOR *pDestImage,
+//** int destWidth, int destHeight,
+//** double (*FilterProc)(double) )
+//** Returns: 0 on failure and 1 on success
+//** Description: Filters a 2d image with a two pass filter by averaging the
+//** weighted contributions of the pixels within the filter region. The
+//** contributions are determined by a weighting function parameter.
+//**-------------------------------------------------------------------------
+int FilterTwoPass( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight,
+ RGBCOLOR *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double (*FilterProc)(double) )
+{
+ FilterWeights *contrib;
+ RGBCOLOR *pPixel;
+ RGBCOLOR *pSrcPixel;
+ RGBCOLOR *pTempImage;
+ int iRow;
+ int iCol;
+ int iSrcCol;
+ int iSrcRow;
+ int iWeight;
+ double dRed;
+ double dGreen;
+ double dBlue;
+ double dAlpha;
+ double filterSize = 3.0;
+
+ int maxDim = (srcWidth>srcHeight)?srcWidth:srcHeight;
+ contrib = (FilterWeights*)malloc(maxDim * sizeof(FilterWeights));
+
+ //**------------------------------------------------------------------------
+ //** Need to create a temporary image to stuff the horizontally scaled image
+ //**------------------------------------------------------------------------
+ pTempImage = (RGBCOLOR *)malloc( destWidth * srcHeight * sizeof(RGBCOLOR) );
+ if ( pTempImage == NULL )
+ {
+ return 0;
+ }
+
+ //**-------------------------------------------------------
+ //** Horizontally filter the image into the temporary image
+ //**-------------------------------------------------------
+ bool bWrapHorizontal = !!(wrapFlags&FILTER_WRAP_X);
+ CalcContributions( srcWidth, destWidth, filterSize, bWrapHorizontal, FilterProc, contrib );
+ for ( iRow = 0; iRow < srcHeight; iRow++ )
+ {
+ for ( iCol = 0; iCol < destWidth; iCol++ )
+ {
+ dRed = 0;
+ dGreen = 0;
+ dBlue = 0;
+ dAlpha = 0;
+
+ for ( iWeight = 0; iWeight < contrib[iCol].numWeights; iWeight++ )
+ {
+ iSrcCol = iWeight + contrib[iCol].first;
+ if (bWrapHorizontal)
+ {
+ iSrcCol = (iSrcCol < 0) ? (srcWidth + iSrcCol) : (iSrcCol >= srcWidth) ? (iSrcCol - srcWidth) : iSrcCol;
+ }
+ pSrcPixel = pSrcImage + (iRow * srcWidth) + iSrcCol;
+ dRed += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[0];
+ dGreen += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[1];
+ dBlue += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[2];
+ dAlpha += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[3];
+ }
+
+ pPixel = pTempImage + (iRow * destWidth) + iCol;
+ pPixel->rgba[0] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dRed)));
+ pPixel->rgba[1] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dGreen)));
+ pPixel->rgba[2] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dBlue)));
+ pPixel->rgba[3] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dAlpha)));
+ }
+ }
+
+ //**-------------------------------------------------------
+ //** Vertically filter the image into the destination image
+ //**-------------------------------------------------------
+ bool bWrapVertical = !!(wrapFlags&FILTER_WRAP_Y);
+ CalcContributions(srcHeight, destHeight, filterSize, bWrapVertical, FilterProc, contrib);
+ for ( iCol = 0; iCol < destWidth; iCol++ )
+ {
+ for ( iRow = 0; iRow < destHeight; iRow++ )
+ {
+ dRed = 0;
+ dGreen = 0;
+ dBlue = 0;
+ dAlpha = 0;
+
+ for ( iWeight = 0; iWeight < contrib[iRow].numWeights; iWeight++ )
+ {
+ iSrcRow = iWeight + contrib[iRow].first;
+ if (bWrapVertical)
+ {
+ iSrcRow = (iSrcRow < 0) ? (srcHeight + iSrcRow) : (iSrcRow >= srcHeight) ? (iSrcRow - srcHeight) : iSrcRow;
+ }
+ pSrcPixel = pTempImage + (iSrcRow * destWidth) + iCol;
+ dRed += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[0];
+ dGreen += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[1];
+ dBlue += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[2];
+ dAlpha += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[3];
+ }
+
+ pPixel = pDestImage + (iRow * destWidth) + iCol;
+ pPixel->rgba[0] = (unsigned char)(std::max( 0.0, std::min( 255.0, dRed)));
+ pPixel->rgba[1] = (unsigned char)(std::max( 0.0, std::min( 255.0, dGreen)));
+ pPixel->rgba[2] = (unsigned char)(std::max( 0.0, std::min( 255.0, dBlue)));
+ pPixel->rgba[3] = (unsigned char)(std::max( 0.0, std::min( 255.0, dAlpha)));
+ }
+ }
+
+ free( pTempImage );
+ free( contrib );
+
+ return 1;
+}
+
+//**-------------------------------------------------------------------------
+//** Name: FilterResample(RGBCOLOR *pSrcImage, int srcWidth, int srcHeight,
+//** RGBCOLOR *pDstImage, int dstWidth, int dstHeight)
+//** Returns: 1
+//** Description: This function runs a 2d box filter over the srouce image
+//** to produce the destination image.
+//**-------------------------------------------------------------------------
+void FilterResample( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight,
+ RGBCOLOR *pDstImage, int dstWidth, int dstHeight )
+{
+ int iRow;
+ int iCol;
+ int iSampleRow;
+ int iSampleCol;
+ int iFirstSampleRow;
+ int iFirstSampleCol;
+ int iLastSampleRow;
+ int iLastSampleCol;
+ int red;
+ int green;
+ int blue;
+ int alpha;
+ int samples;
+ float xScale;
+ float yScale;
+
+ RGBCOLOR *pSrcPixel;
+ RGBCOLOR *pDstPixel;
+
+ xScale = (float)srcWidth / dstWidth;
+ yScale = (float)srcHeight / dstHeight;
+
+ for ( iRow = 0; iRow < dstHeight; iRow++ )
+ {
+ for ( iCol = 0; iCol < dstWidth; iCol++ )
+ {
+ iFirstSampleRow = (int)(iRow * yScale);
+ iLastSampleRow = (int)ceil(iFirstSampleRow + yScale - 1);
+ if ( iLastSampleRow >= srcHeight )
+ {
+ iLastSampleRow = srcHeight - 1;
+ }
+
+ iFirstSampleCol = (int)(iCol * xScale);
+ iLastSampleCol = (int)ceil(iFirstSampleCol + xScale - 1);
+ if ( iLastSampleCol >= srcWidth )
+ {
+ iLastSampleCol = srcWidth - 1;
+ }
+
+ samples = 0;
+ red = 0;
+ green = 0;
+ blue = 0;
+ alpha = 0;
+ for ( iSampleRow = iFirstSampleRow; iSampleRow <= iLastSampleRow; iSampleRow++ )
+ {
+ for ( iSampleCol = iFirstSampleCol; iSampleCol <= iLastSampleCol; iSampleCol++ )
+ {
+ pSrcPixel = pSrcImage + iSampleRow * srcWidth + iSampleCol;
+ red += pSrcPixel->rgba[0];
+ green += pSrcPixel->rgba[1];
+ blue += pSrcPixel->rgba[2];
+ alpha += pSrcPixel->rgba[3];
+
+ samples++;
+ }
+ }
+
+ pDstPixel = pDstImage + iRow * dstWidth + iCol;
+ if ( samples > 0 )
+ {
+ pDstPixel->rgba[0] = static_cast<uint8_t>(red / samples);
+ pDstPixel->rgba[1] = static_cast<uint8_t>(green / samples);
+ pDstPixel->rgba[2] = static_cast<uint8_t>(blue / samples);
+ pDstPixel->rgba[3] = static_cast<uint8_t>(alpha / samples);
+ }
+ else
+ {
+ pDstPixel->rgba[0] = static_cast<uint8_t>(red);
+ pDstPixel->rgba[1] = static_cast<uint8_t>(green);
+ pDstPixel->rgba[2] = static_cast<uint8_t>(blue);
+ pDstPixel->rgba[3] = static_cast<uint8_t>(alpha);
+ }
+ }
+ }
+}
+
+
+} \ No newline at end of file
diff --git a/thirdparty/etc2comp/EtcFilter.h b/thirdparty/etc2comp/EtcFilter.h
new file mode 100644
index 0000000000..fcf125c6df
--- /dev/null
+++ b/thirdparty/etc2comp/EtcFilter.h
@@ -0,0 +1,244 @@
+#pragma once
+#include <stdint.h>
+#include <algorithm>
+
+namespace Etc
+{
+
+enum FilterEnums
+{
+ MaxFilterSize = 32
+};
+
+enum WrapFlags
+{
+ FILTER_WRAP_NONE = 0,
+ FILTER_WRAP_X = 0x1,
+ FILTER_WRAP_Y = 0x2
+};
+
+typedef struct tagFilterWeights
+{
+ int first;
+ int numWeights;
+ double weight[MaxFilterSize * 2 + 1];
+} FilterWeights;
+
+typedef struct tagRGBCOLOR
+{
+ union
+ {
+ uint32_t ulColor;
+ uint8_t rgba[4];
+ };
+} RGBCOLOR;
+
+
+double FilterBox( double t );
+double FilterLinear( double t );
+double FilterLanczos3( double t );
+
+int FilterTwoPass( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight,
+ RGBCOLOR *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double (*FilterProc)(double) );
+void FilterResample( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight,
+ RGBCOLOR *pDstImage, int dstWidth, int dstHeight );
+
+
+void CalcContributions(int srcSize, int destSize, double filterSize, bool wrap, double(*FilterProc)(double), FilterWeights contrib[]);
+
+template <typename T>
+void FilterResample(T *pSrcImage, int srcWidth, int srcHeight, T *pDstImage, int dstWidth, int dstHeight)
+{
+ float xScale;
+ float yScale;
+
+ T *pSrcPixel;
+ T *pDstPixel;
+
+ xScale = (float)srcWidth / dstWidth;
+ yScale = (float)srcHeight / dstHeight;
+
+ for (int iRow = 0; iRow < dstHeight; iRow++)
+ {
+ for (int iCol = 0; iCol < dstWidth; iCol++)
+ {
+ int samples;
+ int iFirstSampleRow;
+ int iFirstSampleCol;
+ int iLastSampleRow;
+ int iLastSampleCol;
+ float red;
+ float green;
+ float blue;
+ float alpha;
+
+ iFirstSampleRow = (int)(iRow * yScale);
+ iLastSampleRow = (int)ceil(iFirstSampleRow + yScale - 1);
+ if (iLastSampleRow >= srcHeight)
+ {
+ iLastSampleRow = srcHeight - 1;
+ }
+
+ iFirstSampleCol = (int)(iCol * xScale);
+ iLastSampleCol = (int)ceil(iFirstSampleCol + xScale - 1);
+ if (iLastSampleCol >= srcWidth)
+ {
+ iLastSampleCol = srcWidth - 1;
+ }
+
+ samples = 0;
+ red = 0.f;
+ green = 0.f;
+ blue = 0.f;
+ alpha = 0.f;
+ for (int iSampleRow = iFirstSampleRow; iSampleRow <= iLastSampleRow; iSampleRow++)
+ {
+ for (int iSampleCol = iFirstSampleCol; iSampleCol <= iLastSampleCol; iSampleCol++)
+ {
+ pSrcPixel = pSrcImage + (iSampleRow * srcWidth + iSampleCol) * 4;
+ red += static_cast<float>(pSrcPixel[0]);
+ green += static_cast<float>(pSrcPixel[1]);
+ blue += static_cast<float>(pSrcPixel[2]);
+ alpha += static_cast<float>(pSrcPixel[3]);
+
+ samples++;
+ }
+ }
+
+ pDstPixel = pDstImage + (iRow * dstWidth + iCol) * 4;
+ if (samples > 0)
+ {
+ pDstPixel[0] = static_cast<T>(red / samples);
+ pDstPixel[1] = static_cast<T>(green / samples);
+ pDstPixel[2] = static_cast<T>(blue / samples);
+ pDstPixel[3] = static_cast<T>(alpha / samples);
+ }
+ else
+ {
+ pDstPixel[0] = static_cast<T>(red);
+ pDstPixel[1] = static_cast<T>(green);
+ pDstPixel[2] = static_cast<T>(blue);
+ pDstPixel[3] = static_cast<T>(alpha);
+ }
+ }
+ }
+
+}
+
+//**-------------------------------------------------------------------------
+//** Name: Filter_TwoPass( RGBCOLOR *pSrcImage,
+//** int srcWidth, int srcHeight,
+//** RGBCOLOR *pDestImage,
+//** int destWidth, int destHeight,
+//** double (*FilterProc)(double) )
+//** Returns: 0 on failure and 1 on success
+//** Description: Filters a 2d image with a two pass filter by averaging the
+//** weighted contributions of the pixels within the filter region. The
+//** contributions are determined by a weighting function parameter.
+//**-------------------------------------------------------------------------
+template <typename T>
+int FilterTwoPass(T *pSrcImage, int srcWidth, int srcHeight,
+ T *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double(*FilterProc)(double))
+{
+ const int numComponents = 4;
+ FilterWeights *contrib;
+ T *pPixel;
+ T *pTempImage;
+ double dRed;
+ double dGreen;
+ double dBlue;
+ double dAlpha;
+ double filterSize = 3.0;
+
+ int maxDim = (srcWidth>srcHeight) ? srcWidth : srcHeight;
+ contrib = new FilterWeights[maxDim];
+
+ //**------------------------------------------------------------------------
+ //** Need to create a temporary image to stuff the horizontally scaled image
+ //**------------------------------------------------------------------------
+ pTempImage = new T[destWidth * srcHeight * numComponents];
+ if (pTempImage == NULL)
+ {
+ return 0;
+ }
+
+ //**-------------------------------------------------------
+ //** Horizontally filter the image into the temporary image
+ //**-------------------------------------------------------
+ bool bWrapHorizontal = !!(wrapFlags&FILTER_WRAP_X);
+ CalcContributions(srcWidth, destWidth, filterSize, bWrapHorizontal, FilterProc, contrib);
+ for (int iRow = 0; iRow < srcHeight; iRow++)
+ {
+ for (int iCol = 0; iCol < destWidth; iCol++)
+ {
+ dRed = 0;
+ dGreen = 0;
+ dBlue = 0;
+ dAlpha = 0;
+
+ for (int iWeight = 0; iWeight < contrib[iCol].numWeights; iWeight++)
+ {
+ int iSrcCol = iWeight + contrib[iCol].first;
+ if(bWrapHorizontal)
+ {
+ iSrcCol = (iSrcCol < 0)?(srcWidth+iSrcCol):(iSrcCol >= srcWidth)?(iSrcCol-srcWidth):iSrcCol;
+ }
+ T* pSrcPixel = pSrcImage + ((iRow * srcWidth) + iSrcCol)*numComponents;
+ dRed += contrib[iCol].weight[iWeight] * pSrcPixel[0];
+ dGreen += contrib[iCol].weight[iWeight] * pSrcPixel[1];
+ dBlue += contrib[iCol].weight[iWeight] * pSrcPixel[2];
+ dAlpha += contrib[iCol].weight[iWeight] * pSrcPixel[3];
+ }
+
+ pPixel = pTempImage + ((iRow * destWidth) + iCol)*numComponents;
+ pPixel[0] = static_cast<T>(std::max(0.0, std::min(255.0, dRed)));
+ pPixel[1] = static_cast<T>(std::max(0.0, std::min(255.0, dGreen)));
+ pPixel[2] = static_cast<T>(std::max(0.0, std::min(255.0, dBlue)));
+ pPixel[3] = static_cast<T>(std::max(0.0, std::min(255.0, dAlpha)));
+ }
+ }
+
+ //**-------------------------------------------------------
+ //** Vertically filter the image into the destination image
+ //**-------------------------------------------------------
+ bool bWrapVertical = !!(wrapFlags&FILTER_WRAP_Y);
+ CalcContributions(srcHeight, destHeight, filterSize, bWrapVertical, FilterProc, contrib);
+ for (int iCol = 0; iCol < destWidth; iCol++)
+ {
+ for (int iRow = 0; iRow < destHeight; iRow++)
+ {
+ dRed = 0;
+ dGreen = 0;
+ dBlue = 0;
+ dAlpha = 0;
+
+ for (int iWeight = 0; iWeight < contrib[iRow].numWeights; iWeight++)
+ {
+ int iSrcRow = iWeight + contrib[iRow].first;
+ if (bWrapVertical)
+ {
+ iSrcRow = (iSrcRow < 0) ? (srcHeight + iSrcRow) : (iSrcRow >= srcHeight) ? (iSrcRow - srcHeight) : iSrcRow;
+ }
+ T* pSrcPixel = pTempImage + ((iSrcRow * destWidth) + iCol)*numComponents;
+ dRed += contrib[iRow].weight[iWeight] * pSrcPixel[0];
+ dGreen += contrib[iRow].weight[iWeight] * pSrcPixel[1];
+ dBlue += contrib[iRow].weight[iWeight] * pSrcPixel[2];
+ dAlpha += contrib[iRow].weight[iWeight] * pSrcPixel[3];
+ }
+
+ pPixel = pDestImage + ((iRow * destWidth) + iCol)*numComponents;
+ pPixel[0] = static_cast<T>(std::max(0.0, std::min(255.0, dRed)));
+ pPixel[1] = static_cast<T>(std::max(0.0, std::min(255.0, dGreen)));
+ pPixel[2] = static_cast<T>(std::max(0.0, std::min(255.0, dBlue)));
+ pPixel[3] = static_cast<T>(std::max(0.0, std::min(255.0, dAlpha)));
+ }
+ }
+
+ delete[] pTempImage;
+ delete[] contrib;
+
+ return 1;
+}
+
+
+} \ No newline at end of file
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
diff --git a/thirdparty/etc2comp/EtcImage.h b/thirdparty/etc2comp/EtcImage.h
new file mode 100644
index 0000000000..bd807ac32e
--- /dev/null
+++ b/thirdparty/etc2comp/EtcImage.h
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+//#include "Etc.h"
+#include "EtcColorFloatRGBA.h"
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcErrorMetric.h"
+
+
+namespace Etc
+{
+ class Block4x4;
+ class EncoderSpec;
+ class SortedBlockList;
+
+ class Image
+ {
+ public:
+
+ //the differnt warning and errors that can come up during encoding
+ enum EncodingStatus
+ {
+ SUCCESS = 0,
+ //
+ WARNING_THRESHOLD = 1 << 0,
+ //
+ WARNING_EFFORT_OUT_OF_RANGE = 1 << 1,
+ WARNING_JOBS_OUT_OF_RANGE = 1 << 2,
+ WARNING_SOME_NON_OPAQUE_PIXELS = 1 << 3,//just for opaque formats, etc1, rgb8, r11, rg11
+ WARNING_ALL_OPAQUE_PIXELS = 1 << 4,
+ WARNING_ALL_TRANSPARENT_PIXELS = 1 << 5,
+ WARNING_SOME_TRANSLUCENT_PIXELS = 1 << 6,//just for rgb8A1
+ WARNING_SOME_RGBA_NOT_0_TO_1 = 1 << 7,
+ WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO = 1 << 8,
+ WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO = 1 << 9,
+ //
+ ERROR_THRESHOLD = 1 << 16,
+ //
+ ERROR_UNKNOWN_FORMAT = 1 << 17,
+ ERROR_UNKNOWN_ERROR_METRIC = 1 << 18,
+ ERROR_ZERO_WIDTH_OR_HEIGHT = 1 << 19,
+ //
+ };
+
+ enum class Format
+ {
+ UNKNOWN,
+ //
+ ETC1,
+ //
+ // ETC2 formats
+ RGB8,
+ SRGB8,
+ RGBA8,
+ SRGBA8,
+ R11,
+ SIGNED_R11,
+ RG11,
+ SIGNED_RG11,
+ RGB8A1,
+ SRGB8A1,
+ //
+ FORMATS,
+ //
+ DEFAULT = SRGB8
+ };
+
+ // constructor using source image
+ Image(float *a_pafSourceRGBA, unsigned int a_uiSourceWidth,
+ unsigned int a_uiSourceHeight,
+ ErrorMetric a_errormetric);
+
+ // constructor using encoding bits
+ 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);
+
+ ~Image(void);
+
+ EncodingStatus Encode(Format a_format, ErrorMetric a_errormetric, float a_fEffort,
+ unsigned int a_uiJobs, unsigned int a_uiMaxJobs);
+
+ inline void AddToEncodingStatus(EncodingStatus a_encStatus)
+ {
+ m_encodingStatus = (EncodingStatus)((unsigned int)m_encodingStatus | (unsigned int)a_encStatus);
+ }
+
+ inline unsigned int GetSourceWidth(void)
+ {
+ return m_uiSourceWidth;
+ }
+
+ inline unsigned int GetSourceHeight(void)
+ {
+ return m_uiSourceHeight;
+ }
+
+ inline unsigned int GetExtendedWidth(void)
+ {
+ return m_uiExtendedWidth;
+ }
+
+ inline unsigned int GetExtendedHeight(void)
+ {
+ return m_uiExtendedHeight;
+ }
+
+ inline unsigned int GetNumberOfBlocks()
+ {
+ return m_uiBlockColumns * m_uiBlockRows;
+ }
+
+ inline Block4x4 * GetBlocks()
+ {
+ return m_pablock;
+ }
+
+ inline unsigned char * GetEncodingBits(void)
+ {
+ return m_paucEncodingBits;
+ }
+
+ inline unsigned int GetEncodingBitsBytes(void)
+ {
+ return m_uiEncodingBitsBytes;
+ }
+
+ inline int GetEncodingTimeMs(void)
+ {
+ return m_iEncodeTime_ms;
+ }
+
+ float GetError(void);
+
+ inline ColorFloatRGBA * GetSourcePixel(unsigned int a_uiH, unsigned int a_uiV)
+ {
+ if (a_uiH >= m_uiSourceWidth || a_uiV >= m_uiSourceHeight)
+ {
+ return nullptr;
+ }
+
+ return &m_pafrgbaSource[a_uiV*m_uiSourceWidth + a_uiH];
+ }
+
+ inline Format GetFormat(void)
+ {
+ return m_format;
+ }
+
+ static Block4x4EncodingBits::Format DetermineEncodingBitsFormat(Format a_format);
+
+ inline static unsigned short CalcExtendedDimension(unsigned short a_ushOriginalDimension)
+ {
+ return (unsigned short)((a_ushOriginalDimension + 3) & ~3);
+ }
+
+ inline ErrorMetric GetErrorMetric(void)
+ {
+ return m_errormetric;
+ }
+
+ static const char * EncodingFormatToString(Image::Format a_format);
+ const char * EncodingFormatToString(void);
+ //used to get basic information about the image data
+ int m_iNumOpaquePixels;
+ int m_iNumTranslucentPixels;
+ int m_iNumTransparentPixels;
+
+ ColorFloatRGBA m_numColorValues;
+ ColorFloatRGBA m_numOutOfRangeValues;
+
+ bool m_bVerboseOutput;
+ private:
+ //add a warning or error to check for while encoding
+ inline void TrackEncodingWarning(EncodingStatus a_encStatus)
+ {
+ m_warningsToCapture = (EncodingStatus)((unsigned int)m_warningsToCapture | (unsigned int)a_encStatus);
+ }
+
+ //report the warning if it is something we care about for this encoding
+ inline void AddToEncodingStatusIfSignfigant(EncodingStatus a_encStatus)
+ {
+ if ((EncodingStatus)((unsigned int)m_warningsToCapture & (unsigned int)a_encStatus) == a_encStatus)
+ {
+ AddToEncodingStatus(a_encStatus);
+ }
+ }
+
+ Image(void);
+ void FindEncodingWarningTypesForCurFormat();
+ void FindAndSetEncodingWarnings();
+
+ void InitBlocksAndBlockSorter(void);
+
+ void RunFirstPass(unsigned int a_uiMultithreadingOffset,
+ unsigned int a_uiMultithreadingStride);
+
+ void SetEncodingBits(unsigned int a_uiMultithreadingOffset,
+ unsigned int a_uiMultithreadingStride);
+
+ unsigned int IterateThroughWorstBlocks(unsigned int a_uiMaxBlocks,
+ unsigned int a_uiMultithreadingOffset,
+ unsigned int a_uiMultithreadingStride);
+
+ // inputs
+ ColorFloatRGBA *m_pafrgbaSource;
+ unsigned int m_uiSourceWidth;
+ unsigned int m_uiSourceHeight;
+ unsigned int m_uiExtendedWidth;
+ unsigned int m_uiExtendedHeight;
+ unsigned int m_uiBlockColumns;
+ unsigned int m_uiBlockRows;
+ // intermediate data
+ Block4x4 *m_pablock;
+ // encoding
+ Format m_format;
+ Block4x4EncodingBits::Format m_encodingbitsformat;
+ unsigned int m_uiEncodingBitsBytes; // for entire image
+ unsigned char *m_paucEncodingBits;
+ ErrorMetric m_errormetric;
+ float m_fEffort;
+ // stats
+ int m_iEncodeTime_ms;
+
+ SortedBlockList *m_psortedblocklist;
+ //this will hold any warning or errors that happen during encoding
+ EncodingStatus m_encodingStatus;
+ //these will be the warnings we are tracking
+ EncodingStatus m_warningsToCapture;
+ };
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcIndividualTrys.cpp b/thirdparty/etc2comp/EtcIndividualTrys.cpp
new file mode 100644
index 0000000000..56ff4c65ec
--- /dev/null
+++ b/thirdparty/etc2comp/EtcIndividualTrys.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+/*
+EtcIndividualTrys.cpp
+
+Gathers the results of the various encoding trys for both halves of a 4x4 block for Individual mode
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcIndividualTrys.h"
+
+#include <assert.h>
+
+namespace Etc
+{
+
+ // ----------------------------------------------------------------------------------------------------
+ // construct a list of trys (encoding attempts)
+ //
+ // a_frgbaColor1 is the basecolor for the first half
+ // a_frgbaColor2 is the basecolor for the second half
+ // a_pauiPixelMapping1 is the pixel order for the first half
+ // a_pauiPixelMapping2 is the pixel order for the second half
+ // a_uiRadius is the amount to vary the base colors
+ //
+ IndividualTrys::IndividualTrys(ColorFloatRGBA a_frgbaColor1, ColorFloatRGBA a_frgbaColor2,
+ const unsigned int *a_pauiPixelMapping1,
+ const unsigned int *a_pauiPixelMapping2,
+ unsigned int a_uiRadius)
+ {
+ assert(a_uiRadius <= MAX_RADIUS);
+
+ ColorFloatRGBA frgbaQuantizedColor1 = a_frgbaColor1.QuantizeR4G4B4();
+ ColorFloatRGBA frgbaQuantizedColor2 = a_frgbaColor2.QuantizeR4G4B4();
+
+ // quantize base colors
+ // ensure that trys with a_uiRadius don't overflow
+ int iRed1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntRed(15.0f), a_uiRadius);
+ int iGreen1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntGreen(15.0f), a_uiRadius);
+ int iBlue1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntBlue(15.0f), a_uiRadius);
+ int iRed2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntRed(15.0f), a_uiRadius);
+ int iGreen2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntGreen(15.0f), a_uiRadius);
+ int iBlue2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntBlue(15.0f), a_uiRadius);
+
+ m_half1.Init(iRed1, iGreen1, iBlue1, a_pauiPixelMapping1, a_uiRadius);
+ m_half2.Init(iRed2, iGreen2, iBlue2, a_pauiPixelMapping2, a_uiRadius);
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ void IndividualTrys::Half::Init(int a_iRed, int a_iGreen, int a_iBlue,
+ const unsigned int *a_pauiPixelMapping, unsigned int a_uiRadius)
+ {
+
+ m_iRed = a_iRed;
+ m_iGreen = a_iGreen;
+ m_iBlue = a_iBlue;
+
+ m_pauiPixelMapping = a_pauiPixelMapping;
+ m_uiRadius = a_uiRadius;
+
+ m_uiTrys = 0;
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcIndividualTrys.h b/thirdparty/etc2comp/EtcIndividualTrys.h
new file mode 100644
index 0000000000..5fb12fbcf4
--- /dev/null
+++ b/thirdparty/etc2comp/EtcIndividualTrys.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColorFloatRGBA.h"
+
+namespace Etc
+{
+
+ class IndividualTrys
+ {
+ public:
+
+ static const unsigned int MAX_RADIUS = 1;
+
+ IndividualTrys(ColorFloatRGBA a_frgbaColor1,
+ ColorFloatRGBA a_frgbaColor2,
+ const unsigned int *a_pauiPixelMapping1,
+ const unsigned int *a_pauiPixelMapping2,
+ unsigned int a_uiRadius);
+
+ inline static int MoveAwayFromEdge(int a_i, int a_iDistance)
+ {
+ if (a_i < (0+ a_iDistance))
+ {
+ return (0 + a_iDistance);
+ }
+ else if (a_i > (15- a_iDistance))
+ {
+ return (15 - a_iDistance);
+ }
+
+ return a_i;
+ }
+
+ class Try
+ {
+ public :
+ static const unsigned int SELECTORS = 8; // per half
+
+ int m_iRed;
+ int m_iGreen;
+ int m_iBlue;
+ unsigned int m_uiCW;
+ unsigned int m_auiSelectors[SELECTORS];
+ float m_fError;
+ };
+
+ class Half
+ {
+ public:
+
+ static const unsigned int MAX_TRYS = 27;
+
+ void Init(int a_iRed, int a_iGreen, int a_iBlue,
+ const unsigned int *a_pauiPixelMapping,
+ unsigned int a_uiRadius);
+
+ // center of trys
+ int m_iRed;
+ int m_iGreen;
+ int m_iBlue;
+
+ const unsigned int *m_pauiPixelMapping;
+ unsigned int m_uiRadius;
+
+ unsigned int m_uiTrys;
+ Try m_atry[MAX_TRYS];
+
+ Try *m_ptryBest;
+ };
+
+ Half m_half1;
+ Half m_half2;
+
+ };
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcMath.cpp b/thirdparty/etc2comp/EtcMath.cpp
new file mode 100644
index 0000000000..096d5f7ab9
--- /dev/null
+++ b/thirdparty/etc2comp/EtcMath.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include "EtcConfig.h"
+#include "EtcMath.h"
+
+namespace Etc
+{
+
+ // ----------------------------------------------------------------------------------------------------
+ // calculate the line that best fits the set of XY points contained in a_afX[] and a_afY[]
+ // use a_fSlope and a_fOffset to define that line
+ //
+ bool Regression(float a_afX[], float a_afY[], unsigned int a_Points,
+ float *a_fSlope, float *a_fOffset)
+ {
+ float fPoints = (float)a_Points;
+
+ float fSumX = 0.0f;
+ float fSumY = 0.0f;
+ float fSumXY = 0.0f;
+ float fSumX2 = 0.0f;
+
+ for (unsigned int uiPoint = 0; uiPoint < a_Points; uiPoint++)
+ {
+ fSumX += a_afX[uiPoint];
+ fSumY += a_afY[uiPoint];
+ fSumXY += a_afX[uiPoint] * a_afY[uiPoint];
+ fSumX2 += a_afX[uiPoint] * a_afX[uiPoint];
+ }
+
+ float fDivisor = fPoints*fSumX2 - fSumX*fSumX;
+
+ // if vertical line
+ if (fDivisor == 0.0f)
+ {
+ *a_fSlope = 0.0f;
+ *a_fOffset = 0.0f;
+ return true;
+ }
+
+ *a_fSlope = (fPoints*fSumXY - fSumX*fSumY) / fDivisor;
+ *a_fOffset = (fSumY - (*a_fSlope)*fSumX) / fPoints;
+
+ return false;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcMath.h b/thirdparty/etc2comp/EtcMath.h
new file mode 100644
index 0000000000..c58c9a91bc
--- /dev/null
+++ b/thirdparty/etc2comp/EtcMath.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math.h>
+
+namespace Etc
+{
+
+ // ----------------------------------------------------------------------------------------------------
+ // return true if vertical line
+ bool Regression(float a_afX[], float a_afY[], unsigned int a_Points,
+ float *a_fSlope, float *a_fOffset);
+
+ inline float ConvertMSEToPSNR(float a_fMSE)
+ {
+ if (a_fMSE == 0.0f)
+ {
+ return INFINITY;
+ }
+
+ return 10.0f * log10f(1.0f / a_fMSE);
+ }
+
+
+}
diff --git a/thirdparty/etc2comp/EtcSortedBlockList.cpp b/thirdparty/etc2comp/EtcSortedBlockList.cpp
new file mode 100644
index 0000000000..bfa6b7b3fa
--- /dev/null
+++ b/thirdparty/etc2comp/EtcSortedBlockList.cpp
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+/*
+EtcSortedBlockList.cpp
+
+SortedBlockList is a list of 4x4 blocks that can be used by the "effort" system to prioritize
+the encoding of the 4x4 blocks.
+
+The sorting is done with buckets, where each bucket is an indication of how much error each 4x4 block has
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcSortedBlockList.h"
+
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+namespace Etc
+{
+
+ // ----------------------------------------------------------------------------------------------------
+ // construct an empty list
+ //
+ // allocate enough memory to add all of the image's 4x4 blocks later
+ // allocate enough buckets to sort the blocks
+ //
+ SortedBlockList::SortedBlockList(unsigned int a_uiImageBlocks, unsigned int a_uiBuckets)
+ {
+ m_uiImageBlocks = a_uiImageBlocks;
+ m_iBuckets = (int)a_uiBuckets;
+
+ m_uiAddedBlocks = 0;
+ m_uiSortedBlocks = 0;
+ m_palinkPool = new Link[m_uiImageBlocks];
+ m_pabucket = new Bucket[m_iBuckets];
+ m_fMaxError = 0.0f;
+
+ InitBuckets();
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+ SortedBlockList::~SortedBlockList(void)
+ {
+ delete[] m_palinkPool;
+ delete[] m_pabucket;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // add a 4x4 block to the list
+ // the 4x4 block will be sorted later
+ //
+ void SortedBlockList::AddBlock(Block4x4 *a_pblock)
+ {
+ assert(m_uiAddedBlocks < m_uiImageBlocks);
+ Link *plink = &m_palinkPool[m_uiAddedBlocks++];
+ plink->Init(a_pblock);
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // sort all of the 4x4 blocks that have been added to the list
+ //
+ // first, determine the maximum error, then assign an error range to each bucket
+ // next, determine which bucket each 4x4 block belongs to based on the 4x4 block's error
+ // add the 4x4 block to the appropriate bucket
+ // lastly, walk thru the buckets and add each bucket to a sorted linked list
+ //
+ // the resultant sorting is an approximate sorting from most to least error
+ //
+ void SortedBlockList::Sort(void)
+ {
+ assert(m_uiAddedBlocks == m_uiImageBlocks);
+ InitBuckets();
+
+ // find max block error
+ m_fMaxError = -1.0f;
+
+ for (unsigned int uiLink = 0; uiLink < m_uiAddedBlocks; uiLink++)
+ {
+ Link *plinkBlock = &m_palinkPool[uiLink];
+
+ float fBlockError = plinkBlock->GetBlock()->GetError();
+ if (fBlockError > m_fMaxError)
+ {
+ m_fMaxError = fBlockError;
+ }
+ }
+ // prevent divide by zero or divide by negative
+ if (m_fMaxError <= 0.0f)
+ {
+ m_fMaxError = 1.0f;
+ }
+ //used for debugging
+ //int numDone = 0;
+ // put all of the blocks with unfinished encodings into the appropriate bucket
+ m_uiSortedBlocks = 0;
+ for (unsigned int uiLink = 0; uiLink < m_uiAddedBlocks; uiLink++)
+ {
+ Link *plinkBlock = &m_palinkPool[uiLink];
+
+ // if the encoding is done, don't add it to the list
+ if (plinkBlock->GetBlock()->GetEncoding()->IsDone())
+ {
+ //numDone++;
+ continue;
+ }
+
+ // calculate the appropriate sort bucket
+ float fBlockError = plinkBlock->GetBlock()->GetError();
+ int iBucket = (int) floorf(m_iBuckets * fBlockError / m_fMaxError);
+ // clamp to bucket index
+ iBucket = iBucket < 0 ? 0 : iBucket >= m_iBuckets ? m_iBuckets - 1 : iBucket;
+
+ // add block to bucket
+ {
+ Bucket *pbucket = &m_pabucket[iBucket];
+ if (pbucket->plinkLast)
+ {
+ pbucket->plinkLast->SetNext(plinkBlock);
+ pbucket->plinkLast = plinkBlock;
+ }
+ else
+ {
+ pbucket->plinkFirst = pbucket->plinkLast = plinkBlock;
+ }
+ plinkBlock->SetNext(nullptr);
+ }
+
+ m_uiSortedBlocks++;
+
+ if (0)
+ {
+ printf("%u: e=%.3f\n", uiLink, fBlockError);
+ Print();
+ printf("\n\n\n");
+ }
+ }
+ //printf("num blocks already done: %d\n",numDone);
+ //link the blocks together across buckets
+ m_plinkFirst = nullptr;
+ m_plinkLast = nullptr;
+ for (int iBucket = m_iBuckets - 1; iBucket >= 0; iBucket--)
+ {
+ Bucket *pbucket = &m_pabucket[iBucket];
+
+ if (pbucket->plinkFirst)
+ {
+ if (m_plinkFirst == nullptr)
+ {
+ m_plinkFirst = pbucket->plinkFirst;
+ }
+ else
+ {
+ assert(pbucket->plinkLast->GetNext() == nullptr);
+ m_plinkLast->SetNext(pbucket->plinkFirst);
+ }
+
+ m_plinkLast = pbucket->plinkLast;
+ }
+ }
+
+
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // clear all of the buckets. normally done in preparation for a sort
+ //
+ void SortedBlockList::InitBuckets(void)
+ {
+ for (int iBucket = 0; iBucket < m_iBuckets; iBucket++)
+ {
+ Bucket *pbucket = &m_pabucket[iBucket];
+
+ pbucket->plinkFirst = 0;
+ pbucket->plinkLast = 0;
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // print out the list of sorted 4x4 blocks
+ // normally used for debugging
+ //
+ void SortedBlockList::Print(void)
+ {
+ for (int iBucket = m_iBuckets-1; iBucket >= 0; iBucket--)
+ {
+ Bucket *pbucket = &m_pabucket[iBucket];
+
+ unsigned int uiBlocks = 0;
+ for (Link *plink = pbucket->plinkFirst; plink != nullptr; plink = plink->GetNext() )
+ {
+ uiBlocks++;
+
+ if (plink == pbucket->plinkLast)
+ {
+ break;
+ }
+ }
+
+ float fBucketError = m_fMaxError * iBucket / m_iBuckets;
+ float fBucketRMS = sqrtf(fBucketError / (4.0f*16.0f) );
+ printf("%3d: e=%.3f rms=%.6f %u\n", iBucket, fBucketError, fBucketRMS, uiBlocks);
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ //
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/EtcSortedBlockList.h b/thirdparty/etc2comp/EtcSortedBlockList.h
new file mode 100644
index 0000000000..960e8adc34
--- /dev/null
+++ b/thirdparty/etc2comp/EtcSortedBlockList.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace Etc
+{
+ class Block4x4;
+
+ class SortedBlockList
+ {
+ public:
+
+ class Link
+ {
+ public:
+
+ inline void Init(Block4x4 *a_pblock)
+ {
+ m_pblock = a_pblock;
+ m_plinkNext = nullptr;
+ }
+
+ inline Block4x4 * GetBlock(void)
+ {
+ return m_pblock;
+ }
+
+ inline void SetNext(Link *a_plinkNext)
+ {
+ m_plinkNext = a_plinkNext;
+ }
+
+ inline Link * GetNext(void)
+ {
+ return m_plinkNext;
+ }
+
+ inline Link * Advance(unsigned int a_uiSteps = 1)
+ {
+ Link *plink = this;
+
+ for (unsigned int uiStep = 0; uiStep < a_uiSteps; uiStep++)
+ {
+ if (plink == nullptr)
+ {
+ break;
+ }
+
+ plink = plink->m_plinkNext;
+ }
+
+ return plink;
+ }
+
+ private:
+
+ Block4x4 *m_pblock;
+ Link *m_plinkNext;
+ };
+
+ SortedBlockList(unsigned int a_uiImageBlocks, unsigned int a_uiBuckets);
+ ~SortedBlockList(void);
+
+ void AddBlock(Block4x4 *a_pblock);
+
+ void Sort(void);
+
+ inline Link * GetLinkToFirstBlock(void)
+ {
+ return m_plinkFirst;
+ }
+
+ inline unsigned int GetNumberOfAddedBlocks(void)
+ {
+ return m_uiAddedBlocks;
+ }
+
+ inline unsigned int GetNumberOfSortedBlocks(void)
+ {
+ return m_uiSortedBlocks;
+ }
+
+ void Print(void);
+
+ private:
+
+ void InitBuckets(void);
+
+ class Bucket
+ {
+ public:
+ Link *plinkFirst;
+ Link *plinkLast;
+ };
+
+ unsigned int m_uiImageBlocks;
+ int m_iBuckets;
+
+ unsigned int m_uiAddedBlocks;
+ unsigned int m_uiSortedBlocks;
+ Link *m_palinkPool;
+ Bucket *m_pabucket;
+ float m_fMaxError;
+
+ Link *m_plinkFirst;
+ Link *m_plinkLast;
+
+ };
+
+} // namespace Etc
diff --git a/thirdparty/etc2comp/LICENSE b/thirdparty/etc2comp/LICENSE
new file mode 100644
index 0000000000..75b52484ea
--- /dev/null
+++ b/thirdparty/etc2comp/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/thirdparty/etc2comp/README.md b/thirdparty/etc2comp/README.md
new file mode 100644
index 0000000000..1c70ae9f4e
--- /dev/null
+++ b/thirdparty/etc2comp/README.md
@@ -0,0 +1,197 @@
+# Etc2Comp - Texture to ETC2 compressor
+
+Etc2Comp is a command line tool that converts textures (e.g. bitmaps)
+into the [ETC2](https://en.wikipedia.org/wiki/Ericsson_Texture_Compression)
+format. The tool is built with a focus on encoding performance
+to reduce the amount of time required to compile asset heavy applications as
+well as reduce overall application size.
+
+This repo provides source code that can be compiled into a binary. The
+binary can then be used to convert textures to the ETC2 format.
+
+Important: This is not an official Google product. It is an experimental
+library published as-is. Please see the CONTRIBUTORS.md file for information
+about questions or issues.
+
+## Setup
+This project uses [CMake](https://cmake.org/) to generate platform-specific
+build files:
+ - Linux: make files
+ - OS X: Xcode workspace files
+ - Microsoft Windows: Visual Studio solution files
+ - Note: CMake supports other formats, but this doc only provides steps for
+ one of each platform for brevity.
+
+Refer to each platform's setup section to setup your environment and build
+an Etc2Comp binary. Then skip to the usage section of this page for examples
+of how to use the library.
+
+### Setup for OS X
+ build tested on this config:
+ OS X 10.9.5 i7 16GB RAM
+ Xcode 5.1.1
+ cmake 3.2.3
+
+Start by downloading and installing the following components if they are not
+already installed on your development machine.
+ - *Xcode* version 5.1.1, or greater
+ - [CMake](https://cmake.org/download/) version 3.2.3, or greater
+
+To build the Etc2Comp binary:
+ 1. Open a *Terminal* window and navigate to the project directory.
+ 1. Run `mkdir build_xcode`
+ 1. Run `cd build_xcode`
+ 1. Run `cmake -G Xcode ../`
+ 1. Open *Xcode* and import the `build_xcode/EtcTest.xcodeproj` file.
+ 1. Open the Product menu and choose Build For -> Running.
+ 1. Once the build succeeds the binary located at `build_xcode/EtcTool/Debug/EtcTool`
+can be executed.
+
+Optional
+Xcode EtcTool ‘Run’ preferences
+note: if the build_xcode/EtcTest.xcodeproj is manually deleted then some Xcode preferences
+will need to be set by hand after cmake is run (these prefs are retained across
+cmake updates if the .xcodeproj is not deleted/removed)
+
+1. Set the active scheme to ‘EtcTool’
+1. Edit the scheme
+1. Select option ‘Run EtcTool’, then tab ‘Arguments’.
+Add this launch argument: ‘-argfile ../../EtcTool/args.txt’
+1. Select tab ‘Options’ and set a custom working directory to: ‘$(SRCROOT)/Build_Xcode/EtcTool’
+
+### SetUp for Windows
+
+1. Open a *Terminal* window and navigate to the project directory.
+1. Run `mkdir build_vs`
+1. Run `cd build_vs`
+1. Run CMAKE, noting what build version you need, and pointing to the parent directory as the source root;
+ For VS 2013 : `cmake -G "Visual Studio 12 2013 Win64" ../`
+ For VS 2015 : `cmake -G "Visual Studio 14 2015 Win64" ../`
+ NOTE: To see what supported Visual Studio outputs there are, run `cmake -G`
+1. open the 'EtcTest' solution
+1. make the 'EtcTool' project the start up project
+1. (optional) in the project properties, under 'Debugging ->command arguments'
+add the argfile textfile thats included in the EtcTool directory.
+example: -argfile C:\etc2\EtcTool\Args.txt
+
+### Setup For Linux
+The Linux build was tested on this config:
+ Ubuntu desktop 14.04
+ gcc/g++ 4.8
+ cmake 2.8.12.2
+
+1. Verify linux has cmake and C++-11 capable g++ installed
+1. Open shell
+1. Run `mkdir build_linux`
+1. Run `cd build_linux`
+1. Run `cmake ../`
+1. Run `make`
+1. navigate to the newly created EtcTool directory `cd EtcTool`
+1. run the executable: `./EtcTool -argfile ../../EtcTool/args.txt`
+
+Skip to the <a href="#usage">Usage</a> section for more information about using the
+tool.
+
+## Usage
+
+### Command Line Usage
+EtcTool can be run from the command line with the following usage:
+ etctool.exe source_image [options ...] -output encoded_image
+
+The encoder will use an array of RGBA floats read from the source_image to create
+an ETC1 or ETC2 encoded image in encoded_image. The RGBA floats should be in the
+range [0:1].
+
+Options:
+
+ -analyze <analysis_folder>
+ -argfile <arg_file> additional command line arguments read from a file
+ -blockAtHV <H V> encodes a single block that contains the
+ pixel specified by the H V coordinates
+ -compare <comparison_image> compares source_image to comparison_image
+ -effort <amount> number between 0 and 100 to specify the encoding quality
+ (100 is the highest quality)
+ -errormetric <error_metric> specify the error metric, the options are
+ rgba, rgbx, rec709, numeric and normalxyz
+ -format <etc_format> ETC1, RGB8, SRGB8, RGBA8, SRGB8, RGB8A1,
+ SRGB8A1 or R11
+ -help prints this message
+ -jobs or -j <thread_count> specifies the number of threads (default=1)
+ -normalizexyz normalize RGB to have a length of 1
+ -verbose or -v shows status information during the encoding
+ process
+ -mipmaps or -m <mip_count> sets the maximum number of mipaps to generate (default=1)
+ -mipwrap or -w <x|y|xy> sets the mipmap filter wrap mode (default=clamp)
+
+* -analyze will run an analysis of the encoding and place it in folder
+"analysis_folder" (e.g. ../analysis/kodim05). within the analysis_folder, a folder
+will be created with a name of the current date/time (e.g. 20151204_153306). this
+date/time folder is used to compare encodings of the same texture over time.
+within the date/time folder is a text file with several encoding stats and a 2x png
+image showing the encoding mode for each 4x4 block.
+
+* -argfile allows additional command line arguments to be placed in a text file
+
+* -blockAtHV selects the 4x4 pixel subset of the source image at position (H,V).
+This is mainly used for debugging
+
+* -compare compares the source image to the created encoded image. The encoding
+will dictate what error analysis is used in the comparison.
+
+* -effort uses an "amount" between 0 and 100 to determine how much additional effort
+to apply during the encoding.
+
+* -errormetric selects the fitting algorithm used by the encoder. "rgba" calculates
+RMS error using RGB components that are weighted by A. "rgbx" calculates RMS error
+using RGBA components, where A is treated as an additional data channel, instead of
+as alpha. "rec709" is similar to "rgba", except the RGB components are also weighted
+according to Rec709. "numeric" calculates RMS error using unweighted RGBA components.
+"normalize" calculates error based on dot product and vector length for RGB and RMS
+error for A.
+
+* -help prints out the usage message
+
+* -jobs enables multi-threading to speed up image encoding
+
+* -normalizexyz normalizes the source RGB to have a length of 1.
+
+* -verbose shows information on the current encoding process. It will then display the
+PSNR and time time it took to encode the image.
+
+* -mipmaps takes an argument that specifies how many mipmaps to generate from the
+source image. The mipmaps are generated with a lanczos3 filter using edge clamping.
+If the mipmaps option is not specified no mipmaps are created.
+
+* -mipwrap takes an argument that specifies the mipmap filter wrap mode. The options
+are "x", "y" and "xy" which specify wrapping in x only, y only or x and y respectively.
+The default options are clamping in both x and y.
+
+Note: Path names can use slashes or backslashes. The tool will convert the
+slashes to the appropriate polarity for the current platform.
+
+
+## API
+
+The library supports two different APIs - a C-like API that is not heavily
+class-based and a class-based API.
+
+main() in EtcTool.cpp contains an example of both APIs.
+
+The Encode() method now returns an EncodingStatus that contains bit flags for
+reporting various warnings and flags encountered when encoding.
+
+
+## Copyright
+Copyright 2015 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.
diff --git a/thirdparty/rg-etc1/rg_etc1.cpp b/thirdparty/rg-etc1/rg_etc1.cpp
deleted file mode 100644
index 8e28b53f9d..0000000000
--- a/thirdparty/rg-etc1/rg_etc1.cpp
+++ /dev/null
@@ -1,2446 +0,0 @@
-// File: rg_etc1.cpp - Fast, high quality ETC1 block packer/unpacker - Rich Geldreich <richgel99@gmail.com>
-// Please see ZLIB license at the end of rg_etc1.h.
-//
-// For more information Ericsson Texture Compression (ETC/ETC1), see:
-// http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
-//
-// v1.04 - 5/15/14 - Fix signed vs. unsigned subtraction problem (noticed when compiled with gcc) in pack_etc1_block_init().
-// This issue would cause an assert when this func. was called in debug. (Note this module was developed/testing with MSVC,
-// I still need to test it throughly when compiled with gcc.)
-//
-// v1.03 - 5/12/13 - Initial public release
-#include "rg_etc1.h"
-
-#include <stdlib.h>
-#include <memory.h>
-#include <assert.h>
-//#include <stdio.h>
-#include <math.h>
-
-#pragma warning (disable: 4201) // nonstandard extension used : nameless struct/union
-
-#if defined(_DEBUG) || defined(DEBUG)
-#define RG_ETC1_BUILD_DEBUG
-#endif
-
-#define RG_ETC1_ASSERT assert
-
-namespace rg_etc1
-{
- typedef unsigned char uint8;
- typedef unsigned short uint16;
- typedef unsigned int uint;
- typedef unsigned int uint32;
- typedef long long int64;
- typedef unsigned long long uint64;
-
- const uint32 cUINT32_MAX = 0xFFFFFFFFU;
- const uint64 cUINT64_MAX = 0xFFFFFFFFFFFFFFFFULL; //0xFFFFFFFFFFFFFFFFui64;
-
- template<typename T> inline T minimum(T a, T b) { return (a < b) ? a : b; }
- template<typename T> inline T minimum(T a, T b, T c) { return minimum(minimum(a, b), c); }
- template<typename T> inline T maximum(T a, T b) { return (a > b) ? a : b; }
- template<typename T> inline T maximum(T a, T b, T c) { return maximum(maximum(a, b), c); }
- template<typename T> inline T clamp(T value, T low, T high) { return (value < low) ? low : ((value > high) ? high : value); }
- template<typename T> inline T square(T value) { return value * value; }
- template<typename T> inline void zero_object(T& obj) { memset((void*)&obj, 0, sizeof(obj)); }
- template<typename T> inline void zero_this(T* pObj) { memset((void*)pObj, 0, sizeof(*pObj)); }
-
- template<class T, size_t N> T decay_array_to_subtype(T (&a)[N]);
-
-#define RG_ETC1_ARRAY_SIZE(X) (sizeof(X) / sizeof(decay_array_to_subtype(X)))
-
- enum eNoClamp { cNoClamp };
-
- struct color_quad_u8
- {
- static inline int clamp(int v) { if (v & 0xFFFFFF00U) v = (~(static_cast<int>(v) >> 31)) & 0xFF; return v; }
-
- struct component_traits { enum { cSigned = false, cFloat = false, cMin = 0U, cMax = 255U }; };
-
- public:
- typedef unsigned char component_t;
- typedef int parameter_t;
-
- enum { cNumComps = 4 };
-
- union
- {
- struct
- {
- component_t r;
- component_t g;
- component_t b;
- component_t a;
- };
-
- component_t c[cNumComps];
-
- uint32 m_u32;
- };
-
- inline color_quad_u8()
- {
- }
-
- inline color_quad_u8(const color_quad_u8& other) : m_u32(other.m_u32)
- {
- }
-
- explicit inline color_quad_u8(parameter_t y, parameter_t alpha = component_traits::cMax)
- {
- set(y, alpha);
- }
-
- inline color_quad_u8(parameter_t red, parameter_t green, parameter_t blue, parameter_t alpha = component_traits::cMax)
- {
- set(red, green, blue, alpha);
- }
-
- explicit inline color_quad_u8(eNoClamp, parameter_t y, parameter_t alpha = component_traits::cMax)
- {
- set_noclamp_y_alpha(y, alpha);
- }
-
- inline color_quad_u8(eNoClamp, parameter_t red, parameter_t green, parameter_t blue, parameter_t alpha = component_traits::cMax)
- {
- set_noclamp_rgba(red, green, blue, alpha);
- }
-
- inline void clear()
- {
- m_u32 = 0;
- }
-
- inline color_quad_u8& operator= (const color_quad_u8& other)
- {
- m_u32 = other.m_u32;
- return *this;
- }
-
- inline color_quad_u8& set_rgb(const color_quad_u8& other)
- {
- r = other.r;
- g = other.g;
- b = other.b;
- return *this;
- }
-
- inline color_quad_u8& operator= (parameter_t y)
- {
- set(y, component_traits::cMax);
- return *this;
- }
-
- inline color_quad_u8& set(parameter_t y, parameter_t alpha = component_traits::cMax)
- {
- y = clamp(y);
- alpha = clamp(alpha);
- r = static_cast<component_t>(y);
- g = static_cast<component_t>(y);
- b = static_cast<component_t>(y);
- a = static_cast<component_t>(alpha);
- return *this;
- }
-
- inline color_quad_u8& set_noclamp_y_alpha(parameter_t y, parameter_t alpha = component_traits::cMax)
- {
- RG_ETC1_ASSERT( (y >= component_traits::cMin) && (y <= component_traits::cMax) );
- RG_ETC1_ASSERT( (alpha >= component_traits::cMin) && (alpha <= component_traits::cMax) );
-
- r = static_cast<component_t>(y);
- g = static_cast<component_t>(y);
- b = static_cast<component_t>(y);
- a = static_cast<component_t>(alpha);
- return *this;
- }
-
- inline color_quad_u8& set(parameter_t red, parameter_t green, parameter_t blue, parameter_t alpha = component_traits::cMax)
- {
- r = static_cast<component_t>(clamp(red));
- g = static_cast<component_t>(clamp(green));
- b = static_cast<component_t>(clamp(blue));
- a = static_cast<component_t>(clamp(alpha));
- return *this;
- }
-
- inline color_quad_u8& set_noclamp_rgba(parameter_t red, parameter_t green, parameter_t blue, parameter_t alpha)
- {
- RG_ETC1_ASSERT( (red >= component_traits::cMin) && (red <= component_traits::cMax) );
- RG_ETC1_ASSERT( (green >= component_traits::cMin) && (green <= component_traits::cMax) );
- RG_ETC1_ASSERT( (blue >= component_traits::cMin) && (blue <= component_traits::cMax) );
- RG_ETC1_ASSERT( (alpha >= component_traits::cMin) && (alpha <= component_traits::cMax) );
-
- r = static_cast<component_t>(red);
- g = static_cast<component_t>(green);
- b = static_cast<component_t>(blue);
- a = static_cast<component_t>(alpha);
- return *this;
- }
-
- inline color_quad_u8& set_noclamp_rgb(parameter_t red, parameter_t green, parameter_t blue)
- {
- RG_ETC1_ASSERT( (red >= component_traits::cMin) && (red <= component_traits::cMax) );
- RG_ETC1_ASSERT( (green >= component_traits::cMin) && (green <= component_traits::cMax) );
- RG_ETC1_ASSERT( (blue >= component_traits::cMin) && (blue <= component_traits::cMax) );
-
- r = static_cast<component_t>(red);
- g = static_cast<component_t>(green);
- b = static_cast<component_t>(blue);
- return *this;
- }
-
- static inline parameter_t get_min_comp() { return component_traits::cMin; }
- static inline parameter_t get_max_comp() { return component_traits::cMax; }
- static inline bool get_comps_are_signed() { return component_traits::cSigned; }
-
- inline component_t operator[] (uint i) const { RG_ETC1_ASSERT(i < cNumComps); return c[i]; }
- inline component_t& operator[] (uint i) { RG_ETC1_ASSERT(i < cNumComps); return c[i]; }
-
- inline color_quad_u8& set_component(uint i, parameter_t f)
- {
- RG_ETC1_ASSERT(i < cNumComps);
-
- c[i] = static_cast<component_t>(clamp(f));
-
- return *this;
- }
-
- inline color_quad_u8& set_grayscale(parameter_t l)
- {
- component_t x = static_cast<component_t>(clamp(l));
- c[0] = x;
- c[1] = x;
- c[2] = x;
- return *this;
- }
-
- inline color_quad_u8& clamp(const color_quad_u8& l, const color_quad_u8& h)
- {
- for (uint i = 0; i < cNumComps; i++)
- c[i] = static_cast<component_t>(rg_etc1::clamp<parameter_t>(c[i], l[i], h[i]));
- return *this;
- }
-
- inline color_quad_u8& clamp(parameter_t l, parameter_t h)
- {
- for (uint i = 0; i < cNumComps; i++)
- c[i] = static_cast<component_t>(rg_etc1::clamp<parameter_t>(c[i], l, h));
- return *this;
- }
-
- // Returns CCIR 601 luma (consistent with color_utils::RGB_To_Y).
- inline parameter_t get_luma() const
- {
- return static_cast<parameter_t>((19595U * r + 38470U * g + 7471U * b + 32768U) >> 16U);
- }
-
- // Returns REC 709 luma.
- inline parameter_t get_luma_rec709() const
- {
- return static_cast<parameter_t>((13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U);
- }
-
- inline uint squared_distance_rgb(const color_quad_u8& c) const
- {
- return rg_etc1::square(r - c.r) + rg_etc1::square(g - c.g) + rg_etc1::square(b - c.b);
- }
-
- inline uint squared_distance_rgba(const color_quad_u8& c) const
- {
- return rg_etc1::square(r - c.r) + rg_etc1::square(g - c.g) + rg_etc1::square(b - c.b) + rg_etc1::square(a - c.a);
- }
-
- inline bool rgb_equals(const color_quad_u8& rhs) const
- {
- return (r == rhs.r) && (g == rhs.g) && (b == rhs.b);
- }
-
- inline bool operator== (const color_quad_u8& rhs) const
- {
- return m_u32 == rhs.m_u32;
- }
-
- color_quad_u8& operator+= (const color_quad_u8& other)
- {
- for (uint i = 0; i < 4; i++)
- c[i] = static_cast<component_t>(clamp(c[i] + other.c[i]));
- return *this;
- }
-
- color_quad_u8& operator-= (const color_quad_u8& other)
- {
- for (uint i = 0; i < 4; i++)
- c[i] = static_cast<component_t>(clamp(c[i] - other.c[i]));
- return *this;
- }
-
- friend color_quad_u8 operator+ (const color_quad_u8& lhs, const color_quad_u8& rhs)
- {
- color_quad_u8 result(lhs);
- result += rhs;
- return result;
- }
-
- friend color_quad_u8 operator- (const color_quad_u8& lhs, const color_quad_u8& rhs)
- {
- color_quad_u8 result(lhs);
- result -= rhs;
- return result;
- }
- }; // class color_quad_u8
-
- struct vec3F
- {
- float m_s[3];
-
- inline vec3F() { }
- inline vec3F(float s) { m_s[0] = s; m_s[1] = s; m_s[2] = s; }
- inline vec3F(float x, float y, float z) { m_s[0] = x; m_s[1] = y; m_s[2] = z; }
-
- inline float operator[] (uint i) const { RG_ETC1_ASSERT(i < 3); return m_s[i]; }
-
- inline vec3F& operator += (const vec3F& other) { for (uint i = 0; i < 3; i++) m_s[i] += other.m_s[i]; return *this; }
-
- inline vec3F& operator *= (float s) { for (uint i = 0; i < 3; i++) m_s[i] *= s; return *this; }
- };
-
- enum etc_constants
- {
- cETC1BytesPerBlock = 8U,
-
- cETC1SelectorBits = 2U,
- cETC1SelectorValues = 1U << cETC1SelectorBits,
- cETC1SelectorMask = cETC1SelectorValues - 1U,
-
- cETC1BlockShift = 2U,
- cETC1BlockSize = 1U << cETC1BlockShift,
-
- cETC1LSBSelectorIndicesBitOffset = 0,
- cETC1MSBSelectorIndicesBitOffset = 16,
-
- cETC1FlipBitOffset = 32,
- cETC1DiffBitOffset = 33,
-
- cETC1IntenModifierNumBits = 3,
- cETC1IntenModifierValues = 1 << cETC1IntenModifierNumBits,
- cETC1RightIntenModifierTableBitOffset = 34,
- cETC1LeftIntenModifierTableBitOffset = 37,
-
- // Base+Delta encoding (5 bit bases, 3 bit delta)
- cETC1BaseColorCompNumBits = 5,
- cETC1BaseColorCompMax = 1 << cETC1BaseColorCompNumBits,
-
- cETC1DeltaColorCompNumBits = 3,
- cETC1DeltaColorComp = 1 << cETC1DeltaColorCompNumBits,
- cETC1DeltaColorCompMax = 1 << cETC1DeltaColorCompNumBits,
-
- cETC1BaseColor5RBitOffset = 59,
- cETC1BaseColor5GBitOffset = 51,
- cETC1BaseColor5BBitOffset = 43,
-
- cETC1DeltaColor3RBitOffset = 56,
- cETC1DeltaColor3GBitOffset = 48,
- cETC1DeltaColor3BBitOffset = 40,
-
- // Absolute (non-delta) encoding (two 4-bit per component bases)
- cETC1AbsColorCompNumBits = 4,
- cETC1AbsColorCompMax = 1 << cETC1AbsColorCompNumBits,
-
- cETC1AbsColor4R1BitOffset = 60,
- cETC1AbsColor4G1BitOffset = 52,
- cETC1AbsColor4B1BitOffset = 44,
-
- cETC1AbsColor4R2BitOffset = 56,
- cETC1AbsColor4G2BitOffset = 48,
- cETC1AbsColor4B2BitOffset = 40,
-
- cETC1ColorDeltaMin = -4,
- cETC1ColorDeltaMax = 3,
-
- // Delta3:
- // 0 1 2 3 4 5 6 7
- // 000 001 010 011 100 101 110 111
- // 0 1 2 3 -4 -3 -2 -1
- };
-
- static uint8 g_quant5_tab[256+16];
-
- static const int g_etc1_inten_tables[cETC1IntenModifierValues][cETC1SelectorValues] =
- {
- { -8, -2, 2, 8 }, { -17, -5, 5, 17 }, { -29, -9, 9, 29 }, { -42, -13, 13, 42 },
- { -60, -18, 18, 60 }, { -80, -24, 24, 80 }, { -106, -33, 33, 106 }, { -183, -47, 47, 183 }
- };
-
- static const uint8 g_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };
- static const uint8 g_selector_index_to_etc1[cETC1SelectorValues] = { 3, 2, 0, 1 };
-
- // Given an ETC1 diff/inten_table/selector, and an 8-bit desired color, this table encodes the best packed_color in the low byte, and the abs error in the high byte.
- static uint16 g_etc1_inverse_lookup[2*8*4][256]; // [diff/inten_table/selector][desired_color]
-
- // g_color8_to_etc_block_config[color][table_index] = Supplies for each 8-bit color value a list of packed ETC1 diff/intensity table/selectors/packed_colors that map to that color.
- // To pack: diff | (inten << 1) | (selector << 4) | (packed_c << 8)
- static const uint16 g_color8_to_etc_block_config_0_255[2][33] =
- {
- { 0x0000, 0x0010, 0x0002, 0x0012, 0x0004, 0x0014, 0x0006, 0x0016, 0x0008, 0x0018, 0x000A, 0x001A, 0x000C, 0x001C, 0x000E, 0x001E,
- 0x0001, 0x0011, 0x0003, 0x0013, 0x0005, 0x0015, 0x0007, 0x0017, 0x0009, 0x0019, 0x000B, 0x001B, 0x000D, 0x001D, 0x000F, 0x001F, 0xFFFF },
- { 0x0F20, 0x0F30, 0x0E32, 0x0F22, 0x0E34, 0x0F24, 0x0D36, 0x0F26, 0x0C38, 0x0E28, 0x0B3A, 0x0E2A, 0x093C, 0x0E2C, 0x053E, 0x0D2E,
- 0x1E31, 0x1F21, 0x1D33, 0x1F23, 0x1C35, 0x1E25, 0x1A37, 0x1E27, 0x1839, 0x1D29, 0x163B, 0x1C2B, 0x133D, 0x1B2D, 0x093F, 0x1A2F, 0xFFFF },
- };
-
- // Really only [254][11].
- static const uint16 g_color8_to_etc_block_config_1_to_254[254][12] =
- {
- { 0x021C, 0x0D0D, 0xFFFF }, { 0x0020, 0x0021, 0x0A0B, 0x061F, 0xFFFF }, { 0x0113, 0x0217, 0xFFFF }, { 0x0116, 0x031E,
- 0x0B0E, 0x0405, 0xFFFF }, { 0x0022, 0x0204, 0x050A, 0x0023, 0xFFFF }, { 0x0111, 0x0319, 0x0809, 0x170F, 0xFFFF }, {
- 0x0303, 0x0215, 0x0607, 0xFFFF }, { 0x0030, 0x0114, 0x0408, 0x0031, 0x0201, 0x051D, 0xFFFF }, { 0x0100, 0x0024, 0x0306,
- 0x0025, 0x041B, 0x0E0D, 0xFFFF }, { 0x021A, 0x0121, 0x0B0B, 0x071F, 0xFFFF }, { 0x0213, 0x0317, 0xFFFF }, { 0x0112,
- 0x0505, 0xFFFF }, { 0x0026, 0x070C, 0x0123, 0x0027, 0xFFFF }, { 0x0211, 0x0909, 0xFFFF }, { 0x0110, 0x0315, 0x0707,
- 0x0419, 0x180F, 0xFFFF }, { 0x0218, 0x0131, 0x0301, 0x0403, 0x061D, 0xFFFF }, { 0x0032, 0x0202, 0x0033, 0x0125, 0x051B,
- 0x0F0D, 0xFFFF }, { 0x0028, 0x031C, 0x0221, 0x0029, 0xFFFF }, { 0x0120, 0x0313, 0x0C0B, 0x081F, 0xFFFF }, { 0x0605,
- 0x0417, 0xFFFF }, { 0x0216, 0x041E, 0x0C0E, 0x0223, 0x0127, 0xFFFF }, { 0x0122, 0x0304, 0x060A, 0x0311, 0x0A09, 0xFFFF
- }, { 0x0519, 0x190F, 0xFFFF }, { 0x002A, 0x0231, 0x0503, 0x0415, 0x0807, 0x002B, 0x071D, 0xFFFF }, { 0x0130, 0x0214,
- 0x0508, 0x0401, 0x0133, 0x0225, 0x061B, 0xFFFF }, { 0x0200, 0x0124, 0x0406, 0x0321, 0x0129, 0x100D, 0xFFFF }, { 0x031A,
- 0x0D0B, 0x091F, 0xFFFF }, { 0x0413, 0x0705, 0x0517, 0xFFFF }, { 0x0212, 0x0034, 0x0323, 0x0035, 0x0227, 0xFFFF }, {
- 0x0126, 0x080C, 0x0B09, 0xFFFF }, { 0x0411, 0x0619, 0x1A0F, 0xFFFF }, { 0x0210, 0x0331, 0x0603, 0x0515, 0x0907, 0x012B,
- 0xFFFF }, { 0x0318, 0x002C, 0x0501, 0x0233, 0x0325, 0x071B, 0x002D, 0x081D, 0xFFFF }, { 0x0132, 0x0302, 0x0229, 0x110D,
- 0xFFFF }, { 0x0128, 0x041C, 0x0421, 0x0E0B, 0x0A1F, 0xFFFF }, { 0x0220, 0x0513, 0x0617, 0xFFFF }, { 0x0135, 0x0805,
- 0x0327, 0xFFFF }, { 0x0316, 0x051E, 0x0D0E, 0x0423, 0xFFFF }, { 0x0222, 0x0404, 0x070A, 0x0511, 0x0719, 0x0C09, 0x1B0F,
- 0xFFFF }, { 0x0703, 0x0615, 0x0A07, 0x022B, 0xFFFF }, { 0x012A, 0x0431, 0x0601, 0x0333, 0x012D, 0x091D, 0xFFFF }, {
- 0x0230, 0x0314, 0x0036, 0x0608, 0x0425, 0x0037, 0x0329, 0x081B, 0x120D, 0xFFFF }, { 0x0300, 0x0224, 0x0506, 0x0521,
- 0x0F0B, 0x0B1F, 0xFFFF }, { 0x041A, 0x0613, 0x0717, 0xFFFF }, { 0x0235, 0x0905, 0xFFFF }, { 0x0312, 0x0134, 0x0523,
- 0x0427, 0xFFFF }, { 0x0226, 0x090C, 0x002E, 0x0611, 0x0D09, 0x002F, 0xFFFF }, { 0x0715, 0x0B07, 0x0819, 0x032B, 0x1C0F,
- 0xFFFF }, { 0x0310, 0x0531, 0x0701, 0x0803, 0x022D, 0x0A1D, 0xFFFF }, { 0x0418, 0x012C, 0x0433, 0x0525, 0x0137, 0x091B,
- 0x130D, 0xFFFF }, { 0x0232, 0x0402, 0x0621, 0x0429, 0xFFFF }, { 0x0228, 0x051C, 0x0713, 0x100B, 0x0C1F, 0xFFFF }, {
- 0x0320, 0x0335, 0x0A05, 0x0817, 0xFFFF }, { 0x0623, 0x0527, 0xFFFF }, { 0x0416, 0x061E, 0x0E0E, 0x0711, 0x0E09, 0x012F,
- 0xFFFF }, { 0x0322, 0x0504, 0x080A, 0x0919, 0x1D0F, 0xFFFF }, { 0x0631, 0x0903, 0x0815, 0x0C07, 0x042B, 0x032D, 0x0B1D,
- 0xFFFF }, { 0x022A, 0x0801, 0x0533, 0x0625, 0x0237, 0x0A1B, 0xFFFF }, { 0x0330, 0x0414, 0x0136, 0x0708, 0x0721, 0x0529,
- 0x140D, 0xFFFF }, { 0x0400, 0x0324, 0x0606, 0x0038, 0x0039, 0x110B, 0x0D1F, 0xFFFF }, { 0x051A, 0x0813, 0x0B05, 0x0917,
- 0xFFFF }, { 0x0723, 0x0435, 0x0627, 0xFFFF }, { 0x0412, 0x0234, 0x0F09, 0x022F, 0xFFFF }, { 0x0326, 0x0A0C, 0x012E,
- 0x0811, 0x0A19, 0x1E0F, 0xFFFF }, { 0x0731, 0x0A03, 0x0915, 0x0D07, 0x052B, 0xFFFF }, { 0x0410, 0x0901, 0x0633, 0x0725,
- 0x0337, 0x0B1B, 0x042D, 0x0C1D, 0xFFFF }, { 0x0518, 0x022C, 0x0629, 0x150D, 0xFFFF }, { 0x0332, 0x0502, 0x0821, 0x0139,
- 0x120B, 0x0E1F, 0xFFFF }, { 0x0328, 0x061C, 0x0913, 0x0A17, 0xFFFF }, { 0x0420, 0x0535, 0x0C05, 0x0727, 0xFFFF }, {
- 0x0823, 0x032F, 0xFFFF }, { 0x0516, 0x071E, 0x0F0E, 0x0911, 0x0B19, 0x1009, 0x1F0F, 0xFFFF }, { 0x0422, 0x0604, 0x090A,
- 0x0B03, 0x0A15, 0x0E07, 0x062B, 0xFFFF }, { 0x0831, 0x0A01, 0x0733, 0x052D, 0x0D1D, 0xFFFF }, { 0x032A, 0x0825, 0x0437,
- 0x0729, 0x0C1B, 0x160D, 0xFFFF }, { 0x0430, 0x0514, 0x0236, 0x0808, 0x0921, 0x0239, 0x130B, 0x0F1F, 0xFFFF }, { 0x0500,
- 0x0424, 0x0706, 0x0138, 0x0A13, 0x0B17, 0xFFFF }, { 0x061A, 0x0635, 0x0D05, 0xFFFF }, { 0x0923, 0x0827, 0xFFFF }, {
- 0x0512, 0x0334, 0x003A, 0x0A11, 0x1109, 0x003B, 0x042F, 0xFFFF }, { 0x0426, 0x0B0C, 0x022E, 0x0B15, 0x0F07, 0x0C19,
- 0x072B, 0xFFFF }, { 0x0931, 0x0B01, 0x0C03, 0x062D, 0x0E1D, 0xFFFF }, { 0x0510, 0x0833, 0x0925, 0x0537, 0x0D1B, 0x170D,
- 0xFFFF }, { 0x0618, 0x032C, 0x0A21, 0x0339, 0x0829, 0xFFFF }, { 0x0432, 0x0602, 0x0B13, 0x140B, 0x101F, 0xFFFF }, {
- 0x0428, 0x071C, 0x0735, 0x0E05, 0x0C17, 0xFFFF }, { 0x0520, 0x0A23, 0x0927, 0xFFFF }, { 0x0B11, 0x1209, 0x013B, 0x052F,
- 0xFFFF }, { 0x0616, 0x081E, 0x0D19, 0xFFFF }, { 0x0522, 0x0704, 0x0A0A, 0x0A31, 0x0D03, 0x0C15, 0x1007, 0x082B, 0x072D,
- 0x0F1D, 0xFFFF }, { 0x0C01, 0x0933, 0x0A25, 0x0637, 0x0E1B, 0xFFFF }, { 0x042A, 0x0B21, 0x0929, 0x180D, 0xFFFF }, {
- 0x0530, 0x0614, 0x0336, 0x0908, 0x0439, 0x150B, 0x111F, 0xFFFF }, { 0x0600, 0x0524, 0x0806, 0x0238, 0x0C13, 0x0F05,
- 0x0D17, 0xFFFF }, { 0x071A, 0x0B23, 0x0835, 0x0A27, 0xFFFF }, { 0x1309, 0x023B, 0x062F, 0xFFFF }, { 0x0612, 0x0434,
- 0x013A, 0x0C11, 0x0E19, 0xFFFF }, { 0x0526, 0x0C0C, 0x032E, 0x0B31, 0x0E03, 0x0D15, 0x1107, 0x092B, 0xFFFF }, { 0x0D01,
- 0x0A33, 0x0B25, 0x0737, 0x0F1B, 0x082D, 0x101D, 0xFFFF }, { 0x0610, 0x0A29, 0x190D, 0xFFFF }, { 0x0718, 0x042C, 0x0C21,
- 0x0539, 0x160B, 0x121F, 0xFFFF }, { 0x0532, 0x0702, 0x0D13, 0x0E17, 0xFFFF }, { 0x0528, 0x081C, 0x0935, 0x1005, 0x0B27,
- 0xFFFF }, { 0x0620, 0x0C23, 0x033B, 0x072F, 0xFFFF }, { 0x0D11, 0x0F19, 0x1409, 0xFFFF }, { 0x0716, 0x003C, 0x091E,
- 0x0F03, 0x0E15, 0x1207, 0x0A2B, 0x003D, 0xFFFF }, { 0x0622, 0x0804, 0x0B0A, 0x0C31, 0x0E01, 0x0B33, 0x092D, 0x111D,
- 0xFFFF }, { 0x0C25, 0x0837, 0x0B29, 0x101B, 0x1A0D, 0xFFFF }, { 0x052A, 0x0D21, 0x0639, 0x170B, 0x131F, 0xFFFF }, {
- 0x0630, 0x0714, 0x0436, 0x0A08, 0x0E13, 0x0F17, 0xFFFF }, { 0x0700, 0x0624, 0x0906, 0x0338, 0x0A35, 0x1105, 0xFFFF }, {
- 0x081A, 0x0D23, 0x0C27, 0xFFFF }, { 0x0E11, 0x1509, 0x043B, 0x082F, 0xFFFF }, { 0x0712, 0x0534, 0x023A, 0x0F15, 0x1307,
- 0x1019, 0x0B2B, 0x013D, 0xFFFF }, { 0x0626, 0x0D0C, 0x042E, 0x0D31, 0x0F01, 0x1003, 0x0A2D, 0x121D, 0xFFFF }, { 0x0C33,
- 0x0D25, 0x0937, 0x111B, 0x1B0D, 0xFFFF }, { 0x0710, 0x0E21, 0x0739, 0x0C29, 0xFFFF }, { 0x0818, 0x052C, 0x0F13, 0x180B,
- 0x141F, 0xFFFF }, { 0x0632, 0x0802, 0x0B35, 0x1205, 0x1017, 0xFFFF }, { 0x0628, 0x091C, 0x0E23, 0x0D27, 0xFFFF }, {
- 0x0720, 0x0F11, 0x1609, 0x053B, 0x092F, 0xFFFF }, { 0x1119, 0x023D, 0xFFFF }, { 0x0816, 0x013C, 0x0A1E, 0x0E31, 0x1103,
- 0x1015, 0x1407, 0x0C2B, 0x0B2D, 0x131D, 0xFFFF }, { 0x0722, 0x0904, 0x0C0A, 0x1001, 0x0D33, 0x0E25, 0x0A37, 0x121B,
- 0xFFFF }, { 0x0F21, 0x0D29, 0x1C0D, 0xFFFF }, { 0x062A, 0x0839, 0x190B, 0x151F, 0xFFFF }, { 0x0730, 0x0814, 0x0536,
- 0x0B08, 0x1013, 0x1305, 0x1117, 0xFFFF }, { 0x0800, 0x0724, 0x0A06, 0x0438, 0x0F23, 0x0C35, 0x0E27, 0xFFFF }, { 0x091A,
- 0x1709, 0x063B, 0x0A2F, 0xFFFF }, { 0x1011, 0x1219, 0x033D, 0xFFFF }, { 0x0812, 0x0634, 0x033A, 0x0F31, 0x1203, 0x1115,
- 0x1507, 0x0D2B, 0xFFFF }, { 0x0726, 0x0E0C, 0x052E, 0x1101, 0x0E33, 0x0F25, 0x0B37, 0x131B, 0x0C2D, 0x141D, 0xFFFF }, {
- 0x0E29, 0x1D0D, 0xFFFF }, { 0x0810, 0x1021, 0x0939, 0x1A0B, 0x161F, 0xFFFF }, { 0x0918, 0x062C, 0x1113, 0x1217, 0xFFFF
- }, { 0x0732, 0x0902, 0x0D35, 0x1405, 0x0F27, 0xFFFF }, { 0x0728, 0x0A1C, 0x1023, 0x073B, 0x0B2F, 0xFFFF }, { 0x0820,
- 0x1111, 0x1319, 0x1809, 0xFFFF }, { 0x1303, 0x1215, 0x1607, 0x0E2B, 0x043D, 0xFFFF }, { 0x0916, 0x023C, 0x0B1E, 0x1031,
- 0x1201, 0x0F33, 0x0D2D, 0x151D, 0xFFFF }, { 0x0822, 0x0A04, 0x0D0A, 0x1025, 0x0C37, 0x0F29, 0x141B, 0x1E0D, 0xFFFF }, {
- 0x1121, 0x0A39, 0x1B0B, 0x171F, 0xFFFF }, { 0x072A, 0x1213, 0x1317, 0xFFFF }, { 0x0830, 0x0914, 0x0636, 0x0C08, 0x0E35,
- 0x1505, 0xFFFF }, { 0x0900, 0x0824, 0x0B06, 0x0538, 0x1123, 0x1027, 0xFFFF }, { 0x0A1A, 0x1211, 0x1909, 0x083B, 0x0C2F,
- 0xFFFF }, { 0x1315, 0x1707, 0x1419, 0x0F2B, 0x053D, 0xFFFF }, { 0x0912, 0x0734, 0x043A, 0x1131, 0x1301, 0x1403, 0x0E2D,
- 0x161D, 0xFFFF }, { 0x0826, 0x0F0C, 0x062E, 0x1033, 0x1125, 0x0D37, 0x151B, 0x1F0D, 0xFFFF }, { 0x1221, 0x0B39, 0x1029,
- 0xFFFF }, { 0x0910, 0x1313, 0x1C0B, 0x181F, 0xFFFF }, { 0x0A18, 0x072C, 0x0F35, 0x1605, 0x1417, 0xFFFF }, { 0x0832,
- 0x0A02, 0x1223, 0x1127, 0xFFFF }, { 0x0828, 0x0B1C, 0x1311, 0x1A09, 0x093B, 0x0D2F, 0xFFFF }, { 0x0920, 0x1519, 0x063D,
- 0xFFFF }, { 0x1231, 0x1503, 0x1415, 0x1807, 0x102B, 0x0F2D, 0x171D, 0xFFFF }, { 0x0A16, 0x033C, 0x0C1E, 0x1401, 0x1133,
- 0x1225, 0x0E37, 0x161B, 0xFFFF }, { 0x0922, 0x0B04, 0x0E0A, 0x1321, 0x1129, 0xFFFF }, { 0x0C39, 0x1D0B, 0x191F, 0xFFFF
- }, { 0x082A, 0x1413, 0x1705, 0x1517, 0xFFFF }, { 0x0930, 0x0A14, 0x0736, 0x0D08, 0x1323, 0x1035, 0x1227, 0xFFFF }, {
- 0x0A00, 0x0924, 0x0C06, 0x0638, 0x1B09, 0x0A3B, 0x0E2F, 0xFFFF }, { 0x0B1A, 0x1411, 0x1619, 0x073D, 0xFFFF }, { 0x1331,
- 0x1603, 0x1515, 0x1907, 0x112B, 0xFFFF }, { 0x0A12, 0x0834, 0x053A, 0x1501, 0x1233, 0x1325, 0x0F37, 0x171B, 0x102D,
- 0x181D, 0xFFFF }, { 0x0926, 0x072E, 0x1229, 0xFFFF }, { 0x1421, 0x0D39, 0x1E0B, 0x1A1F, 0xFFFF }, { 0x0A10, 0x1513,
- 0x1617, 0xFFFF }, { 0x0B18, 0x082C, 0x1135, 0x1805, 0x1327, 0xFFFF }, { 0x0932, 0x0B02, 0x1423, 0x0B3B, 0x0F2F, 0xFFFF
- }, { 0x0928, 0x0C1C, 0x1511, 0x1719, 0x1C09, 0xFFFF }, { 0x0A20, 0x1703, 0x1615, 0x1A07, 0x122B, 0x083D, 0xFFFF }, {
- 0x1431, 0x1601, 0x1333, 0x112D, 0x191D, 0xFFFF }, { 0x0B16, 0x043C, 0x0D1E, 0x1425, 0x1037, 0x1329, 0x181B, 0xFFFF }, {
- 0x0A22, 0x0C04, 0x0F0A, 0x1521, 0x0E39, 0x1F0B, 0x1B1F, 0xFFFF }, { 0x1613, 0x1717, 0xFFFF }, { 0x092A, 0x1235, 0x1905,
- 0xFFFF }, { 0x0A30, 0x0B14, 0x0836, 0x0E08, 0x1523, 0x1427, 0xFFFF }, { 0x0B00, 0x0A24, 0x0D06, 0x0738, 0x1611, 0x1D09,
- 0x0C3B, 0x102F, 0xFFFF }, { 0x0C1A, 0x1715, 0x1B07, 0x1819, 0x132B, 0x093D, 0xFFFF }, { 0x1531, 0x1701, 0x1803, 0x122D,
- 0x1A1D, 0xFFFF }, { 0x0B12, 0x0934, 0x063A, 0x1433, 0x1525, 0x1137, 0x191B, 0xFFFF }, { 0x0A26, 0x003E, 0x082E, 0x1621,
- 0x0F39, 0x1429, 0x003F, 0xFFFF }, { 0x1713, 0x1C1F, 0xFFFF }, { 0x0B10, 0x1335, 0x1A05, 0x1817, 0xFFFF }, { 0x0C18,
- 0x092C, 0x1623, 0x1527, 0xFFFF }, { 0x0A32, 0x0C02, 0x1711, 0x1E09, 0x0D3B, 0x112F, 0xFFFF }, { 0x0A28, 0x0D1C, 0x1919,
- 0x0A3D, 0xFFFF }, { 0x0B20, 0x1631, 0x1903, 0x1815, 0x1C07, 0x142B, 0x132D, 0x1B1D, 0xFFFF }, { 0x1801, 0x1533, 0x1625,
- 0x1237, 0x1A1B, 0xFFFF }, { 0x0C16, 0x053C, 0x0E1E, 0x1721, 0x1529, 0x013F, 0xFFFF }, { 0x0B22, 0x0D04, 0x1039, 0x1D1F,
- 0xFFFF }, { 0x1813, 0x1B05, 0x1917, 0xFFFF }, { 0x0A2A, 0x1723, 0x1435, 0x1627, 0xFFFF }, { 0x0B30, 0x0C14, 0x0936,
- 0x0F08, 0x1F09, 0x0E3B, 0x122F, 0xFFFF }, { 0x0C00, 0x0B24, 0x0E06, 0x0838, 0x1811, 0x1A19, 0x0B3D, 0xFFFF }, { 0x0D1A,
- 0x1731, 0x1A03, 0x1915, 0x1D07, 0x152B, 0xFFFF }, { 0x1901, 0x1633, 0x1725, 0x1337, 0x1B1B, 0x142D, 0x1C1D, 0xFFFF }, {
- 0x0C12, 0x0A34, 0x073A, 0x1629, 0x023F, 0xFFFF }, { 0x0B26, 0x013E, 0x092E, 0x1821, 0x1139, 0x1E1F, 0xFFFF }, { 0x1913,
- 0x1A17, 0xFFFF }, { 0x0C10, 0x1535, 0x1C05, 0x1727, 0xFFFF }, { 0x0D18, 0x0A2C, 0x1823, 0x0F3B, 0x132F, 0xFFFF }, {
- 0x0B32, 0x0D02, 0x1911, 0x1B19, 0xFFFF }, { 0x0B28, 0x0E1C, 0x1B03, 0x1A15, 0x1E07, 0x162B, 0x0C3D, 0xFFFF }, { 0x0C20,
- 0x1831, 0x1A01, 0x1733, 0x152D, 0x1D1D, 0xFFFF }, { 0x1825, 0x1437, 0x1729, 0x1C1B, 0x033F, 0xFFFF }, { 0x0D16, 0x063C,
- 0x0F1E, 0x1921, 0x1239, 0x1F1F, 0xFFFF }, { 0x0C22, 0x0E04, 0x1A13, 0x1B17, 0xFFFF }, { 0x1635, 0x1D05, 0xFFFF }, {
- 0x0B2A, 0x1923, 0x1827, 0xFFFF }, { 0x0C30, 0x0D14, 0x0A36, 0x1A11, 0x103B, 0x142F, 0xFFFF }, { 0x0D00, 0x0C24, 0x0F06,
- 0x0938, 0x1B15, 0x1F07, 0x1C19, 0x172B, 0x0D3D, 0xFFFF }, { 0x0E1A, 0x1931, 0x1B01, 0x1C03, 0x162D, 0x1E1D, 0xFFFF }, {
- 0x1833, 0x1925, 0x1537, 0x1D1B, 0xFFFF }, { 0x0D12, 0x0B34, 0x083A, 0x1A21, 0x1339, 0x1829, 0x043F, 0xFFFF }, { 0x0C26,
- 0x023E, 0x0A2E, 0x1B13, 0xFFFF }, { 0x1735, 0x1E05, 0x1C17, 0xFFFF }, { 0x0D10, 0x1A23, 0x1927, 0xFFFF }, { 0x0E18,
- 0x0B2C, 0x1B11, 0x113B, 0x152F, 0xFFFF }, { 0x0C32, 0x0E02, 0x1D19, 0x0E3D, 0xFFFF }, { 0x0C28, 0x0F1C, 0x1A31, 0x1D03,
- 0x1C15, 0x182B, 0x172D, 0x1F1D, 0xFFFF }, { 0x0D20, 0x1C01, 0x1933, 0x1A25, 0x1637, 0x1E1B, 0xFFFF }, { 0x1B21, 0x1929,
- 0x053F, 0xFFFF }, { 0x0E16, 0x073C, 0x1439, 0xFFFF }, { 0x0D22, 0x0F04, 0x1C13, 0x1F05, 0x1D17, 0xFFFF }, { 0x1B23,
- 0x1835, 0x1A27, 0xFFFF }, { 0x0C2A, 0x123B, 0x162F, 0xFFFF }, { 0x0D30, 0x0E14, 0x0B36, 0x1C11, 0x1E19, 0x0F3D, 0xFFFF
- }, { 0x0E00, 0x0D24, 0x0A38, 0x1B31, 0x1E03, 0x1D15, 0x192B, 0xFFFF }, { 0x0F1A, 0x1D01, 0x1A33, 0x1B25, 0x1737, 0x1F1B,
- 0x182D, 0xFFFF }, { 0x1A29, 0x063F, 0xFFFF }, { 0x0E12, 0x0C34, 0x093A, 0x1C21, 0x1539, 0xFFFF }, { 0x0D26, 0x033E,
- 0x0B2E, 0x1D13, 0x1E17, 0xFFFF }, { 0x1935, 0x1B27, 0xFFFF }, { 0x0E10, 0x1C23, 0x133B, 0x172F, 0xFFFF }, { 0x0F18,
- 0x0C2C, 0x1D11, 0x1F19, 0xFFFF }, { 0x0D32, 0x0F02, 0x1F03, 0x1E15, 0x1A2B, 0x103D, 0xFFFF }, { 0x0D28, 0x1C31, 0x1E01,
- 0x1B33, 0x192D, 0xFFFF }, { 0x0E20, 0x1C25, 0x1837, 0x1B29, 0x073F, 0xFFFF }, { 0x1D21, 0x1639, 0xFFFF }, { 0x0F16,
- 0x083C, 0x1E13, 0x1F17, 0xFFFF }, { 0x0E22, 0x1A35, 0xFFFF }, { 0x1D23, 0x1C27, 0xFFFF }, { 0x0D2A, 0x1E11, 0x143B,
- 0x182F, 0xFFFF }, { 0x0E30, 0x0F14, 0x0C36, 0x1F15, 0x1B2B, 0x113D, 0xFFFF }, { 0x0F00, 0x0E24, 0x0B38, 0x1D31, 0x1F01,
- 0x1A2D, 0xFFFF }, { 0x1C33, 0x1D25, 0x1937, 0xFFFF }, { 0x1E21, 0x1739, 0x1C29, 0x083F, 0xFFFF }, { 0x0F12, 0x0D34,
- 0x0A3A, 0x1F13, 0xFFFF }, { 0x0E26, 0x043E, 0x0C2E, 0x1B35, 0xFFFF }, { 0x1E23, 0x1D27, 0xFFFF }, { 0x0F10, 0x1F11,
- 0x153B, 0x192F, 0xFFFF }, { 0x0D2C, 0x123D, 0xFFFF },
- };
-
- struct etc1_block
- {
- // big endian uint64:
- // bit ofs: 56 48 40 32 24 16 8 0
- // byte ofs: b0, b1, b2, b3, b4, b5, b6, b7
- union
- {
- uint64 m_uint64;
- uint8 m_bytes[8];
- };
-
- uint8 m_low_color[2];
- uint8 m_high_color[2];
-
- enum { cNumSelectorBytes = 4 };
- uint8 m_selectors[cNumSelectorBytes];
-
- inline void clear()
- {
- zero_this(this);
- }
-
- inline uint get_byte_bits(uint ofs, uint num) const
- {
- RG_ETC1_ASSERT((ofs + num) <= 64U);
- RG_ETC1_ASSERT(num && (num <= 8U));
- RG_ETC1_ASSERT((ofs >> 3) == ((ofs + num - 1) >> 3));
- const uint byte_ofs = 7 - (ofs >> 3);
- const uint byte_bit_ofs = ofs & 7;
- return (m_bytes[byte_ofs] >> byte_bit_ofs) & ((1 << num) - 1);
- }
-
- inline void set_byte_bits(uint ofs, uint num, uint bits)
- {
- RG_ETC1_ASSERT((ofs + num) <= 64U);
- RG_ETC1_ASSERT(num && (num < 32U));
- RG_ETC1_ASSERT((ofs >> 3) == ((ofs + num - 1) >> 3));
- RG_ETC1_ASSERT(bits < (1U << num));
- const uint byte_ofs = 7 - (ofs >> 3);
- const uint byte_bit_ofs = ofs & 7;
- const uint mask = (1 << num) - 1;
- m_bytes[byte_ofs] &= ~(mask << byte_bit_ofs);
- m_bytes[byte_ofs] |= (bits << byte_bit_ofs);
- }
-
- // false = left/right subblocks
- // true = upper/lower subblocks
- inline bool get_flip_bit() const
- {
- return (m_bytes[3] & 1) != 0;
- }
-
- inline void set_flip_bit(bool flip)
- {
- m_bytes[3] &= ~1;
- m_bytes[3] |= static_cast<uint8>(flip);
- }
-
- inline bool get_diff_bit() const
- {
- return (m_bytes[3] & 2) != 0;
- }
-
- inline void set_diff_bit(bool diff)
- {
- m_bytes[3] &= ~2;
- m_bytes[3] |= (static_cast<uint>(diff) << 1);
- }
-
- // Returns intensity modifier table (0-7) used by subblock subblock_id.
- // subblock_id=0 left/top (CW 1), 1=right/bottom (CW 2)
- inline uint get_inten_table(uint subblock_id) const
- {
- RG_ETC1_ASSERT(subblock_id < 2);
- const uint ofs = subblock_id ? 2 : 5;
- return (m_bytes[3] >> ofs) & 7;
- }
-
- // Sets intensity modifier table (0-7) used by subblock subblock_id (0 or 1)
- inline void set_inten_table(uint subblock_id, uint t)
- {
- RG_ETC1_ASSERT(subblock_id < 2);
- RG_ETC1_ASSERT(t < 8);
- const uint ofs = subblock_id ? 2 : 5;
- m_bytes[3] &= ~(7 << ofs);
- m_bytes[3] |= (t << ofs);
- }
-
- // Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.
- inline uint get_selector(uint x, uint y) const
- {
- RG_ETC1_ASSERT((x | y) < 4);
-
- const uint bit_index = x * 4 + y;
- const uint byte_bit_ofs = bit_index & 7;
- const uint8 *p = &m_bytes[7 - (bit_index >> 3)];
- const uint lsb = (p[0] >> byte_bit_ofs) & 1;
- const uint msb = (p[-2] >> byte_bit_ofs) & 1;
- const uint val = lsb | (msb << 1);
-
- return g_etc1_to_selector_index[val];
- }
-
- // Selector "val" ranges from 0-3 and is a direct index into g_etc1_inten_tables.
- inline void set_selector(uint x, uint y, uint val)
- {
- RG_ETC1_ASSERT((x | y | val) < 4);
- const uint bit_index = x * 4 + y;
-
- uint8 *p = &m_bytes[7 - (bit_index >> 3)];
-
- const uint byte_bit_ofs = bit_index & 7;
- const uint mask = 1 << byte_bit_ofs;
-
- const uint etc1_val = g_selector_index_to_etc1[val];
-
- const uint lsb = etc1_val & 1;
- const uint msb = etc1_val >> 1;
-
- p[0] &= ~mask;
- p[0] |= (lsb << byte_bit_ofs);
-
- p[-2] &= ~mask;
- p[-2] |= (msb << byte_bit_ofs);
- }
-
- inline void set_base4_color(uint idx, uint16 c)
- {
- if (idx)
- {
- set_byte_bits(cETC1AbsColor4R2BitOffset, 4, (c >> 8) & 15);
- set_byte_bits(cETC1AbsColor4G2BitOffset, 4, (c >> 4) & 15);
- set_byte_bits(cETC1AbsColor4B2BitOffset, 4, c & 15);
- }
- else
- {
- set_byte_bits(cETC1AbsColor4R1BitOffset, 4, (c >> 8) & 15);
- set_byte_bits(cETC1AbsColor4G1BitOffset, 4, (c >> 4) & 15);
- set_byte_bits(cETC1AbsColor4B1BitOffset, 4, c & 15);
- }
- }
-
- inline uint16 get_base4_color(uint idx) const
- {
- uint r, g, b;
- if (idx)
- {
- r = get_byte_bits(cETC1AbsColor4R2BitOffset, 4);
- g = get_byte_bits(cETC1AbsColor4G2BitOffset, 4);
- b = get_byte_bits(cETC1AbsColor4B2BitOffset, 4);
- }
- else
- {
- r = get_byte_bits(cETC1AbsColor4R1BitOffset, 4);
- g = get_byte_bits(cETC1AbsColor4G1BitOffset, 4);
- b = get_byte_bits(cETC1AbsColor4B1BitOffset, 4);
- }
- return static_cast<uint16>(b | (g << 4U) | (r << 8U));
- }
-
- inline void set_base5_color(uint16 c)
- {
- set_byte_bits(cETC1BaseColor5RBitOffset, 5, (c >> 10) & 31);
- set_byte_bits(cETC1BaseColor5GBitOffset, 5, (c >> 5) & 31);
- set_byte_bits(cETC1BaseColor5BBitOffset, 5, c & 31);
- }
-
- inline uint16 get_base5_color() const
- {
- const uint r = get_byte_bits(cETC1BaseColor5RBitOffset, 5);
- const uint g = get_byte_bits(cETC1BaseColor5GBitOffset, 5);
- const uint b = get_byte_bits(cETC1BaseColor5BBitOffset, 5);
- return static_cast<uint16>(b | (g << 5U) | (r << 10U));
- }
-
- void set_delta3_color(uint16 c)
- {
- set_byte_bits(cETC1DeltaColor3RBitOffset, 3, (c >> 6) & 7);
- set_byte_bits(cETC1DeltaColor3GBitOffset, 3, (c >> 3) & 7);
- set_byte_bits(cETC1DeltaColor3BBitOffset, 3, c & 7);
- }
-
- inline uint16 get_delta3_color() const
- {
- const uint r = get_byte_bits(cETC1DeltaColor3RBitOffset, 3);
- const uint g = get_byte_bits(cETC1DeltaColor3GBitOffset, 3);
- const uint b = get_byte_bits(cETC1DeltaColor3BBitOffset, 3);
- return static_cast<uint16>(b | (g << 3U) | (r << 6U));
- }
-
- // Base color 5
- static uint16 pack_color5(const color_quad_u8& color, bool scaled, uint bias = 127U);
- static uint16 pack_color5(uint r, uint g, uint b, bool scaled, uint bias = 127U);
-
- static color_quad_u8 unpack_color5(uint16 packed_color5, bool scaled, uint alpha = 255U);
- static void unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color, bool scaled);
-
- static bool unpack_color5(color_quad_u8& result, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha = 255U);
- static bool unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha = 255U);
-
- // Delta color 3
- // Inputs range from -4 to 3 (cETC1ColorDeltaMin to cETC1ColorDeltaMax)
- static uint16 pack_delta3(int r, int g, int b);
-
- // Results range from -4 to 3 (cETC1ColorDeltaMin to cETC1ColorDeltaMax)
- static void unpack_delta3(int& r, int& g, int& b, uint16 packed_delta3);
-
- // Abs color 4
- static uint16 pack_color4(const color_quad_u8& color, bool scaled, uint bias = 127U);
- static uint16 pack_color4(uint r, uint g, uint b, bool scaled, uint bias = 127U);
-
- static color_quad_u8 unpack_color4(uint16 packed_color4, bool scaled, uint alpha = 255U);
- static void unpack_color4(uint& r, uint& g, uint& b, uint16 packed_color4, bool scaled);
-
- // subblock colors
- static void get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint table_idx);
- static bool get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint16 packed_delta3, uint table_idx);
- static void get_abs_subblock_colors(color_quad_u8* pDst, uint16 packed_color4, uint table_idx);
-
- static inline void unscaled_to_scaled_color(color_quad_u8& dst, const color_quad_u8& src, bool color4)
- {
- if (color4)
- {
- dst.r = src.r | (src.r << 4);
- dst.g = src.g | (src.g << 4);
- dst.b = src.b | (src.b << 4);
- }
- else
- {
- dst.r = (src.r >> 2) | (src.r << 3);
- dst.g = (src.g >> 2) | (src.g << 3);
- dst.b = (src.b >> 2) | (src.b << 3);
- }
- dst.a = src.a;
- }
- };
-
- // Returns pointer to sorted array.
- template<typename T, typename Q>
- T* indirect_radix_sort(uint num_indices, T* pIndices0, T* pIndices1, const Q* pKeys, uint key_ofs, uint key_size, bool init_indices)
- {
- RG_ETC1_ASSERT((key_ofs >= 0) && (key_ofs < sizeof(T)));
- RG_ETC1_ASSERT((key_size >= 1) && (key_size <= 4));
-
- if (init_indices)
- {
- T* p = pIndices0;
- T* q = pIndices0 + (num_indices >> 1) * 2;
- uint i;
- for (i = 0; p != q; p += 2, i += 2)
- {
- p[0] = static_cast<T>(i);
- p[1] = static_cast<T>(i + 1);
- }
-
- if (num_indices & 1)
- *p = static_cast<T>(i);
- }
-
- uint hist[256 * 4];
-
- memset(hist, 0, sizeof(hist[0]) * 256 * key_size);
-
-#define RG_ETC1_GET_KEY(p) (*(const uint*)((const uint8*)(pKeys + *(p)) + key_ofs))
-#define RG_ETC1_GET_KEY_FROM_INDEX(i) (*(const uint*)((const uint8*)(pKeys + (i)) + key_ofs))
-
- if (key_size == 4)
- {
- T* p = pIndices0;
- T* q = pIndices0 + num_indices;
- for ( ; p != q; p++)
- {
- const uint key = RG_ETC1_GET_KEY(p);
-
- hist[ key & 0xFF]++;
- hist[256 + ((key >> 8) & 0xFF)]++;
- hist[512 + ((key >> 16) & 0xFF)]++;
- hist[768 + ((key >> 24) & 0xFF)]++;
- }
- }
- else if (key_size == 3)
- {
- T* p = pIndices0;
- T* q = pIndices0 + num_indices;
- for ( ; p != q; p++)
- {
- const uint key = RG_ETC1_GET_KEY(p);
-
- hist[ key & 0xFF]++;
- hist[256 + ((key >> 8) & 0xFF)]++;
- hist[512 + ((key >> 16) & 0xFF)]++;
- }
- }
- else if (key_size == 2)
- {
- T* p = pIndices0;
- T* q = pIndices0 + (num_indices >> 1) * 2;
-
- for ( ; p != q; p += 2)
- {
- const uint key0 = RG_ETC1_GET_KEY(p);
- const uint key1 = RG_ETC1_GET_KEY(p+1);
-
- hist[ key0 & 0xFF]++;
- hist[256 + ((key0 >> 8) & 0xFF)]++;
-
- hist[ key1 & 0xFF]++;
- hist[256 + ((key1 >> 8) & 0xFF)]++;
- }
-
- if (num_indices & 1)
- {
- const uint key = RG_ETC1_GET_KEY(p);
-
- hist[ key & 0xFF]++;
- hist[256 + ((key >> 8) & 0xFF)]++;
- }
- }
- else
- {
- RG_ETC1_ASSERT(key_size == 1);
- if (key_size != 1)
- return NULL;
-
- T* p = pIndices0;
- T* q = pIndices0 + (num_indices >> 1) * 2;
-
- for ( ; p != q; p += 2)
- {
- const uint key0 = RG_ETC1_GET_KEY(p);
- const uint key1 = RG_ETC1_GET_KEY(p+1);
-
- hist[key0 & 0xFF]++;
- hist[key1 & 0xFF]++;
- }
-
- if (num_indices & 1)
- {
- const uint key = RG_ETC1_GET_KEY(p);
-
- hist[key & 0xFF]++;
- }
- }
-
- T* pCur = pIndices0;
- T* pNew = pIndices1;
-
- for (uint pass = 0; pass < key_size; pass++)
- {
- const uint* pHist = &hist[pass << 8];
-
- uint offsets[256];
-
- uint cur_ofs = 0;
- for (uint i = 0; i < 256; i += 2)
- {
- offsets[i] = cur_ofs;
- cur_ofs += pHist[i];
-
- offsets[i+1] = cur_ofs;
- cur_ofs += pHist[i+1];
- }
-
- const uint pass_shift = pass << 3;
-
- T* p = pCur;
- T* q = pCur + (num_indices >> 1) * 2;
-
- for ( ; p != q; p += 2)
- {
- uint index0 = p[0];
- uint index1 = p[1];
-
- uint c0 = (RG_ETC1_GET_KEY_FROM_INDEX(index0) >> pass_shift) & 0xFF;
- uint c1 = (RG_ETC1_GET_KEY_FROM_INDEX(index1) >> pass_shift) & 0xFF;
-
- if (c0 == c1)
- {
- uint dst_offset0 = offsets[c0];
-
- offsets[c0] = dst_offset0 + 2;
-
- pNew[dst_offset0] = static_cast<T>(index0);
- pNew[dst_offset0 + 1] = static_cast<T>(index1);
- }
- else
- {
- uint dst_offset0 = offsets[c0]++;
- uint dst_offset1 = offsets[c1]++;
-
- pNew[dst_offset0] = static_cast<T>(index0);
- pNew[dst_offset1] = static_cast<T>(index1);
- }
- }
-
- if (num_indices & 1)
- {
- uint index = *p;
- uint c = (RG_ETC1_GET_KEY_FROM_INDEX(index) >> pass_shift) & 0xFF;
-
- uint dst_offset = offsets[c];
- offsets[c] = dst_offset + 1;
-
- pNew[dst_offset] = static_cast<T>(index);
- }
-
- T* t = pCur;
- pCur = pNew;
- pNew = t;
- }
-
- return pCur;
- }
-
-#undef RG_ETC1_GET_KEY
-#undef RG_ETC1_GET_KEY_FROM_INDEX
-
- uint16 etc1_block::pack_color5(const color_quad_u8& color, bool scaled, uint bias)
- {
- return pack_color5(color.r, color.g, color.b, scaled, bias);
- }
-
- uint16 etc1_block::pack_color5(uint r, uint g, uint b, bool scaled, uint bias)
- {
- if (scaled)
- {
- r = (r * 31U + bias) / 255U;
- g = (g * 31U + bias) / 255U;
- b = (b * 31U + bias) / 255U;
- }
-
- r = rg_etc1::minimum(r, 31U);
- g = rg_etc1::minimum(g, 31U);
- b = rg_etc1::minimum(b, 31U);
-
- return static_cast<uint16>(b | (g << 5U) | (r << 10U));
- }
-
- color_quad_u8 etc1_block::unpack_color5(uint16 packed_color5, bool scaled, uint alpha)
- {
- uint b = packed_color5 & 31U;
- uint g = (packed_color5 >> 5U) & 31U;
- uint r = (packed_color5 >> 10U) & 31U;
-
- if (scaled)
- {
- b = (b << 3U) | (b >> 2U);
- g = (g << 3U) | (g >> 2U);
- r = (r << 3U) | (r >> 2U);
- }
-
- return color_quad_u8(cNoClamp, r, g, b, rg_etc1::minimum(alpha, 255U));
- }
-
- void etc1_block::unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color5, bool scaled)
- {
- color_quad_u8 c(unpack_color5(packed_color5, scaled, 0));
- r = c.r;
- g = c.g;
- b = c.b;
- }
-
- bool etc1_block::unpack_color5(color_quad_u8& result, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha)
- {
- int dc_r, dc_g, dc_b;
- unpack_delta3(dc_r, dc_g, dc_b, packed_delta3);
-
- int b = (packed_color5 & 31U) + dc_b;
- int g = ((packed_color5 >> 5U) & 31U) + dc_g;
- int r = ((packed_color5 >> 10U) & 31U) + dc_r;
-
- bool success = true;
- if (static_cast<uint>(r | g | b) > 31U)
- {
- success = false;
- r = rg_etc1::clamp<int>(r, 0, 31);
- g = rg_etc1::clamp<int>(g, 0, 31);
- b = rg_etc1::clamp<int>(b, 0, 31);
- }
-
- if (scaled)
- {
- b = (b << 3U) | (b >> 2U);
- g = (g << 3U) | (g >> 2U);
- r = (r << 3U) | (r >> 2U);
- }
-
- result.set_noclamp_rgba(r, g, b, rg_etc1::minimum(alpha, 255U));
- return success;
- }
-
- bool etc1_block::unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha)
- {
- color_quad_u8 result;
- const bool success = unpack_color5(result, packed_color5, packed_delta3, scaled, alpha);
- r = result.r;
- g = result.g;
- b = result.b;
- return success;
- }
-
- uint16 etc1_block::pack_delta3(int r, int g, int b)
- {
- RG_ETC1_ASSERT((r >= cETC1ColorDeltaMin) && (r <= cETC1ColorDeltaMax));
- RG_ETC1_ASSERT((g >= cETC1ColorDeltaMin) && (g <= cETC1ColorDeltaMax));
- RG_ETC1_ASSERT((b >= cETC1ColorDeltaMin) && (b <= cETC1ColorDeltaMax));
- if (r < 0) r += 8;
- if (g < 0) g += 8;
- if (b < 0) b += 8;
- return static_cast<uint16>(b | (g << 3) | (r << 6));
- }
-
- void etc1_block::unpack_delta3(int& r, int& g, int& b, uint16 packed_delta3)
- {
- r = (packed_delta3 >> 6) & 7;
- g = (packed_delta3 >> 3) & 7;
- b = packed_delta3 & 7;
- if (r >= 4) r -= 8;
- if (g >= 4) g -= 8;
- if (b >= 4) b -= 8;
- }
-
- uint16 etc1_block::pack_color4(const color_quad_u8& color, bool scaled, uint bias)
- {
- return pack_color4(color.r, color.g, color.b, scaled, bias);
- }
-
- uint16 etc1_block::pack_color4(uint r, uint g, uint b, bool scaled, uint bias)
- {
- if (scaled)
- {
- r = (r * 15U + bias) / 255U;
- g = (g * 15U + bias) / 255U;
- b = (b * 15U + bias) / 255U;
- }
-
- r = rg_etc1::minimum(r, 15U);
- g = rg_etc1::minimum(g, 15U);
- b = rg_etc1::minimum(b, 15U);
-
- return static_cast<uint16>(b | (g << 4U) | (r << 8U));
- }
-
- color_quad_u8 etc1_block::unpack_color4(uint16 packed_color4, bool scaled, uint alpha)
- {
- uint b = packed_color4 & 15U;
- uint g = (packed_color4 >> 4U) & 15U;
- uint r = (packed_color4 >> 8U) & 15U;
-
- if (scaled)
- {
- b = (b << 4U) | b;
- g = (g << 4U) | g;
- r = (r << 4U) | r;
- }
-
- return color_quad_u8(cNoClamp, r, g, b, rg_etc1::minimum(alpha, 255U));
- }
-
- void etc1_block::unpack_color4(uint& r, uint& g, uint& b, uint16 packed_color4, bool scaled)
- {
- color_quad_u8 c(unpack_color4(packed_color4, scaled, 0));
- r = c.r;
- g = c.g;
- b = c.b;
- }
-
- void etc1_block::get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint table_idx)
- {
- RG_ETC1_ASSERT(table_idx < cETC1IntenModifierValues);
- const int *pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
-
- uint r, g, b;
- unpack_color5(r, g, b, packed_color5, true);
-
- const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);
-
- const int y0 = pInten_modifer_table[0];
- pDst[0].set(ir + y0, ig + y0, ib + y0);
-
- const int y1 = pInten_modifer_table[1];
- pDst[1].set(ir + y1, ig + y1, ib + y1);
-
- const int y2 = pInten_modifer_table[2];
- pDst[2].set(ir + y2, ig + y2, ib + y2);
-
- const int y3 = pInten_modifer_table[3];
- pDst[3].set(ir + y3, ig + y3, ib + y3);
- }
-
- bool etc1_block::get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint16 packed_delta3, uint table_idx)
- {
- RG_ETC1_ASSERT(table_idx < cETC1IntenModifierValues);
- const int *pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
-
- uint r, g, b;
- bool success = unpack_color5(r, g, b, packed_color5, packed_delta3, true);
-
- const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);
-
- const int y0 = pInten_modifer_table[0];
- pDst[0].set(ir + y0, ig + y0, ib + y0);
-
- const int y1 = pInten_modifer_table[1];
- pDst[1].set(ir + y1, ig + y1, ib + y1);
-
- const int y2 = pInten_modifer_table[2];
- pDst[2].set(ir + y2, ig + y2, ib + y2);
-
- const int y3 = pInten_modifer_table[3];
- pDst[3].set(ir + y3, ig + y3, ib + y3);
-
- return success;
- }
-
- void etc1_block::get_abs_subblock_colors(color_quad_u8* pDst, uint16 packed_color4, uint table_idx)
- {
- RG_ETC1_ASSERT(table_idx < cETC1IntenModifierValues);
- const int *pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
-
- uint r, g, b;
- unpack_color4(r, g, b, packed_color4, true);
-
- const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);
-
- const int y0 = pInten_modifer_table[0];
- pDst[0].set(ir + y0, ig + y0, ib + y0);
-
- const int y1 = pInten_modifer_table[1];
- pDst[1].set(ir + y1, ig + y1, ib + y1);
-
- const int y2 = pInten_modifer_table[2];
- pDst[2].set(ir + y2, ig + y2, ib + y2);
-
- const int y3 = pInten_modifer_table[3];
- pDst[3].set(ir + y3, ig + y3, ib + y3);
- }
-
- bool unpack_etc1_block(const void* pETC1_block, unsigned int* pDst_pixels_rgba, bool preserve_alpha)
- {
- color_quad_u8* pDst = reinterpret_cast<color_quad_u8*>(pDst_pixels_rgba);
- const etc1_block& block = *static_cast<const etc1_block*>(pETC1_block);
-
- const bool diff_flag = block.get_diff_bit();
- const bool flip_flag = block.get_flip_bit();
- const uint table_index0 = block.get_inten_table(0);
- const uint table_index1 = block.get_inten_table(1);
-
- color_quad_u8 subblock_colors0[4];
- color_quad_u8 subblock_colors1[4];
- bool success = true;
-
- if (diff_flag)
- {
- const uint16 base_color5 = block.get_base5_color();
- const uint16 delta_color3 = block.get_delta3_color();
- etc1_block::get_diff_subblock_colors(subblock_colors0, base_color5, table_index0);
-
- if (!etc1_block::get_diff_subblock_colors(subblock_colors1, base_color5, delta_color3, table_index1))
- success = false;
- }
- else
- {
- const uint16 base_color4_0 = block.get_base4_color(0);
- etc1_block::get_abs_subblock_colors(subblock_colors0, base_color4_0, table_index0);
-
- const uint16 base_color4_1 = block.get_base4_color(1);
- etc1_block::get_abs_subblock_colors(subblock_colors1, base_color4_1, table_index1);
- }
-
- if (preserve_alpha)
- {
- if (flip_flag)
- {
- for (uint y = 0; y < 2; y++)
- {
- pDst[0].set_rgb(subblock_colors0[block.get_selector(0, y)]);
- pDst[1].set_rgb(subblock_colors0[block.get_selector(1, y)]);
- pDst[2].set_rgb(subblock_colors0[block.get_selector(2, y)]);
- pDst[3].set_rgb(subblock_colors0[block.get_selector(3, y)]);
- pDst += 4;
- }
-
- for (uint y = 2; y < 4; y++)
- {
- pDst[0].set_rgb(subblock_colors1[block.get_selector(0, y)]);
- pDst[1].set_rgb(subblock_colors1[block.get_selector(1, y)]);
- pDst[2].set_rgb(subblock_colors1[block.get_selector(2, y)]);
- pDst[3].set_rgb(subblock_colors1[block.get_selector(3, y)]);
- pDst += 4;
- }
- }
- else
- {
- for (uint y = 0; y < 4; y++)
- {
- pDst[0].set_rgb(subblock_colors0[block.get_selector(0, y)]);
- pDst[1].set_rgb(subblock_colors0[block.get_selector(1, y)]);
- pDst[2].set_rgb(subblock_colors1[block.get_selector(2, y)]);
- pDst[3].set_rgb(subblock_colors1[block.get_selector(3, y)]);
- pDst += 4;
- }
- }
- }
- else
- {
- if (flip_flag)
- {
- // 0000
- // 0000
- // 1111
- // 1111
- for (uint y = 0; y < 2; y++)
- {
- pDst[0] = subblock_colors0[block.get_selector(0, y)];
- pDst[1] = subblock_colors0[block.get_selector(1, y)];
- pDst[2] = subblock_colors0[block.get_selector(2, y)];
- pDst[3] = subblock_colors0[block.get_selector(3, y)];
- pDst += 4;
- }
-
- for (uint y = 2; y < 4; y++)
- {
- pDst[0] = subblock_colors1[block.get_selector(0, y)];
- pDst[1] = subblock_colors1[block.get_selector(1, y)];
- pDst[2] = subblock_colors1[block.get_selector(2, y)];
- pDst[3] = subblock_colors1[block.get_selector(3, y)];
- pDst += 4;
- }
- }
- else
- {
- // 0011
- // 0011
- // 0011
- // 0011
- for (uint y = 0; y < 4; y++)
- {
- pDst[0] = subblock_colors0[block.get_selector(0, y)];
- pDst[1] = subblock_colors0[block.get_selector(1, y)];
- pDst[2] = subblock_colors1[block.get_selector(2, y)];
- pDst[3] = subblock_colors1[block.get_selector(3, y)];
- pDst += 4;
- }
- }
- }
-
- return success;
- }
-
- struct etc1_solution_coordinates
- {
- inline etc1_solution_coordinates() :
- m_unscaled_color(0, 0, 0, 0),
- m_inten_table(0),
- m_color4(false)
- {
- }
-
- inline etc1_solution_coordinates(uint r, uint g, uint b, uint inten_table, bool color4) :
- m_unscaled_color(r, g, b, 255),
- m_inten_table(inten_table),
- m_color4(color4)
- {
- }
-
- inline etc1_solution_coordinates(const color_quad_u8& c, uint inten_table, bool color4) :
- m_unscaled_color(c),
- m_inten_table(inten_table),
- m_color4(color4)
- {
- }
-
- inline etc1_solution_coordinates(const etc1_solution_coordinates& other)
- {
- *this = other;
- }
-
- inline etc1_solution_coordinates& operator= (const etc1_solution_coordinates& rhs)
- {
- m_unscaled_color = rhs.m_unscaled_color;
- m_inten_table = rhs.m_inten_table;
- m_color4 = rhs.m_color4;
- return *this;
- }
-
- inline void clear()
- {
- m_unscaled_color.clear();
- m_inten_table = 0;
- m_color4 = false;
- }
-
- inline color_quad_u8 get_scaled_color() const
- {
- int br, bg, bb;
- if (m_color4)
- {
- br = m_unscaled_color.r | (m_unscaled_color.r << 4);
- bg = m_unscaled_color.g | (m_unscaled_color.g << 4);
- bb = m_unscaled_color.b | (m_unscaled_color.b << 4);
- }
- else
- {
- br = (m_unscaled_color.r >> 2) | (m_unscaled_color.r << 3);
- bg = (m_unscaled_color.g >> 2) | (m_unscaled_color.g << 3);
- bb = (m_unscaled_color.b >> 2) | (m_unscaled_color.b << 3);
- }
- return color_quad_u8(br, bg, bb);
- }
-
- inline void get_block_colors(color_quad_u8* pBlock_colors)
- {
- int br, bg, bb;
- if (m_color4)
- {
- br = m_unscaled_color.r | (m_unscaled_color.r << 4);
- bg = m_unscaled_color.g | (m_unscaled_color.g << 4);
- bb = m_unscaled_color.b | (m_unscaled_color.b << 4);
- }
- else
- {
- br = (m_unscaled_color.r >> 2) | (m_unscaled_color.r << 3);
- bg = (m_unscaled_color.g >> 2) | (m_unscaled_color.g << 3);
- bb = (m_unscaled_color.b >> 2) | (m_unscaled_color.b << 3);
- }
- const int* pInten_table = g_etc1_inten_tables[m_inten_table];
- pBlock_colors[0].set(br + pInten_table[0], bg + pInten_table[0], bb + pInten_table[0]);
- pBlock_colors[1].set(br + pInten_table[1], bg + pInten_table[1], bb + pInten_table[1]);
- pBlock_colors[2].set(br + pInten_table[2], bg + pInten_table[2], bb + pInten_table[2]);
- pBlock_colors[3].set(br + pInten_table[3], bg + pInten_table[3], bb + pInten_table[3]);
- }
-
- color_quad_u8 m_unscaled_color;
- uint m_inten_table;
- bool m_color4;
- };
-
- class etc1_optimizer
- {
- etc1_optimizer(const etc1_optimizer&);
- etc1_optimizer& operator= (const etc1_optimizer&);
-
- public:
- etc1_optimizer()
- {
- clear();
- }
-
- void clear()
- {
- m_pParams = NULL;
- m_pResult = NULL;
- m_pSorted_luma = NULL;
- m_pSorted_luma_indices = NULL;
- }
-
- struct params : etc1_pack_params
- {
- params()
- {
- clear();
- }
-
- params(const etc1_pack_params& base_params) :
- etc1_pack_params(base_params)
- {
- clear_optimizer_params();
- }
-
- void clear()
- {
- etc1_pack_params::clear();
- clear_optimizer_params();
- }
-
- void clear_optimizer_params()
- {
- m_num_src_pixels = 0;
- m_pSrc_pixels = 0;
-
- m_use_color4 = false;
- static const int s_default_scan_delta[] = { 0 };
- m_pScan_deltas = s_default_scan_delta;
- m_scan_delta_size = 1;
-
- m_base_color5.clear();
- m_constrain_against_base_color5 = false;
- }
-
- uint m_num_src_pixels;
- const color_quad_u8* m_pSrc_pixels;
-
- bool m_use_color4;
- const int* m_pScan_deltas;
- uint m_scan_delta_size;
-
- color_quad_u8 m_base_color5;
- bool m_constrain_against_base_color5;
- };
-
- struct results
- {
- uint64 m_error;
- color_quad_u8 m_block_color_unscaled;
- uint m_block_inten_table;
- uint m_n;
- uint8* m_pSelectors;
- bool m_block_color4;
-
- inline results& operator= (const results& rhs)
- {
- m_block_color_unscaled = rhs.m_block_color_unscaled;
- m_block_color4 = rhs.m_block_color4;
- m_block_inten_table = rhs.m_block_inten_table;
- m_error = rhs.m_error;
- RG_ETC1_ASSERT(m_n == rhs.m_n);
- memcpy(m_pSelectors, rhs.m_pSelectors, rhs.m_n);
- return *this;
- }
- };
-
- void init(const params& params, results& result);
- bool compute();
-
- private:
- struct potential_solution
- {
- potential_solution() : m_coords(), m_error(cUINT64_MAX), m_valid(false)
- {
- }
-
- etc1_solution_coordinates m_coords;
- uint8 m_selectors[8];
- uint64 m_error;
- bool m_valid;
-
- void clear()
- {
- m_coords.clear();
- m_error = cUINT64_MAX;
- m_valid = false;
- }
- };
-
- const params* m_pParams;
- results* m_pResult;
-
- int m_limit;
-
- vec3F m_avg_color;
- int m_br, m_bg, m_bb;
- uint16 m_luma[8];
- uint32 m_sorted_luma[2][8];
- const uint32* m_pSorted_luma_indices;
- uint32* m_pSorted_luma;
-
- uint8 m_selectors[8];
- uint8 m_best_selectors[8];
-
- potential_solution m_best_solution;
- potential_solution m_trial_solution;
- uint8 m_temp_selectors[8];
-
- bool evaluate_solution(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);
- bool evaluate_solution_fast(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);
- };
-
- bool etc1_optimizer::compute()
- {
- const uint n = m_pParams->m_num_src_pixels;
- const int scan_delta_size = m_pParams->m_scan_delta_size;
-
- // Scan through a subset of the 3D lattice centered around the avg block color trying each 3D (555 or 444) lattice point as a potential block color.
- // Each time a better solution is found try to refine the current solution's block color based of the current selectors and intensity table index.
- for (int zdi = 0; zdi < scan_delta_size; zdi++)
- {
- const int zd = m_pParams->m_pScan_deltas[zdi];
- const int mbb = m_bb + zd;
- if (mbb < 0) continue; else if (mbb > m_limit) break;
-
- for (int ydi = 0; ydi < scan_delta_size; ydi++)
- {
- const int yd = m_pParams->m_pScan_deltas[ydi];
- const int mbg = m_bg + yd;
- if (mbg < 0) continue; else if (mbg > m_limit) break;
-
- for (int xdi = 0; xdi < scan_delta_size; xdi++)
- {
- const int xd = m_pParams->m_pScan_deltas[xdi];
- const int mbr = m_br + xd;
- if (mbr < 0) continue; else if (mbr > m_limit) break;
-
- etc1_solution_coordinates coords(mbr, mbg, mbb, 0, m_pParams->m_use_color4);
- if (m_pParams->m_quality == cHighQuality)
- {
- if (!evaluate_solution(coords, m_trial_solution, &m_best_solution))
- continue;
- }
- else
- {
- if (!evaluate_solution_fast(coords, m_trial_solution, &m_best_solution))
- continue;
- }
-
- // Now we have the input block, the avg. color of the input pixels, a set of trial selector indices, and the block color+intensity index.
- // Now, for each component, attempt to refine the current solution by solving a simple linear equation. For example, for 4 colors:
- // The goal is:
- // pixel0 - (block_color+inten_table[selector0]) + pixel1 - (block_color+inten_table[selector1]) + pixel2 - (block_color+inten_table[selector2]) + pixel3 - (block_color+inten_table[selector3]) = 0
- // Rearranging this:
- // (pixel0 + pixel1 + pixel2 + pixel3) - (block_color+inten_table[selector0]) - (block_color+inten_table[selector1]) - (block_color+inten_table[selector2]) - (block_color+inten_table[selector3]) = 0
- // (pixel0 + pixel1 + pixel2 + pixel3) - block_color - inten_table[selector0] - block_color-inten_table[selector1] - block_color-inten_table[selector2] - block_color-inten_table[selector3] = 0
- // (pixel0 + pixel1 + pixel2 + pixel3) - 4*block_color - inten_table[selector0] - inten_table[selector1] - inten_table[selector2] - inten_table[selector3] = 0
- // (pixel0 + pixel1 + pixel2 + pixel3) - 4*block_color - (inten_table[selector0] + inten_table[selector1] + inten_table[selector2] + inten_table[selector3]) = 0
- // (pixel0 + pixel1 + pixel2 + pixel3)/4 - block_color - (inten_table[selector0] + inten_table[selector1] + inten_table[selector2] + inten_table[selector3])/4 = 0
- // block_color = (pixel0 + pixel1 + pixel2 + pixel3)/4 - (inten_table[selector0] + inten_table[selector1] + inten_table[selector2] + inten_table[selector3])/4
- // So what this means:
- // optimal_block_color = avg_input - avg_inten_delta
- // So the optimal block color can be computed by taking the average block color and subtracting the current average of the intensity delta.
- // Unfortunately, optimal_block_color must then be quantized to 555 or 444 so it's not always possible to improve matters using this formula.
- // Also, the above formula is for unclamped intensity deltas. The actual implementation takes into account clamping.
-
- const uint max_refinement_trials = (m_pParams->m_quality == cLowQuality) ? 2 : (((xd | yd | zd) == 0) ? 4 : 2);
- for (uint refinement_trial = 0; refinement_trial < max_refinement_trials; refinement_trial++)
- {
- const uint8* pSelectors = m_best_solution.m_selectors;
- const int* pInten_table = g_etc1_inten_tables[m_best_solution.m_coords.m_inten_table];
-
- int delta_sum_r = 0, delta_sum_g = 0, delta_sum_b = 0;
- const color_quad_u8 base_color(m_best_solution.m_coords.get_scaled_color());
- for (uint r = 0; r < n; r++)
- {
- const uint s = *pSelectors++;
- const int yd = pInten_table[s];
- // Compute actual delta being applied to each pixel, taking into account clamping.
- delta_sum_r += rg_etc1::clamp<int>(base_color.r + yd, 0, 255) - base_color.r;
- delta_sum_g += rg_etc1::clamp<int>(base_color.g + yd, 0, 255) - base_color.g;
- delta_sum_b += rg_etc1::clamp<int>(base_color.b + yd, 0, 255) - base_color.b;
- }
- if ((!delta_sum_r) && (!delta_sum_g) && (!delta_sum_b))
- break;
- const float avg_delta_r_f = static_cast<float>(delta_sum_r) / n;
- const float avg_delta_g_f = static_cast<float>(delta_sum_g) / n;
- const float avg_delta_b_f = static_cast<float>(delta_sum_b) / n;
- const int br1 = rg_etc1::clamp<int>(static_cast<uint>((m_avg_color[0] - avg_delta_r_f) * m_limit / 255.0f + .5f), 0, m_limit);
- const int bg1 = rg_etc1::clamp<int>(static_cast<uint>((m_avg_color[1] - avg_delta_g_f) * m_limit / 255.0f + .5f), 0, m_limit);
- const int bb1 = rg_etc1::clamp<int>(static_cast<uint>((m_avg_color[2] - avg_delta_b_f) * m_limit / 255.0f + .5f), 0, m_limit);
-
- bool skip = false;
-
- if ((mbr == br1) && (mbg == bg1) && (mbb == bb1))
- skip = true;
- else if ((br1 == m_best_solution.m_coords.m_unscaled_color.r) && (bg1 == m_best_solution.m_coords.m_unscaled_color.g) && (bb1 == m_best_solution.m_coords.m_unscaled_color.b))
- skip = true;
- else if ((m_br == br1) && (m_bg == bg1) && (m_bb == bb1))
- skip = true;
-
- if (skip)
- break;
-
- etc1_solution_coordinates coords1(br1, bg1, bb1, 0, m_pParams->m_use_color4);
- if (m_pParams->m_quality == cHighQuality)
- {
- if (!evaluate_solution(coords1, m_trial_solution, &m_best_solution))
- break;
- }
- else
- {
- if (!evaluate_solution_fast(coords1, m_trial_solution, &m_best_solution))
- break;
- }
-
- } // refinement_trial
-
- } // xdi
- } // ydi
- } // zdi
-
- if (!m_best_solution.m_valid)
- {
- m_pResult->m_error = cUINT32_MAX;
- return false;
- }
-
- const uint8* pSelectors = m_best_solution.m_selectors;
-
-#ifdef RG_ETC1_BUILD_DEBUG
- {
- color_quad_u8 block_colors[4];
- m_best_solution.m_coords.get_block_colors(block_colors);
-
- const color_quad_u8* pSrc_pixels = m_pParams->m_pSrc_pixels;
- uint64 actual_error = 0;
- for (uint i = 0; i < n; i++)
- actual_error += pSrc_pixels[i].squared_distance_rgb(block_colors[pSelectors[i]]);
-
- RG_ETC1_ASSERT(actual_error == m_best_solution.m_error);
- }
-#endif
-
- m_pResult->m_error = m_best_solution.m_error;
-
- m_pResult->m_block_color_unscaled = m_best_solution.m_coords.m_unscaled_color;
- m_pResult->m_block_color4 = m_best_solution.m_coords.m_color4;
-
- m_pResult->m_block_inten_table = m_best_solution.m_coords.m_inten_table;
- memcpy(m_pResult->m_pSelectors, pSelectors, n);
- m_pResult->m_n = n;
-
- return true;
- }
-
- void etc1_optimizer::init(const params& p, results& r)
- {
- // This version is hardcoded for 8 pixel subblocks.
- RG_ETC1_ASSERT(p.m_num_src_pixels == 8);
-
- m_pParams = &p;
- m_pResult = &r;
-
- const uint n = 8;
-
- m_limit = m_pParams->m_use_color4 ? 15 : 31;
-
- vec3F avg_color(0.0f);
-
- for (uint i = 0; i < n; i++)
- {
- const color_quad_u8& c = m_pParams->m_pSrc_pixels[i];
- const vec3F fc(c.r, c.g, c.b);
-
- avg_color += fc;
-
- m_luma[i] = static_cast<uint16>(c.r + c.g + c.b);
- m_sorted_luma[0][i] = i;
- }
- avg_color *= (1.0f / static_cast<float>(n));
- m_avg_color = avg_color;
-
- m_br = rg_etc1::clamp<int>(static_cast<uint>(m_avg_color[0] * m_limit / 255.0f + .5f), 0, m_limit);
- m_bg = rg_etc1::clamp<int>(static_cast<uint>(m_avg_color[1] * m_limit / 255.0f + .5f), 0, m_limit);
- m_bb = rg_etc1::clamp<int>(static_cast<uint>(m_avg_color[2] * m_limit / 255.0f + .5f), 0, m_limit);
-
- if (m_pParams->m_quality <= cMediumQuality)
- {
- m_pSorted_luma_indices = indirect_radix_sort(n, m_sorted_luma[0], m_sorted_luma[1], m_luma, 0, sizeof(m_luma[0]), false);
- m_pSorted_luma = m_sorted_luma[0];
- if (m_pSorted_luma_indices == m_sorted_luma[0])
- m_pSorted_luma = m_sorted_luma[1];
-
- for (uint i = 0; i < n; i++)
- m_pSorted_luma[i] = m_luma[m_pSorted_luma_indices[i]];
- }
-
- m_best_solution.m_coords.clear();
- m_best_solution.m_valid = false;
- m_best_solution.m_error = cUINT64_MAX;
- }
-
- bool etc1_optimizer::evaluate_solution(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution)
- {
- trial_solution.m_valid = false;
-
- if (m_pParams->m_constrain_against_base_color5)
- {
- const int dr = coords.m_unscaled_color.r - m_pParams->m_base_color5.r;
- const int dg = coords.m_unscaled_color.g - m_pParams->m_base_color5.g;
- const int db = coords.m_unscaled_color.b - m_pParams->m_base_color5.b;
-
- if ((rg_etc1::minimum(dr, dg, db) < cETC1ColorDeltaMin) || (rg_etc1::maximum(dr, dg, db) > cETC1ColorDeltaMax))
- return false;
- }
-
- const color_quad_u8 base_color(coords.get_scaled_color());
-
- const uint n = 8;
-
- trial_solution.m_error = cUINT64_MAX;
-
- for (uint inten_table = 0; inten_table < cETC1IntenModifierValues; inten_table++)
- {
- const int* pInten_table = g_etc1_inten_tables[inten_table];
-
- color_quad_u8 block_colors[4];
- for (uint s = 0; s < 4; s++)
- {
- const int yd = pInten_table[s];
- block_colors[s].set(base_color.r + yd, base_color.g + yd, base_color.b + yd, 0);
- }
-
- uint64 total_error = 0;
-
- const color_quad_u8* pSrc_pixels = m_pParams->m_pSrc_pixels;
- for (uint c = 0; c < n; c++)
- {
- const color_quad_u8& src_pixel = *pSrc_pixels++;
-
- uint best_selector_index = 0;
- uint best_error = rg_etc1::square(src_pixel.r - block_colors[0].r) + rg_etc1::square(src_pixel.g - block_colors[0].g) + rg_etc1::square(src_pixel.b - block_colors[0].b);
-
- uint trial_error = rg_etc1::square(src_pixel.r - block_colors[1].r) + rg_etc1::square(src_pixel.g - block_colors[1].g) + rg_etc1::square(src_pixel.b - block_colors[1].b);
- if (trial_error < best_error)
- {
- best_error = trial_error;
- best_selector_index = 1;
- }
-
- trial_error = rg_etc1::square(src_pixel.r - block_colors[2].r) + rg_etc1::square(src_pixel.g - block_colors[2].g) + rg_etc1::square(src_pixel.b - block_colors[2].b);
- if (trial_error < best_error)
- {
- best_error = trial_error;
- best_selector_index = 2;
- }
-
- trial_error = rg_etc1::square(src_pixel.r - block_colors[3].r) + rg_etc1::square(src_pixel.g - block_colors[3].g) + rg_etc1::square(src_pixel.b - block_colors[3].b);
- if (trial_error < best_error)
- {
- best_error = trial_error;
- best_selector_index = 3;
- }
-
- m_temp_selectors[c] = static_cast<uint8>(best_selector_index);
-
- total_error += best_error;
- if (total_error >= trial_solution.m_error)
- break;
- }
-
- if (total_error < trial_solution.m_error)
- {
- trial_solution.m_error = total_error;
- trial_solution.m_coords.m_inten_table = inten_table;
- memcpy(trial_solution.m_selectors, m_temp_selectors, 8);
- trial_solution.m_valid = true;
- }
- }
- trial_solution.m_coords.m_unscaled_color = coords.m_unscaled_color;
- trial_solution.m_coords.m_color4 = m_pParams->m_use_color4;
-
- bool success = false;
- if (pBest_solution)
- {
- if (trial_solution.m_error < pBest_solution->m_error)
- {
- *pBest_solution = trial_solution;
- success = true;
- }
- }
-
- return success;
- }
-
- bool etc1_optimizer::evaluate_solution_fast(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution)
- {
- if (m_pParams->m_constrain_against_base_color5)
- {
- const int dr = coords.m_unscaled_color.r - m_pParams->m_base_color5.r;
- const int dg = coords.m_unscaled_color.g - m_pParams->m_base_color5.g;
- const int db = coords.m_unscaled_color.b - m_pParams->m_base_color5.b;
-
- if ((rg_etc1::minimum(dr, dg, db) < cETC1ColorDeltaMin) || (rg_etc1::maximum(dr, dg, db) > cETC1ColorDeltaMax))
- {
- trial_solution.m_valid = false;
- return false;
- }
- }
-
- const color_quad_u8 base_color(coords.get_scaled_color());
-
- const uint n = 8;
-
- trial_solution.m_error = cUINT64_MAX;
-
- for (int inten_table = cETC1IntenModifierValues - 1; inten_table >= 0; --inten_table)
- {
- const int* pInten_table = g_etc1_inten_tables[inten_table];
-
- uint block_inten[4];
- color_quad_u8 block_colors[4];
- for (uint s = 0; s < 4; s++)
- {
- const int yd = pInten_table[s];
- color_quad_u8 block_color(base_color.r + yd, base_color.g + yd, base_color.b + yd, 0);
- block_colors[s] = block_color;
- block_inten[s] = block_color.r + block_color.g + block_color.b;
- }
-
- // evaluate_solution_fast() enforces/assumesd a total ordering of the input colors along the intensity (1,1,1) axis to more quickly classify the inputs to selectors.
- // The inputs colors have been presorted along the projection onto this axis, and ETC1 block colors are always ordered along the intensity axis, so this classification is fast.
- // 0 1 2 3
- // 01 12 23
- const uint block_inten_midpoints[3] = { block_inten[0] + block_inten[1], block_inten[1] + block_inten[2], block_inten[2] + block_inten[3] };
-
- uint64 total_error = 0;
- const color_quad_u8* pSrc_pixels = m_pParams->m_pSrc_pixels;
- if ((m_pSorted_luma[n - 1] * 2) < block_inten_midpoints[0])
- {
- if (block_inten[0] > m_pSorted_luma[n - 1])
- {
- const uint min_error = labs(block_inten[0] - m_pSorted_luma[n - 1]);
- if (min_error >= trial_solution.m_error)
- continue;
- }
-
- memset(&m_temp_selectors[0], 0, n);
-
- for (uint c = 0; c < n; c++)
- total_error += block_colors[0].squared_distance_rgb(pSrc_pixels[c]);
- }
- else if ((m_pSorted_luma[0] * 2) >= block_inten_midpoints[2])
- {
- if (m_pSorted_luma[0] > block_inten[3])
- {
- const uint min_error = labs(m_pSorted_luma[0] - block_inten[3]);
- if (min_error >= trial_solution.m_error)
- continue;
- }
-
- memset(&m_temp_selectors[0], 3, n);
-
- for (uint c = 0; c < n; c++)
- total_error += block_colors[3].squared_distance_rgb(pSrc_pixels[c]);
- }
- else
- {
- uint cur_selector = 0, c;
- for (c = 0; c < n; c++)
- {
- const uint y = m_pSorted_luma[c];
- while ((y * 2) >= block_inten_midpoints[cur_selector])
- if (++cur_selector > 2)
- goto done;
- const uint sorted_pixel_index = m_pSorted_luma_indices[c];
- m_temp_selectors[sorted_pixel_index] = static_cast<uint8>(cur_selector);
- total_error += block_colors[cur_selector].squared_distance_rgb(pSrc_pixels[sorted_pixel_index]);
- }
-done:
- while (c < n)
- {
- const uint sorted_pixel_index = m_pSorted_luma_indices[c];
- m_temp_selectors[sorted_pixel_index] = 3;
- total_error += block_colors[3].squared_distance_rgb(pSrc_pixels[sorted_pixel_index]);
- ++c;
- }
- }
-
- if (total_error < trial_solution.m_error)
- {
- trial_solution.m_error = total_error;
- trial_solution.m_coords.m_inten_table = inten_table;
- memcpy(trial_solution.m_selectors, m_temp_selectors, n);
- trial_solution.m_valid = true;
- if (!total_error)
- break;
- }
- }
- trial_solution.m_coords.m_unscaled_color = coords.m_unscaled_color;
- trial_solution.m_coords.m_color4 = m_pParams->m_use_color4;
-
- bool success = false;
- if (pBest_solution)
- {
- if (trial_solution.m_error < pBest_solution->m_error)
- {
- *pBest_solution = trial_solution;
- success = true;
- }
- }
-
- return success;
- }
-
- static uint etc1_decode_value(uint diff, uint inten, uint selector, uint packed_c)
- {
- const uint limit = diff ? 32 : 16; limit;
- RG_ETC1_ASSERT((diff < 2) && (inten < 8) && (selector < 4) && (packed_c < limit));
- int c;
- if (diff)
- c = (packed_c >> 2) | (packed_c << 3);
- else
- c = packed_c | (packed_c << 4);
- c += g_etc1_inten_tables[inten][selector];
- c = rg_etc1::clamp<int>(c, 0, 255);
- return c;
- }
-
- static inline int mul_8bit(int a, int b) { int t = a*b + 128; return (t + (t >> 8)) >> 8; }
-
- void pack_etc1_block_init()
- {
- for (uint diff = 0; diff < 2; diff++)
- {
- const uint limit = diff ? 32 : 16;
-
- for (uint inten = 0; inten < 8; inten++)
- {
- for (uint selector = 0; selector < 4; selector++)
- {
- const uint inverse_table_index = diff + (inten << 1) + (selector << 4);
- for (uint color = 0; color < 256; color++)
- {
- uint best_error = cUINT32_MAX, best_packed_c = 0;
- for (uint packed_c = 0; packed_c < limit; packed_c++)
- {
- int v = etc1_decode_value(diff, inten, selector, packed_c);
- uint err = labs(v - static_cast<int>(color));
- if (err < best_error)
- {
- best_error = err;
- best_packed_c = packed_c;
- if (!best_error)
- break;
- }
- }
- RG_ETC1_ASSERT(best_error <= 255);
- g_etc1_inverse_lookup[inverse_table_index][color] = static_cast<uint16>(best_packed_c | (best_error << 8));
- }
- }
- }
- }
-
- uint expand5[32];
- for(int i = 0; i < 32; i++)
- expand5[i] = (i << 3) | (i >> 2);
-
- for(int i = 0; i < 256 + 16; i++)
- {
- int v = clamp<int>(i - 8, 0, 255);
- g_quant5_tab[i] = static_cast<uint8>(expand5[mul_8bit(v,31)]);
- }
- }
-
- // Packs solid color blocks efficiently using a set of small precomputed tables.
- // For random 888 inputs, MSE results are better than Erricson's ETC1 packer in "slow" mode ~9.5% of the time, is slightly worse only ~.01% of the time, and is equal the rest of the time.
- static uint64 pack_etc1_block_solid_color(etc1_block& block, const uint8* pColor, etc1_pack_params& pack_params)
- {
- pack_params;
- RG_ETC1_ASSERT(g_etc1_inverse_lookup[0][255]);
-
- static uint s_next_comp[4] = { 1, 2, 0, 1 };
-
- uint best_error = cUINT32_MAX, best_i = 0;
- int best_x = 0, best_packed_c1 = 0, best_packed_c2 = 0;
-
- // For each possible 8-bit value, there is a precomputed list of diff/inten/selector configurations that allow that 8-bit value to be encoded with no error.
- for (uint i = 0; i < 3; i++)
- {
- const uint c1 = pColor[s_next_comp[i]], c2 = pColor[s_next_comp[i + 1]];
-
- const int delta_range = 1;
- for (int delta = -delta_range; delta <= delta_range; delta++)
- {
- const int c_plus_delta = rg_etc1::clamp<int>(pColor[i] + delta, 0, 255);
-
- const uint16* pTable;
- if (!c_plus_delta)
- pTable = g_color8_to_etc_block_config_0_255[0];
- else if (c_plus_delta == 255)
- pTable = g_color8_to_etc_block_config_0_255[1];
- else
- pTable = g_color8_to_etc_block_config_1_to_254[c_plus_delta - 1];
-
- do
- {
- const uint x = *pTable++;
-
-#ifdef RG_ETC1_BUILD_DEBUG
- const uint diff = x & 1;
- const uint inten = (x >> 1) & 7;
- const uint selector = (x >> 4) & 3;
- const uint p0 = (x >> 8) & 255;
- RG_ETC1_ASSERT(etc1_decode_value(diff, inten, selector, p0) == (uint)c_plus_delta);
-#endif
-
- const uint16* pInverse_table = g_etc1_inverse_lookup[x & 0xFF];
- uint16 p1 = pInverse_table[c1];
- uint16 p2 = pInverse_table[c2];
- const uint trial_error = rg_etc1::square(c_plus_delta - pColor[i]) + rg_etc1::square(p1 >> 8) + rg_etc1::square(p2 >> 8);
- if (trial_error < best_error)
- {
- best_error = trial_error;
- best_x = x;
- best_packed_c1 = p1 & 0xFF;
- best_packed_c2 = p2 & 0xFF;
- best_i = i;
- if (!best_error)
- goto found_perfect_match;
- }
- } while (*pTable != 0xFFFF);
- }
- }
-found_perfect_match:
-
- const uint diff = best_x & 1;
- const uint inten = (best_x >> 1) & 7;
-
- block.m_bytes[3] = static_cast<uint8>(((inten | (inten << 3)) << 2) | (diff << 1));
-
- const uint etc1_selector = g_selector_index_to_etc1[(best_x >> 4) & 3];
- *reinterpret_cast<uint16*>(&block.m_bytes[4]) = (etc1_selector & 2) ? 0xFFFF : 0;
- *reinterpret_cast<uint16*>(&block.m_bytes[6]) = (etc1_selector & 1) ? 0xFFFF : 0;
-
- const uint best_packed_c0 = (best_x >> 8) & 255;
- if (diff)
- {
- block.m_bytes[best_i] = static_cast<uint8>(best_packed_c0 << 3);
- block.m_bytes[s_next_comp[best_i]] = static_cast<uint8>(best_packed_c1 << 3);
- block.m_bytes[s_next_comp[best_i+1]] = static_cast<uint8>(best_packed_c2 << 3);
- }
- else
- {
- block.m_bytes[best_i] = static_cast<uint8>(best_packed_c0 | (best_packed_c0 << 4));
- block.m_bytes[s_next_comp[best_i]] = static_cast<uint8>(best_packed_c1 | (best_packed_c1 << 4));
- block.m_bytes[s_next_comp[best_i+1]] = static_cast<uint8>(best_packed_c2 | (best_packed_c2 << 4));
- }
-
- return best_error;
- }
-
- static uint pack_etc1_block_solid_color_constrained(
- etc1_optimizer::results& results,
- uint num_colors, const uint8* pColor,
- etc1_pack_params& pack_params,
- bool use_diff,
- const color_quad_u8* pBase_color5_unscaled)
- {
- RG_ETC1_ASSERT(g_etc1_inverse_lookup[0][255]);
-
- pack_params;
- static uint s_next_comp[4] = { 1, 2, 0, 1 };
-
- uint best_error = cUINT32_MAX, best_i = 0;
- int best_x = 0, best_packed_c1 = 0, best_packed_c2 = 0;
-
- // For each possible 8-bit value, there is a precomputed list of diff/inten/selector configurations that allow that 8-bit value to be encoded with no error.
- for (uint i = 0; i < 3; i++)
- {
- const uint c1 = pColor[s_next_comp[i]], c2 = pColor[s_next_comp[i + 1]];
-
- const int delta_range = 1;
- for (int delta = -delta_range; delta <= delta_range; delta++)
- {
- const int c_plus_delta = rg_etc1::clamp<int>(pColor[i] + delta, 0, 255);
-
- const uint16* pTable;
- if (!c_plus_delta)
- pTable = g_color8_to_etc_block_config_0_255[0];
- else if (c_plus_delta == 255)
- pTable = g_color8_to_etc_block_config_0_255[1];
- else
- pTable = g_color8_to_etc_block_config_1_to_254[c_plus_delta - 1];
-
- do
- {
- const uint x = *pTable++;
- const uint diff = x & 1;
- if (static_cast<uint>(use_diff) != diff)
- {
- if (*pTable == 0xFFFF)
- break;
- continue;
- }
-
- if ((diff) && (pBase_color5_unscaled))
- {
- const int p0 = (x >> 8) & 255;
- int delta = p0 - static_cast<int>(pBase_color5_unscaled->c[i]);
- if ((delta < cETC1ColorDeltaMin) || (delta > cETC1ColorDeltaMax))
- {
- if (*pTable == 0xFFFF)
- break;
- continue;
- }
- }
-
-#ifdef RG_ETC1_BUILD_DEBUG
- {
- const uint inten = (x >> 1) & 7;
- const uint selector = (x >> 4) & 3;
- const uint p0 = (x >> 8) & 255;
- RG_ETC1_ASSERT(etc1_decode_value(diff, inten, selector, p0) == (uint)c_plus_delta);
- }
-#endif
-
- const uint16* pInverse_table = g_etc1_inverse_lookup[x & 0xFF];
- uint16 p1 = pInverse_table[c1];
- uint16 p2 = pInverse_table[c2];
-
- if ((diff) && (pBase_color5_unscaled))
- {
- int delta1 = (p1 & 0xFF) - static_cast<int>(pBase_color5_unscaled->c[s_next_comp[i]]);
- int delta2 = (p2 & 0xFF) - static_cast<int>(pBase_color5_unscaled->c[s_next_comp[i + 1]]);
- if ((delta1 < cETC1ColorDeltaMin) || (delta1 > cETC1ColorDeltaMax) || (delta2 < cETC1ColorDeltaMin) || (delta2 > cETC1ColorDeltaMax))
- {
- if (*pTable == 0xFFFF)
- break;
- continue;
- }
- }
-
- const uint trial_error = rg_etc1::square(c_plus_delta - pColor[i]) + rg_etc1::square(p1 >> 8) + rg_etc1::square(p2 >> 8);
- if (trial_error < best_error)
- {
- best_error = trial_error;
- best_x = x;
- best_packed_c1 = p1 & 0xFF;
- best_packed_c2 = p2 & 0xFF;
- best_i = i;
- if (!best_error)
- goto found_perfect_match;
- }
- } while (*pTable != 0xFFFF);
- }
- }
-found_perfect_match:
-
- if (best_error == cUINT32_MAX)
- return best_error;
-
- best_error *= num_colors;
-
- results.m_n = num_colors;
- results.m_block_color4 = !(best_x & 1);
- results.m_block_inten_table = (best_x >> 1) & 7;
- memset(results.m_pSelectors, (best_x >> 4) & 3, num_colors);
-
- const uint best_packed_c0 = (best_x >> 8) & 255;
- results.m_block_color_unscaled[best_i] = static_cast<uint8>(best_packed_c0);
- results.m_block_color_unscaled[s_next_comp[best_i]] = static_cast<uint8>(best_packed_c1);
- results.m_block_color_unscaled[s_next_comp[best_i + 1]] = static_cast<uint8>(best_packed_c2);
- results.m_error = best_error;
-
- return best_error;
- }
-
- // Function originally from RYG's public domain real-time DXT1 compressor, modified for 555.
- static void dither_block_555(color_quad_u8* dest, const color_quad_u8* block)
- {
- int err[8],*ep1 = err,*ep2 = err+4;
- uint8 *quant = g_quant5_tab+8;
-
- memset(dest, 0xFF, sizeof(color_quad_u8)*16);
-
- // process channels seperately
- for(int ch=0;ch<3;ch++)
- {
- uint8* bp = (uint8*)block;
- uint8* dp = (uint8*)dest;
-
- bp += ch; dp += ch;
-
- memset(err,0, sizeof(err));
- for(int y = 0; y < 4; y++)
- {
- // pixel 0
- dp[ 0] = quant[bp[ 0] + ((3*ep2[1] + 5*ep2[0]) >> 4)];
- ep1[0] = bp[ 0] - dp[ 0];
-
- // pixel 1
- dp[ 4] = quant[bp[ 4] + ((7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]) >> 4)];
- ep1[1] = bp[ 4] - dp[ 4];
-
- // pixel 2
- dp[ 8] = quant[bp[ 8] + ((7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]) >> 4)];
- ep1[2] = bp[ 8] - dp[ 8];
-
- // pixel 3
- dp[12] = quant[bp[12] + ((7*ep1[2] + 5*ep2[3] + ep2[2]) >> 4)];
- ep1[3] = bp[12] - dp[12];
-
- // advance to next line
- int* tmp = ep1; ep1 = ep2; ep2 = tmp;
- bp += 16;
- dp += 16;
- }
- }
- }
-
- unsigned int pack_etc1_block(void* pETC1_block, const unsigned int* pSrc_pixels_rgba, etc1_pack_params& pack_params)
- {
- const color_quad_u8* pSrc_pixels = reinterpret_cast<const color_quad_u8*>(pSrc_pixels_rgba);
- etc1_block& dst_block = *static_cast<etc1_block*>(pETC1_block);
-
-#ifdef RG_ETC1_BUILD_DEBUG
- // Ensure all alpha values are 0xFF.
- for (uint i = 0; i < 16; i++)
- {
- RG_ETC1_ASSERT(pSrc_pixels[i].a == 255);
- }
-#endif
-
- color_quad_u8 src_pixel0(pSrc_pixels[0]);
-
- // Check for solid block.
- const uint32 first_pixel_u32 = pSrc_pixels->m_u32;
- int r;
- for (r = 15; r >= 1; --r)
- if (pSrc_pixels[r].m_u32 != first_pixel_u32)
- break;
- if (!r)
- return static_cast<unsigned int>(16 * pack_etc1_block_solid_color(dst_block, &pSrc_pixels[0].r, pack_params));
-
- color_quad_u8 dithered_pixels[16];
- if (pack_params.m_dithering)
- {
- dither_block_555(dithered_pixels, pSrc_pixels);
- pSrc_pixels = dithered_pixels;
- }
-
- etc1_optimizer optimizer;
-
- uint64 best_error = cUINT64_MAX;
- uint best_flip = false, best_use_color4 = false;
-
- uint8 best_selectors[2][8];
- etc1_optimizer::results best_results[2];
- for (uint i = 0; i < 2; i++)
- {
- best_results[i].m_n = 8;
- best_results[i].m_pSelectors = best_selectors[i];
- }
-
- uint8 selectors[3][8];
- etc1_optimizer::results results[3];
-
- for (uint i = 0; i < 3; i++)
- {
- results[i].m_n = 8;
- results[i].m_pSelectors = selectors[i];
- }
-
- color_quad_u8 subblock_pixels[8];
-
- etc1_optimizer::params params(pack_params);
- params.m_num_src_pixels = 8;
- params.m_pSrc_pixels = subblock_pixels;
-
- for (uint flip = 0; flip < 2; flip++)
- {
- for (uint use_color4 = 0; use_color4 < 2; use_color4++)
- {
- uint64 trial_error = 0;
-
- uint subblock;
- for (subblock = 0; subblock < 2; subblock++)
- {
- if (flip)
- memcpy(subblock_pixels, pSrc_pixels + subblock * 8, sizeof(color_quad_u8) * 8);
- else
- {
- const color_quad_u8* pSrc_col = pSrc_pixels + subblock * 2;
- subblock_pixels[0] = pSrc_col[0]; subblock_pixels[1] = pSrc_col[4]; subblock_pixels[2] = pSrc_col[8]; subblock_pixels[3] = pSrc_col[12];
- subblock_pixels[4] = pSrc_col[1]; subblock_pixels[5] = pSrc_col[5]; subblock_pixels[6] = pSrc_col[9]; subblock_pixels[7] = pSrc_col[13];
- }
-
- results[2].m_error = cUINT64_MAX;
- if ((params.m_quality >= cMediumQuality) && ((subblock) || (use_color4)))
- {
- const uint32 subblock_pixel0_u32 = subblock_pixels[0].m_u32;
- for (r = 7; r >= 1; --r)
- if (subblock_pixels[r].m_u32 != subblock_pixel0_u32)
- break;
- if (!r)
- {
- pack_etc1_block_solid_color_constrained(results[2], 8, &subblock_pixels[0].r, pack_params, !use_color4, (subblock && !use_color4) ? &results[0].m_block_color_unscaled : NULL);
- }
- }
-
- params.m_use_color4 = (use_color4 != 0);
- params.m_constrain_against_base_color5 = false;
-
- if ((!use_color4) && (subblock))
- {
- params.m_constrain_against_base_color5 = true;
- params.m_base_color5 = results[0].m_block_color_unscaled;
- }
-
- if (params.m_quality == cHighQuality)
- {
- static const int s_scan_delta_0_to_4[] = { -4, -3, -2, -1, 0, 1, 2, 3, 4 };
- params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_0_to_4);
- params.m_pScan_deltas = s_scan_delta_0_to_4;
- }
- else if (params.m_quality == cMediumQuality)
- {
- static const int s_scan_delta_0_to_1[] = { -1, 0, 1 };
- params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_0_to_1);
- params.m_pScan_deltas = s_scan_delta_0_to_1;
- }
- else
- {
- static const int s_scan_delta_0[] = { 0 };
- params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_0);
- params.m_pScan_deltas = s_scan_delta_0;
- }
-
- optimizer.init(params, results[subblock]);
- if (!optimizer.compute())
- break;
-
- if (params.m_quality >= cMediumQuality)
- {
- // TODO: Fix fairly arbitrary/unrefined thresholds that control how far away to scan for potentially better solutions.
- const uint refinement_error_thresh0 = 3000;
- const uint refinement_error_thresh1 = 6000;
- if (results[subblock].m_error > refinement_error_thresh0)
- {
- if (params.m_quality == cMediumQuality)
- {
- static const int s_scan_delta_2_to_3[] = { -3, -2, 2, 3 };
- params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_2_to_3);
- params.m_pScan_deltas = s_scan_delta_2_to_3;
- }
- else
- {
- static const int s_scan_delta_5_to_5[] = { -5, 5 };
- static const int s_scan_delta_5_to_8[] = { -8, -7, -6, -5, 5, 6, 7, 8 };
- if (results[subblock].m_error > refinement_error_thresh1)
- {
- params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_5_to_8);
- params.m_pScan_deltas = s_scan_delta_5_to_8;
- }
- else
- {
- params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_5_to_5);
- params.m_pScan_deltas = s_scan_delta_5_to_5;
- }
- }
-
- if (!optimizer.compute())
- break;
- }
-
- if (results[2].m_error < results[subblock].m_error)
- results[subblock] = results[2];
- }
-
- trial_error += results[subblock].m_error;
- if (trial_error >= best_error)
- break;
- }
-
- if (subblock < 2)
- continue;
-
- best_error = trial_error;
- best_results[0] = results[0];
- best_results[1] = results[1];
- best_flip = flip;
- best_use_color4 = use_color4;
-
- } // use_color4
-
- } // flip
-
- int dr = best_results[1].m_block_color_unscaled.r - best_results[0].m_block_color_unscaled.r;
- int dg = best_results[1].m_block_color_unscaled.g - best_results[0].m_block_color_unscaled.g;
- int db = best_results[1].m_block_color_unscaled.b - best_results[0].m_block_color_unscaled.b;
- RG_ETC1_ASSERT(best_use_color4 || (rg_etc1::minimum(dr, dg, db) >= cETC1ColorDeltaMin) && (rg_etc1::maximum(dr, dg, db) <= cETC1ColorDeltaMax));
-
- if (best_use_color4)
- {
- dst_block.m_bytes[0] = static_cast<uint8>(best_results[1].m_block_color_unscaled.r | (best_results[0].m_block_color_unscaled.r << 4));
- dst_block.m_bytes[1] = static_cast<uint8>(best_results[1].m_block_color_unscaled.g | (best_results[0].m_block_color_unscaled.g << 4));
- dst_block.m_bytes[2] = static_cast<uint8>(best_results[1].m_block_color_unscaled.b | (best_results[0].m_block_color_unscaled.b << 4));
- }
- else
- {
- if (dr < 0) dr += 8; dst_block.m_bytes[0] = static_cast<uint8>((best_results[0].m_block_color_unscaled.r << 3) | dr);
- if (dg < 0) dg += 8; dst_block.m_bytes[1] = static_cast<uint8>((best_results[0].m_block_color_unscaled.g << 3) | dg);
- if (db < 0) db += 8; dst_block.m_bytes[2] = static_cast<uint8>((best_results[0].m_block_color_unscaled.b << 3) | db);
- }
-
- dst_block.m_bytes[3] = static_cast<uint8>( (best_results[1].m_block_inten_table << 2) | (best_results[0].m_block_inten_table << 5) | ((~best_use_color4 & 1) << 1) | best_flip );
-
- uint selector0 = 0, selector1 = 0;
- if (best_flip)
- {
- // flipped:
- // { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 },
- // { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 }
- //
- // { 0, 2 }, { 1, 2 }, { 2, 2 }, { 3, 2 },
- // { 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 }
- const uint8* pSelectors0 = best_results[0].m_pSelectors;
- const uint8* pSelectors1 = best_results[1].m_pSelectors;
- for (int x = 3; x >= 0; --x)
- {
- uint b;
- b = g_selector_index_to_etc1[pSelectors1[4 + x]];
- selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
- b = g_selector_index_to_etc1[pSelectors1[x]];
- selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
- b = g_selector_index_to_etc1[pSelectors0[4 + x]];
- selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
- b = g_selector_index_to_etc1[pSelectors0[x]];
- selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
- }
- }
- else
- {
- // non-flipped:
- // { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 },
- // { 1, 0 }, { 1, 1 }, { 1, 2 }, { 1, 3 }
- //
- // { 2, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 },
- // { 3, 0 }, { 3, 1 }, { 3, 2 }, { 3, 3 }
- for (int subblock = 1; subblock >= 0; --subblock)
- {
- const uint8* pSelectors = best_results[subblock].m_pSelectors + 4;
- for (uint i = 0; i < 2; i++)
- {
- uint b;
- b = g_selector_index_to_etc1[pSelectors[3]];
- selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
- b = g_selector_index_to_etc1[pSelectors[2]];
- selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
- b = g_selector_index_to_etc1[pSelectors[1]];
- selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
- b = g_selector_index_to_etc1[pSelectors[0]];
- selector0 = (selector0 << 1) | (b & 1);selector1 = (selector1 << 1) | (b >> 1);
-
- pSelectors -= 4;
- }
- }
- }
-
- dst_block.m_bytes[4] = static_cast<uint8>(selector1 >> 8); dst_block.m_bytes[5] = static_cast<uint8>(selector1 & 0xFF);
- dst_block.m_bytes[6] = static_cast<uint8>(selector0 >> 8); dst_block.m_bytes[7] = static_cast<uint8>(selector0 & 0xFF);
-
- return static_cast<unsigned int>(best_error);
- }
-
-} // namespace rg_etc1
diff --git a/thirdparty/rg-etc1/rg_etc1.h b/thirdparty/rg-etc1/rg_etc1.h
deleted file mode 100644
index 9ce89a6cc6..0000000000
--- a/thirdparty/rg-etc1/rg_etc1.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// File: rg_etc1.h - Fast, high quality ETC1 block packer/unpacker - Rich Geldreich <richgel99@gmail.com>
-// Please see ZLIB license at the end of this file.
-#pragma once
-
-namespace rg_etc1
-{
- // Unpacks an 8-byte ETC1 compressed block to a block of 4x4 32bpp RGBA pixels.
- // Returns false if the block is invalid. Invalid blocks will still be unpacked with clamping.
- // This function is thread safe, and does not dynamically allocate any memory.
- // If preserve_alpha is true, the alpha channel of the destination pixels will not be overwritten. Otherwise, alpha will be set to 255.
- bool unpack_etc1_block(const void *pETC1_block, unsigned int* pDst_pixels_rgba, bool preserve_alpha = false);
-
- // Quality setting = the higher the quality, the slower.
- // To pack large textures, it is highly recommended to call pack_etc1_block() in parallel, on different blocks, from multiple threads (particularly when using cHighQuality).
- enum etc1_quality
- {
- cLowQuality,
- cMediumQuality,
- cHighQuality,
- };
-
- struct etc1_pack_params
- {
- etc1_quality m_quality;
- bool m_dithering;
-
- inline etc1_pack_params()
- {
- clear();
- }
-
- void clear()
- {
- m_quality = cHighQuality;
- m_dithering = false;
- }
- };
-
- // Important: pack_etc1_block_init() must be called before calling pack_etc1_block().
- void pack_etc1_block_init();
-
- // Packs a 4x4 block of 32bpp RGBA pixels to an 8-byte ETC1 block.
- // 32-bit RGBA pixels must always be arranged as (R,G,B,A) (R first, A last) in memory, independent of platform endianness. A should always be 255.
- // Returns squared error of result.
- // This function is thread safe, and does not dynamically allocate any memory.
- // pack_etc1_block() does not currently support "perceptual" colorspace metrics - it primarily optimizes for RGB RMSE.
- unsigned int pack_etc1_block(void* pETC1_block, const unsigned int* pSrc_pixels_rgba, etc1_pack_params& pack_params);
-
-} // namespace rg_etc1
-
-//------------------------------------------------------------------------------
-//
-// rg_etc1 uses the ZLIB license:
-// http://opensource.org/licenses/Zlib
-//
-// Copyright (c) 2012 Rich Geldreich
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would be
-// appreciated but is not required.
-//
-// 2. Altered source versions must be plainly marked as such, and must not be
-// misrepresented as being the original software.
-//
-// 3. This notice may not be removed or altered from any source distribution.
-//
-//------------------------------------------------------------------------------