From c2873ff29f129ef54bdd128965204e64925bf237 Mon Sep 17 00:00:00 2001 From: temple-fed Date: Wed, 29 Oct 2025 21:02:40 +0200 Subject: [PATCH 1/2] Const correctness, Added g++ Makefile, etc Functions taking string literals as char * have been made into const char *. The folder structure has been modified and vc6 build files has been moved to build folder. Slight readme change. Enums declared static have been fixed. --- .gitignore | 1 + README.md | 3 +- build/Makefile | 9 + {vc9 => build/vc9}/Debug/etcpack.exe | Bin {vc9 => build/vc9}/Release/etcpack.exe | Bin {vc9 => build/vc9}/etcpack.sln | 40 +- {vc9 => build/vc9}/etcpack.vcproj | 410 +- source/{etcdec.cxx => etcdec.cpp} | 3686 +-- source/{etcpack.cxx => etcpack.cpp} | 32172 ++++++++++++----------- source/{image.cxx => image.cpp} | 922 +- source/{image.h => image.hpp} | 130 +- 11 files changed, 18694 insertions(+), 18679 deletions(-) create mode 100644 .gitignore create mode 100644 build/Makefile rename {vc9 => build/vc9}/Debug/etcpack.exe (100%) mode change 100755 => 100644 rename {vc9 => build/vc9}/Release/etcpack.exe (100%) mode change 100755 => 100644 rename {vc9 => build/vc9}/etcpack.sln (97%) mode change 100755 => 100644 rename {vc9 => build/vc9}/etcpack.vcproj (95%) mode change 100755 => 100644 rename source/{etcdec.cxx => etcdec.cpp} (96%) mode change 100755 => 100644 rename source/{etcpack.cxx => etcpack.cpp} (97%) mode change 100755 => 100644 rename source/{image.cxx => image.cpp} (92%) mode change 100755 => 100644 rename source/{image.h => image.hpp} (78%) mode change 100755 => 100644 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65776c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/bin/ \ No newline at end of file diff --git a/README.md b/README.md index b58e643..ecfa099 100755 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # ETCPACK Ericsson has developed a texture compression system called "Ericsson Texture Compression". The software for compressing images and textures to that format is called ETCPACK. +--- -The latest version of this software includes the possibility to compress images to the new formats introduced as mandatory in the Khronos standards OpenGL ES 3.0 and OpenGL 4.3. We call this package the ETC2-package of codecs, where ETC stands for Ericsson Texture Compression. For instance the new RGB8 ETC2 codec allows higher-quality compression than ETC1. It is also backward compatible; an old ETC1 texture can be decoded using ETC2-capable handsets. There are also new formats for RGBA textures and single-channel (R) and double-channel (RG) textures. For a complete list of codecs, see Appendix C in the OpenGL ES 3.0 standard. The new software also compresses old ETC1 textures. The software can be used by independent hardware vendors who want to include ETC2-package-compression in the tool chains that they give or sell to game developers. It can also be used directly by game developers who want to create games for OpenGL ES 3.0 capable handsets. The software includes source code for the command-line-program etcpack. \ No newline at end of file +The latest version of this software includes the possibility to compress images to the new formats introduced as mandatory in the Khronos standards OpenGL ES 3.0 and OpenGL 4.3. We call this package the ETC2-package of codecs, where ETC stands for Ericsson Texture Compression. For instance the new RGB8 ETC2 codec allows higher-quality compression than ETC1. It is also backward compatible; an old ETC1 texture can be decoded using ETC2-capable handsets. There are also new formats for RGBA textures and single-channel (R) and double-channel (RG) textures. For a complete list of codecs, see Appendix C in the OpenGL ES 3.0 standard. The new software also compresses old ETC1 textures. The software can be used by independent hardware vendors who want to include ETC2-package-compression in the tool chains that they give or sell to game developers. It can also be used directly by game developers who want to create games for OpenGL ES 3.0 capable handsets. The software includes source code for the command-line-program etcpack. diff --git a/build/Makefile b/build/Makefile new file mode 100644 index 0000000..8e22735 --- /dev/null +++ b/build/Makefile @@ -0,0 +1,9 @@ +# COMPILE_FLAGS = -Wall -Wextra -Wpedantic -Wconversion -g +COMPILE_FLAGS = -O3 + +bin_path = ../bin +source_path = ../source +test_path = ../testing + +all: + g++ $(COMPILE_FLAGS) $(source_path)/etcdec.cpp $(source_path)/etcpack.cpp $(source_path)/image.cpp -o $(bin_path)/etcpack diff --git a/vc9/Debug/etcpack.exe b/build/vc9/Debug/etcpack.exe old mode 100755 new mode 100644 similarity index 100% rename from vc9/Debug/etcpack.exe rename to build/vc9/Debug/etcpack.exe diff --git a/vc9/Release/etcpack.exe b/build/vc9/Release/etcpack.exe old mode 100755 new mode 100644 similarity index 100% rename from vc9/Release/etcpack.exe rename to build/vc9/Release/etcpack.exe diff --git a/vc9/etcpack.sln b/build/vc9/etcpack.sln old mode 100755 new mode 100644 similarity index 97% rename from vc9/etcpack.sln rename to build/vc9/etcpack.sln index f97847c..b560aa3 --- a/vc9/etcpack.sln +++ b/build/vc9/etcpack.sln @@ -1,20 +1,20 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "etcpack", "etcpack.vcproj", "{8800817E-EB9A-4ECC-9FE5-90526AF8644A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8800817E-EB9A-4ECC-9FE5-90526AF8644A}.Debug|Win32.ActiveCfg = Debug|Win32 - {8800817E-EB9A-4ECC-9FE5-90526AF8644A}.Debug|Win32.Build.0 = Debug|Win32 - {8800817E-EB9A-4ECC-9FE5-90526AF8644A}.Release|Win32.ActiveCfg = Release|Win32 - {8800817E-EB9A-4ECC-9FE5-90526AF8644A}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "etcpack", "etcpack.vcproj", "{8800817E-EB9A-4ECC-9FE5-90526AF8644A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8800817E-EB9A-4ECC-9FE5-90526AF8644A}.Debug|Win32.ActiveCfg = Debug|Win32 + {8800817E-EB9A-4ECC-9FE5-90526AF8644A}.Debug|Win32.Build.0 = Debug|Win32 + {8800817E-EB9A-4ECC-9FE5-90526AF8644A}.Release|Win32.ActiveCfg = Release|Win32 + {8800817E-EB9A-4ECC-9FE5-90526AF8644A}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vc9/etcpack.vcproj b/build/vc9/etcpack.vcproj old mode 100755 new mode 100644 similarity index 95% rename from vc9/etcpack.vcproj rename to build/vc9/etcpack.vcproj index 33a097d..cadd352 --- a/vc9/etcpack.vcproj +++ b/build/vc9/etcpack.vcproj @@ -1,205 +1,205 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/etcdec.cxx b/source/etcdec.cpp old mode 100755 new mode 100644 similarity index 96% rename from source/etcdec.cxx rename to source/etcdec.cpp index a87a268..f2151f1 --- a/source/etcdec.cxx +++ b/source/etcdec.cpp @@ -1,1842 +1,1844 @@ -/** - -@~English -@page licensing Licensing - -@section etcdec etcdec.cxx License - -etcdec.cxx is made available under the terms and conditions of the following -License Agreement. - -Software License Agreement - -PLEASE REVIEW THE FOLLOWING TERMS AND CONDITIONS PRIOR TO USING THE -ERICSSON TEXTURE COMPRESSION CODEC SOFTWARE (THE "SOFTWARE"). THE USE -OF THE SOFTWARE IS SUBJECT TO THE TERMS AND CONDITIONS OF THE -FOLLOWING SOFTWARE LICENSE AGREEMENT (THE "SLA"). IF YOU DO NOT ACCEPT -SUCH TERMS AND CONDITIONS YOU MAY NOT USE THE SOFTWARE. - -Subject to the terms and conditions of the SLA, the licensee of the -Software (the "Licensee") hereby, receives a non-exclusive, -non-transferable, limited, free-of-charge, perpetual and worldwide -license, to copy, use, distribute and modify the Software, but only -for the purpose of developing, manufacturing, selling, using and -distributing products including the Software in binary form, which -products are used for compression and/or decompression according to -the Khronos standard specifications OpenGL, OpenGL ES and -WebGL. Notwithstanding anything of the above, Licensee may distribute -[etcdec.cxx] in source code form provided (i) it is in unmodified -form; and (ii) it is included in software owned by Licensee. - -If Licensee institutes, or threatens to institute, patent litigation -against Ericsson or Ericsson's affiliates for using the Software for -developing, having developed, manufacturing, having manufactured, -selling, offer for sale, importing, using, leasing, operating, -repairing and/or distributing products (i) within the scope of the -Khronos framework; or (ii) using software or other intellectual -property rights owned by Ericsson or its affiliates and provided under -the Khronos framework, Ericsson shall have the right to terminate this -SLA with immediate effect. Moreover, if Licensee institutes, or -threatens to institute, patent litigation against any other licensee -of the Software for using the Software in products within the scope of -the Khronos framework, Ericsson shall have the right to terminate this -SLA with immediate effect. However, should Licensee institute, or -threaten to institute, patent litigation against any other licensee of -the Software based on such other licensee's use of any other software -together with the Software, then Ericsson shall have no right to -terminate this SLA. - -This SLA does not transfer to Licensee any ownership to any Ericsson -or third party intellectual property rights. All rights not expressly -granted by Ericsson under this SLA are hereby expressly -reserved. Furthermore, nothing in this SLA shall be construed as a -right to use or sell products in a manner which conveys or purports to -convey whether explicitly, by principles of implied license, or -otherwise, any rights to any third party, under any patent of Ericsson -or of Ericsson's affiliates covering or relating to any combination of -the Software with any other software or product (not licensed -hereunder) where the right applies specifically to the combination and -not to the software or product itself. - -THE SOFTWARE IS PROVIDED "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF -ANY KIND, EXTENDS NO WARRANTIES OR CONDITIONS OF ANY KIND, EITHER -EXPRESS, IMPLIED OR STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, -IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS OF TITLE, -MERCHANTABILITY, SATISFACTORY QUALITY, SUITABILITY, AND FITNESS FOR A -PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE -OF THE SOFTWARE IS WITH THE LICENSEE. SHOULD THE SOFTWARE PROVE -DEFECTIVE, THE LICENSEE ASSUMES THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. ERICSSON MAKES NO WARRANTY THAT THE MANUFACTURE, -SALE, OFFERING FOR SALE, DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER -THE SLA WILL BE FREE FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER -INTELLECTUAL PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE -LICENSE AND THE SLA ARE SUBJECT TO LICENSEE'S SOLE RESPONSIBILITY TO -MAKE SUCH DETERMINATION AND ACQUIRE SUCH LICENSES AS MAY BE NECESSARY -WITH RESPECT TO PATENTS, COPYRIGHT AND OTHER INTELLECTUAL PROPERTY OF -THIRD PARTIES. - -THE LICENSEE ACKNOWLEDGES AND ACCEPTS THAT THE SOFTWARE (I) IS NOT -LICENSED FOR; (II) IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY -NOT BE USED FOR; ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT -LIMITED TO OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR -NETWORKS, AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR -ANY OTHER COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR -COMMUNICATION SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE SOFTWARE -COULD LEAD TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR -ENVIRONMENTAL DAMAGE. LICENSEE'S RIGHTS UNDER THIS LICENSE WILL -TERMINATE AUTOMATICALLY AND IMMEDIATELY WITHOUT NOTICE IF LICENSEE -FAILS TO COMPLY WITH THIS PARAGRAPH. - -IN NO EVENT SHALL ERICSSON BE LIABLE FOR ANY DAMAGES WHATSOEVER, -INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, -INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING -BUT NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY -OTHER COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY THE LICENSEE OR THIRD -PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER -SOFTWARE) REGARDLESS OF THE THEORY OF LIABILITY (CONTRACT, TORT, OR -OTHERWISE), EVEN IF THE LICENSEE OR ANY OTHER PARTY HAS BEEN ADVISED -OF THE POSSIBILITY OF SUCH DAMAGES. - -Licensee acknowledges that "ERICSSON ///" is the corporate trademark -of Telefonaktiebolaget LM Ericsson and that both "Ericsson" and the -figure "///" are important features of the trade names of -Telefonaktiebolaget LM Ericsson. Nothing contained in these terms and -conditions shall be deemed to grant Licensee any right, title or -interest in the word "Ericsson" or the figure "///". No delay or -omission by Ericsson to exercise any right or power shall impair any -such right or power to be construed to be a waiver thereof. Consent by -Ericsson to, or waiver of, a breach by the Licensee shall not -constitute consent to, waiver of, or excuse for any other different or -subsequent breach. - -This SLA shall be governed by the substantive law of Sweden. Any -dispute, controversy or claim arising out of or in connection with -this SLA, or the breach, termination or invalidity thereof, shall be -submitted to the exclusive jurisdiction of the Swedish Courts. - -*/ - -//// etcpack v2.74 -//// -//// NO WARRANTY -//// -//// BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE THE PROGRAM IS PROVIDED -//// "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF ANY KIND, EXTENDS NO -//// WARRANTIES OR CONDITIONS OF ANY KIND; EITHER EXPRESS, IMPLIED OR -//// STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, IMPLIED OR -//// STATUTORY WARRANTIES OR CONDITIONS OF TITLE, MERCHANTABILITY, -//// SATISFACTORY QUALITY, SUITABILITY AND FITNESS FOR A PARTICULAR -//// PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -//// PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME -//// THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ERICSSON -//// MAKES NO WARRANTY THAT THE MANUFACTURE, SALE, OFFERING FOR SALE, -//// DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER THE LICENSE WILL BE FREE -//// FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER INTELLECTUAL -//// PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE LICENSE IS SUBJECT -//// TO YOUR SOLE RESPONSIBILITY TO MAKE SUCH DETERMINATION AND ACQUIRE -//// SUCH LICENSES AS MAY BE NECESSARY WITH RESPECT TO PATENTS, COPYRIGHT -//// AND OTHER INTELLECTUAL PROPERTY OF THIRD PARTIES. -//// -//// FOR THE AVOIDANCE OF DOUBT THE PROGRAM (I) IS NOT LICENSED FOR; (II) -//// IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY NOT BE USED FOR; -//// ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT LIMITED TO -//// OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR NETWORKS, -//// AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR ANY OTHER -//// COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR COMMUNICATION -//// SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE PROGRAM COULD LEAD TO -//// DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR ENVIRONMENTAL -//// DAMAGE. YOUR RIGHTS UNDER THIS LICENSE WILL TERMINATE AUTOMATICALLY -//// AND IMMEDIATELY WITHOUT NOTICE IF YOU FAIL TO COMPLY WITH THIS -//// PARAGRAPH. -//// -//// IN NO EVENT WILL ERICSSON, BE LIABLE FOR ANY DAMAGES WHATSOEVER, -//// INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, -//// INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN -//// CONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT -//// NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY OTHER -//// COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING RENDERED -//// INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF -//// THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) REGARDLESS OF THE -//// THEORY OF LIABILITY (CONTRACT, TORT OR OTHERWISE), EVEN IF SUCH HOLDER -//// OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -//// -//// (C) Ericsson AB 2005-2013. All Rights Reserved. -//// - -#include -#include - -// Typedefs -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef short int16; - -// Macros to help with bit extraction/insertion -#define SHIFT(size,startpos) ((startpos)-(size)+1) -#define MASK(size, startpos) (((2<<(size-1))-1) << SHIFT(size,startpos)) -#define PUTBITS( dest, data, size, startpos) dest = ((dest & ~MASK(size, startpos)) | ((data << SHIFT(size, startpos)) & MASK(size,startpos))) -#define SHIFTHIGH(size, startpos) (((startpos)-32)-(size)+1) -#define MASKHIGH(size, startpos) (((1<<(size))-1) << SHIFTHIGH(size,startpos)) -#define PUTBITSHIGH(dest, data, size, startpos) dest = ((dest & ~MASKHIGH(size, startpos)) | ((data << SHIFTHIGH(size, startpos)) & MASKHIGH(size,startpos))) -#define GETBITS(source, size, startpos) (( (source) >> ((startpos)-(size)+1) ) & ((1<<(size)) -1)) -#define GETBITSHIGH(source, size, startpos) (( (source) >> (((startpos)-32)-(size)+1) ) & ((1<<(size)) -1)) -#ifndef PGMOUT -#define PGMOUT 1 -#endif -// Thumb macros and definitions -#define R_BITS59T 4 -#define G_BITS59T 4 -#define B_BITS59T 4 -#define R_BITS58H 4 -#define G_BITS58H 4 -#define B_BITS58H 4 -#define MAXIMUM_ERROR (255*255*16*1000) -#define R 0 -#define G 1 -#define B 2 -#define BLOCKHEIGHT 4 -#define BLOCKWIDTH 4 -#define BINPOW(power) (1<<(power)) -#define TABLE_BITS_59T 3 -#define TABLE_BITS_58H 3 - -// Helper Macros -#define CLAMP(ll,x,ul) (((x)<(ll)) ? (ll) : (((x)>(ul)) ? (ul) : (x))) -#define JAS_ROUND(x) (((x) < 0.0 ) ? ((int)((x)-0.5)) : ((int)((x)+0.5))) - -#define RED_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+0] -#define GREEN_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+1] -#define BLUE_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+2] -#define ALPHA_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+3] - - -// Global tables -static uint8 table59T[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 59 bit T-mode -static uint8 table58H[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 58 bit H-mode -static int compressParams[16][4] = {{-8, -2, 2, 8}, {-8, -2, 2, 8}, {-17, -5, 5, 17}, {-17, -5, 5, 17}, {-29, -9, 9, 29}, {-29, -9, 9, 29}, {-42, -13, 13, 42}, {-42, -13, 13, 42}, {-60, -18, 18, 60}, {-60, -18, 18, 60}, {-80, -24, 24, 80}, {-80, -24, 24, 80}, {-106, -33, 33, 106}, {-106, -33, 33, 106}, {-183, -47, 47, 183}, {-183, -47, 47, 183}}; -static int unscramble[4] = {2, 3, 1, 0}; -int alphaTableInitialized = 0; -int alphaTable[256][8]; -int alphaBase[16][4] = { - {-15,-9,-6,-3}, - {-13,-10,-7,-3}, - {-13,-8,-5,-2}, - {-13,-6,-4,-2}, - {-12,-8,-6,-3}, - {-11,-9,-7,-3}, - {-11,-8,-7,-4}, - {-11,-8,-5,-3}, - { -10,-8,-6,-2}, - { -10,-8,-5,-2}, - { -10,-8,-4,-2}, - { -10,-7,-5,-2}, - { -10,-7,-4,-3}, - { -10,-3,-2, -1}, - { -9,-8,-6,-4}, - { -9,-7,-5,-3} - }; - -// Global variables -int formatSigned = 0; - -// Enums - enum{PATTERN_H = 0, - PATTERN_T = 1}; - - -// Code used to create the valtab -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void setupAlphaTable() -{ - if(alphaTableInitialized) - return; - alphaTableInitialized = 1; - - //read table used for alpha compression - int buf; - for(int i = 16; i<32; i++) - { - for(int j=0; j<8; j++) - { - buf=alphaBase[i-16][3-j%4]; - if(j<4) - alphaTable[i][j]=buf; - else - alphaTable[i][j]=(-buf-1); - } - } - - //beyond the first 16 values, the rest of the table is implicit.. so calculate that! - for(int i=0; i<256; i++) - { - //fill remaining slots in table with multiples of the first ones. - int mul = i/16; - int old = 16+i%16; - for(int j = 0; j<8; j++) - { - alphaTable[i][j]=alphaTable[old][j]*mul; - //note: we don't do clamping here, though we could, because we'll be clamped afterwards anyway. - } - } -} - -// Read a word in big endian style -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void read_big_endian_2byte_word(unsigned short *blockadr, FILE *f) -{ - uint8 bytes[2]; - unsigned short block; - - fread(&bytes[0], 1, 1, f); - fread(&bytes[1], 1, 1, f); - - block = 0; - block |= bytes[0]; - block = block << 8; - block |= bytes[1]; - - blockadr[0] = block; -} - -// Read a word in big endian style -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void read_big_endian_4byte_word(unsigned int *blockadr, FILE *f) -{ - uint8 bytes[4]; - unsigned int block; - - fread(&bytes[0], 1, 1, f); - fread(&bytes[1], 1, 1, f); - fread(&bytes[2], 1, 1, f); - fread(&bytes[3], 1, 1, f); - - block = 0; - block |= bytes[0]; - block = block << 8; - block |= bytes[1]; - block = block << 8; - block |= bytes[2]; - block = block << 8; - block |= bytes[3]; - - blockadr[0] = block; -} - -// The format stores the bits for the three extra modes in a roundabout way to be able to -// fit them without increasing the bit rate. This function converts them into something -// that is easier to work with. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void unstuff57bits(unsigned int planar_word1, unsigned int planar_word2, unsigned int &planar57_word1, unsigned int &planar57_word2) -{ - // Get bits from twotimer configuration for 57 bits - // - // Go to this bit layout: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // |R0 |G01G02 |B01B02 ;B03 |RH1 |RH2|GH | - // ----------------------------------------------------------------------------------------------- - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // ----------------------------------------------------------------------------------------------- - // |BH |RV |GV |BV | not used | - // ----------------------------------------------------------------------------------------------- - // - // From this: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ------------------------------------------------------------------------------------------------ - // |//|R0 |G01|/|G02 |B01|/ // //|B02 |//|B03 |RH1 |df|RH2| - // ------------------------------------------------------------------------------------------------ - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // ----------------------------------------------------------------------------------------------- - // |GH |BH |RV |GV |BV | - // ----------------------------------------------------------------------------------------------- - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - uint8 RO, GO1, GO2, BO1, BO2, BO3, RH1, RH2, GH, BH, RV, GV, BV; - - RO = GETBITSHIGH( planar_word1, 6, 62); - GO1 = GETBITSHIGH( planar_word1, 1, 56); - GO2 = GETBITSHIGH( planar_word1, 6, 54); - BO1 = GETBITSHIGH( planar_word1, 1, 48); - BO2 = GETBITSHIGH( planar_word1, 2, 44); - BO3 = GETBITSHIGH( planar_word1, 3, 41); - RH1 = GETBITSHIGH( planar_word1, 5, 38); - RH2 = GETBITSHIGH( planar_word1, 1, 32); - GH = GETBITS( planar_word2, 7, 31); - BH = GETBITS( planar_word2, 6, 24); - RV = GETBITS( planar_word2, 6, 18); - GV = GETBITS( planar_word2, 7, 12); - BV = GETBITS( planar_word2, 6, 5); - - planar57_word1 = 0; planar57_word2 = 0; - PUTBITSHIGH( planar57_word1, RO, 6, 63); - PUTBITSHIGH( planar57_word1, GO1, 1, 57); - PUTBITSHIGH( planar57_word1, GO2, 6, 56); - PUTBITSHIGH( planar57_word1, BO1, 1, 50); - PUTBITSHIGH( planar57_word1, BO2, 2, 49); - PUTBITSHIGH( planar57_word1, BO3, 3, 47); - PUTBITSHIGH( planar57_word1, RH1, 5, 44); - PUTBITSHIGH( planar57_word1, RH2, 1, 39); - PUTBITSHIGH( planar57_word1, GH, 7, 38); - PUTBITS( planar57_word2, BH, 6, 31); - PUTBITS( planar57_word2, RV, 6, 25); - PUTBITS( planar57_word2, GV, 7, 19); - PUTBITS( planar57_word2, BV, 6, 12); -} - -// The format stores the bits for the three extra modes in a roundabout way to be able to -// fit them without increasing the bit rate. This function converts them into something -// that is easier to work with. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void unstuff58bits(unsigned int thumbH_word1, unsigned int thumbH_word2, unsigned int &thumbH58_word1, unsigned int &thumbH58_word2) -{ - // Go to this layout: - // - // |63 62 61 60 59 58|57 56 55 54 53 52 51|50 49|48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33|32 | - // |-------empty-----|part0---------------|part1|part2------------------------------------------|part3| - // - // from this: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------| - // |//|part0 |// // //|part1|//|part2 |df|part3| - // --------------------------------------------------------------------------------------------------| - - unsigned int part0, part1, part2, part3; - - // move parts - part0 = GETBITSHIGH( thumbH_word1, 7, 62); - part1 = GETBITSHIGH( thumbH_word1, 2, 52); - part2 = GETBITSHIGH( thumbH_word1,16, 49); - part3 = GETBITSHIGH( thumbH_word1, 1, 32); - thumbH58_word1 = 0; - PUTBITSHIGH( thumbH58_word1, part0, 7, 57); - PUTBITSHIGH( thumbH58_word1, part1, 2, 50); - PUTBITSHIGH( thumbH58_word1, part2, 16, 48); - PUTBITSHIGH( thumbH58_word1, part3, 1, 32); - - thumbH58_word2 = thumbH_word2; -} - -// The format stores the bits for the three extra modes in a roundabout way to be able to -// fit them without increasing the bit rate. This function converts them into something -// that is easier to work with. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void unstuff59bits(unsigned int thumbT_word1, unsigned int thumbT_word2, unsigned int &thumbT59_word1, unsigned int &thumbT59_word2) -{ - // Get bits from twotimer configuration 59 bits. - // - // Go to this bit layout: - // - // |63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| - // |----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| - // - // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| - // |----------------------------------------index bits---------------------------------------------| - // - // - // From this: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // |// // //|R0a |//|R0b |G0 |B0 |R1 |G1 |B1 |da |df|db| - // ----------------------------------------------------------------------------------------------- - // - // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| - // |----------------------------------------index bits---------------------------------------------| - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt| - // ------------------------------------------------------------------------------------------------ - - uint8 R0a; - - // Fix middle part - thumbT59_word1 = thumbT_word1 >> 1; - // Fix db (lowest bit of d) - PUTBITSHIGH( thumbT59_word1, thumbT_word1, 1, 32); - // Fix R0a (top two bits of R0) - R0a = GETBITSHIGH( thumbT_word1, 2, 60); - PUTBITSHIGH( thumbT59_word1, R0a, 2, 58); - - // Zero top part (not needed) - PUTBITSHIGH( thumbT59_word1, 0, 5, 63); - - thumbT59_word2 = thumbT_word2; -} - -// The color bits are expanded to the full color -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressColor(int R_B, int G_B, int B_B, uint8 (colors_RGB444)[2][3], uint8 (colors)[2][3]) -{ - // The color should be retrieved as: - // - // c = round(255/(r_bits^2-1))*comp_color - // - // This is similar to bit replication - // - // Note -- this code only work for bit replication from 4 bits and up --- 3 bits needs - // two copy operations. - - colors[0][R] = (colors_RGB444[0][R] << (8 - R_B)) | (colors_RGB444[0][R] >> (R_B - (8-R_B)) ); - colors[0][G] = (colors_RGB444[0][G] << (8 - G_B)) | (colors_RGB444[0][G] >> (G_B - (8-G_B)) ); - colors[0][B] = (colors_RGB444[0][B] << (8 - B_B)) | (colors_RGB444[0][B] >> (B_B - (8-B_B)) ); - colors[1][R] = (colors_RGB444[1][R] << (8 - R_B)) | (colors_RGB444[1][R] >> (R_B - (8-R_B)) ); - colors[1][G] = (colors_RGB444[1][G] << (8 - G_B)) | (colors_RGB444[1][G] >> (G_B - (8-G_B)) ); - colors[1][B] = (colors_RGB444[1][B] << (8 - B_B)) | (colors_RGB444[1][B] >> (B_B - (8-B_B)) ); -} - -void calculatePaintColors59T(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]) -{ - ////////////////////////////////////////////// - // - // C3 C1 C4----C1---C2 - // | | | - // | | | - // |-------| | - // | | | - // | | | - // C4 C2 C3 - // - ////////////////////////////////////////////// - - // C4 - possible_colors[3][R] = CLAMP(0,colors[1][R] - table59T[d],255); - possible_colors[3][G] = CLAMP(0,colors[1][G] - table59T[d],255); - possible_colors[3][B] = CLAMP(0,colors[1][B] - table59T[d],255); - - if (p == PATTERN_T) - { - // C3 - possible_colors[0][R] = colors[0][R]; - possible_colors[0][G] = colors[0][G]; - possible_colors[0][B] = colors[0][B]; - // C2 - possible_colors[1][R] = CLAMP(0,colors[1][R] + table59T[d],255); - possible_colors[1][G] = CLAMP(0,colors[1][G] + table59T[d],255); - possible_colors[1][B] = CLAMP(0,colors[1][B] + table59T[d],255); - // C1 - possible_colors[2][R] = colors[1][R]; - possible_colors[2][G] = colors[1][G]; - possible_colors[2][B] = colors[1][B]; - - } - else - { - printf("Invalid pattern. Terminating"); - exit(1); - } -} -// Decompress a T-mode block (simple packing) -// Simple 59T packing: -//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| -//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockTHUMB59Tc(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty, int channels) -{ - uint8 colorsRGB444[2][3]; - uint8 colors[2][3]; - uint8 paint_colors[4][3]; - uint8 distance; - uint8 block_mask[4][4]; - - // First decode left part of block. - colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 58); - colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 54); - colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 50); - - colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 46); - colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 42); - colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 38); - - distance = GETBITSHIGH(block_part1, TABLE_BITS_59T, 34); - - // Extend the two colors to RGB888 - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - calculatePaintColors59T(distance, PATTERN_T, colors, paint_colors); - - // Choose one of the four paint colors for each texel - for (uint8 x = 0; x < BLOCKWIDTH; ++x) - { - for (uint8 y = 0; y < BLOCKHEIGHT; ++y) - { - //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); - block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; - block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); - img[channels*((starty+y)*width+startx+x)+R] = - CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED - img[channels*((starty+y)*width+startx+x)+G] = - CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN - img[channels*((starty+y)*width+startx+x)+B] = - CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE - } - } -} - -void decompressBlockTHUMB59T(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) -{ - decompressBlockTHUMB59Tc(block_part1, block_part2, img, width, height, startx, starty, 3); -} - -// Calculate the paint colors from the block colors -// using a distance d and one of the H- or T-patterns. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void calculatePaintColors58H(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]) -{ - - ////////////////////////////////////////////// - // - // C3 C1 C4----C1---C2 - // | | | - // | | | - // |-------| | - // | | | - // | | | - // C4 C2 C3 - // - ////////////////////////////////////////////// - - // C4 - possible_colors[3][R] = CLAMP(0,colors[1][R] - table58H[d],255); - possible_colors[3][G] = CLAMP(0,colors[1][G] - table58H[d],255); - possible_colors[3][B] = CLAMP(0,colors[1][B] - table58H[d],255); - - if (p == PATTERN_H) - { - // C1 - possible_colors[0][R] = CLAMP(0,colors[0][R] + table58H[d],255); - possible_colors[0][G] = CLAMP(0,colors[0][G] + table58H[d],255); - possible_colors[0][B] = CLAMP(0,colors[0][B] + table58H[d],255); - // C2 - possible_colors[1][R] = CLAMP(0,colors[0][R] - table58H[d],255); - possible_colors[1][G] = CLAMP(0,colors[0][G] - table58H[d],255); - possible_colors[1][B] = CLAMP(0,colors[0][B] - table58H[d],255); - // C3 - possible_colors[2][R] = CLAMP(0,colors[1][R] + table58H[d],255); - possible_colors[2][G] = CLAMP(0,colors[1][G] + table58H[d],255); - possible_colors[2][B] = CLAMP(0,colors[1][B] + table58H[d],255); - } - else - { - printf("Invalid pattern. Terminating"); - exit(1); - } -} - -// Decompress an H-mode block -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockTHUMB58Hc(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty, int channels) -{ - unsigned int col0, col1; - uint8 colors[2][3]; - uint8 colorsRGB444[2][3]; - uint8 paint_colors[4][3]; - uint8 distance; - uint8 block_mask[4][4]; - - // First decode left part of block. - colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 57); - colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 53); - colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 49); - - colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 45); - colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 41); - colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 37); - - distance = 0; - distance = (GETBITSHIGH(block_part1, 2, 33)) << 1; - - col0 = GETBITSHIGH(block_part1, 12, 57); - col1 = GETBITSHIGH(block_part1, 12, 45); - - if(col0 >= col1) - { - distance |= 1; - } - - // Extend the two colors to RGB888 - decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); - - calculatePaintColors58H(distance, PATTERN_H, colors, paint_colors); - - // Choose one of the four paint colors for each texel - for (uint8 x = 0; x < BLOCKWIDTH; ++x) - { - for (uint8 y = 0; y < BLOCKHEIGHT; ++y) - { - //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); - block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; - block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); - img[channels*((starty+y)*width+startx+x)+R] = - CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED - img[channels*((starty+y)*width+startx+x)+G] = - CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN - img[channels*((starty+y)*width+startx+x)+B] = - CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE - } - } -} -void decompressBlockTHUMB58H(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) -{ - decompressBlockTHUMB58Hc(block_part1, block_part2, img, width, height, startx, starty, 3); -} - -// Decompress the planar mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockPlanar57c(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img, int width, int height, int startx, int starty, int channels) -{ - uint8 colorO[3], colorH[3], colorV[3]; - - colorO[0] = GETBITSHIGH( compressed57_1, 6, 63); - colorO[1] = GETBITSHIGH( compressed57_1, 7, 57); - colorO[2] = GETBITSHIGH( compressed57_1, 6, 50); - colorH[0] = GETBITSHIGH( compressed57_1, 6, 44); - colorH[1] = GETBITSHIGH( compressed57_1, 7, 38); - colorH[2] = GETBITS( compressed57_2, 6, 31); - colorV[0] = GETBITS( compressed57_2, 6, 25); - colorV[1] = GETBITS( compressed57_2, 7, 19); - colorV[2] = GETBITS( compressed57_2, 6, 12); - - colorO[0] = (colorO[0] << 2) | (colorO[0] >> 4); - colorO[1] = (colorO[1] << 1) | (colorO[1] >> 6); - colorO[2] = (colorO[2] << 2) | (colorO[2] >> 4); - - colorH[0] = (colorH[0] << 2) | (colorH[0] >> 4); - colorH[1] = (colorH[1] << 1) | (colorH[1] >> 6); - colorH[2] = (colorH[2] << 2) | (colorH[2] >> 4); - - colorV[0] = (colorV[0] << 2) | (colorV[0] >> 4); - colorV[1] = (colorV[1] << 1) | (colorV[1] >> 6); - colorV[2] = (colorV[2] << 2) | (colorV[2] >> 4); - - int xx, yy; - - for( xx=0; xx<4; xx++) - { - for( yy=0; yy<4; yy++) - { - img[channels*width*(starty+yy) + channels*(startx+xx) + 0] = CLAMP(0, ((xx*(colorH[0]-colorO[0]) + yy*(colorV[0]-colorO[0]) + 4*colorO[0] + 2) >> 2),255); - img[channels*width*(starty+yy) + channels*(startx+xx) + 1] = CLAMP(0, ((xx*(colorH[1]-colorO[1]) + yy*(colorV[1]-colorO[1]) + 4*colorO[1] + 2) >> 2),255); - img[channels*width*(starty+yy) + channels*(startx+xx) + 2] = CLAMP(0, ((xx*(colorH[2]-colorO[2]) + yy*(colorV[2]-colorO[2]) + 4*colorO[2] + 2) >> 2),255); - - //Equivalent method - /*img[channels*width*(starty+yy) + channels*(startx+xx) + 0] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[0]-colorO[0])/4.0 + yy*(colorV[0]-colorO[0])/4.0 + colorO[0])), 255); - img[channels*width*(starty+yy) + channels*(startx+xx) + 1] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[1]-colorO[1])/4.0 + yy*(colorV[1]-colorO[1])/4.0 + colorO[1])), 255); - img[channels*width*(starty+yy) + channels*(startx+xx) + 2] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[2]-colorO[2])/4.0 + yy*(colorV[2]-colorO[2])/4.0 + colorO[2])), 255);*/ - - } - } -} -void decompressBlockPlanar57(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img, int width, int height, int startx, int starty) -{ - decompressBlockPlanar57c(compressed57_1, compressed57_2, img, width, height, startx, starty, 3); -} -// Decompress an ETC1 block (or ETC2 using individual or differential mode). -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockDiffFlipC(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty, int channels) -{ - uint8 avg_color[3], enc_color1[3], enc_color2[3]; - signed char diff[3]; - int table; - int index,shift; - int r,g,b; - int diffbit; - int flipbit; - - diffbit = (GETBITSHIGH(block_part1, 1, 33)); - flipbit = (GETBITSHIGH(block_part1, 1, 32)); - - if( !diffbit ) - { - // We have diffbit = 0. - - // First decode left part of block. - avg_color[0]= GETBITSHIGH(block_part1, 4, 63); - avg_color[1]= GETBITSHIGH(block_part1, 4, 55); - avg_color[2]= GETBITSHIGH(block_part1, 4, 47); - - // Here, we should really multiply by 17 instead of 16. This can - // be done by just copying the four lower bits to the upper ones - // while keeping the lower bits. - avg_color[0] |= (avg_color[0] <<4); - avg_color[1] |= (avg_color[1] <<4); - avg_color[2] |= (avg_color[2] <<4); - - table = GETBITSHIGH(block_part1, 3, 39) << 1; - - unsigned int pixel_indices_MSB, pixel_indices_LSB; - - pixel_indices_MSB = GETBITS(block_part2, 16, 31); - pixel_indices_LSB = GETBITS(block_part2, 16, 15); - - if( (flipbit) == 0 ) - { - // We should not flip - shift = 0; - for(int x=startx; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - - r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); - g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); - b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); - } - } - } - else - { - // We should flip - shift = 0; - for(int x=startx; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - - r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); - g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); - b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); - } - shift+=2; - } - } - - // Now decode other part of block. - avg_color[0]= GETBITSHIGH(block_part1, 4, 59); - avg_color[1]= GETBITSHIGH(block_part1, 4, 51); - avg_color[2]= GETBITSHIGH(block_part1, 4, 43); - - // Here, we should really multiply by 17 instead of 16. This can - // be done by just copying the four lower bits to the upper ones - // while keeping the lower bits. - avg_color[0] |= (avg_color[0] <<4); - avg_color[1] |= (avg_color[1] <<4); - avg_color[2] |= (avg_color[2] <<4); - - table = GETBITSHIGH(block_part1, 3, 36) << 1; - pixel_indices_MSB = GETBITS(block_part2, 16, 31); - pixel_indices_LSB = GETBITS(block_part2, 16, 15); - - if( (flipbit) == 0 ) - { - // We should not flip - shift=8; - for(int x=startx+2; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - - r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); - g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); - b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); - } - } - } - else - { - // We should flip - shift=2; - for(int x=startx; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - - r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); - g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); - b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); - } - shift += 2; - } - } - } - else - { - // We have diffbit = 1. - -// 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 -// --------------------------------------------------------------------------------------------------- -// | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| -// | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | -// --------------------------------------------------------------------------------------------------- -// -// -// c) bit layout in bits 31 through 0 (in both cases) -// -// 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -// -------------------------------------------------------------------------------------------------- -// | most significant pixel index bits | least significant pixel index bits | -// | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | -// -------------------------------------------------------------------------------------------------- - - // First decode left part of block. - enc_color1[0]= GETBITSHIGH(block_part1, 5, 63); - enc_color1[1]= GETBITSHIGH(block_part1, 5, 55); - enc_color1[2]= GETBITSHIGH(block_part1, 5, 47); - - // Expand from 5 to 8 bits - avg_color[0] = (enc_color1[0] <<3) | (enc_color1[0] >> 2); - avg_color[1] = (enc_color1[1] <<3) | (enc_color1[1] >> 2); - avg_color[2] = (enc_color1[2] <<3) | (enc_color1[2] >> 2); - - table = GETBITSHIGH(block_part1, 3, 39) << 1; - - unsigned int pixel_indices_MSB, pixel_indices_LSB; - - pixel_indices_MSB = GETBITS(block_part2, 16, 31); - pixel_indices_LSB = GETBITS(block_part2, 16, 15); - - if( (flipbit) == 0 ) - { - // We should not flip - shift = 0; - for(int x=startx; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - - r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); - g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); - b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); - } - } - } - else - { - // We should flip - shift = 0; - for(int x=startx; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - - r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); - g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); - b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); - } - shift+=2; - } - } - - // Now decode right part of block. - diff[0]= GETBITSHIGH(block_part1, 3, 58); - diff[1]= GETBITSHIGH(block_part1, 3, 50); - diff[2]= GETBITSHIGH(block_part1, 3, 42); - - // Extend sign bit to entire byte. - diff[0] = (diff[0] << 5); - diff[1] = (diff[1] << 5); - diff[2] = (diff[2] << 5); - diff[0] = diff[0] >> 5; - diff[1] = diff[1] >> 5; - diff[2] = diff[2] >> 5; - - // Calculale second color - enc_color2[0]= enc_color1[0] + diff[0]; - enc_color2[1]= enc_color1[1] + diff[1]; - enc_color2[2]= enc_color1[2] + diff[2]; - - // Expand from 5 to 8 bits - avg_color[0] = (enc_color2[0] <<3) | (enc_color2[0] >> 2); - avg_color[1] = (enc_color2[1] <<3) | (enc_color2[1] >> 2); - avg_color[2] = (enc_color2[2] <<3) | (enc_color2[2] >> 2); - - table = GETBITSHIGH(block_part1, 3, 36) << 1; - pixel_indices_MSB = GETBITS(block_part2, 16, 31); - pixel_indices_LSB = GETBITS(block_part2, 16, 15); - - if( (flipbit) == 0 ) - { - // We should not flip - shift=8; - for(int x=startx+2; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - - r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); - g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); - b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); - } - } - } - else - { - // We should flip - shift=2; - for(int x=startx; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - - r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); - g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); - b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); - } - shift += 2; - } - } - } -} -void decompressBlockDiffFlip(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) -{ - decompressBlockDiffFlipC(block_part1, block_part2, img, width, height, startx, starty, 3); -} - -// Decompress an ETC2 RGB block -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockETC2c(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty, int channels) -{ - int diffbit; - signed char color1[3]; - signed char diff[3]; - signed char red, green, blue; - - diffbit = (GETBITSHIGH(block_part1, 1, 33)); - - if( diffbit ) - { - // We have diffbit = 1; - - // Base color - color1[0]= GETBITSHIGH(block_part1, 5, 63); - color1[1]= GETBITSHIGH(block_part1, 5, 55); - color1[2]= GETBITSHIGH(block_part1, 5, 47); - - // Diff color - diff[0]= GETBITSHIGH(block_part1, 3, 58); - diff[1]= GETBITSHIGH(block_part1, 3, 50); - diff[2]= GETBITSHIGH(block_part1, 3, 42); - - // Extend sign bit to entire byte. - diff[0] = (diff[0] << 5); - diff[1] = (diff[1] << 5); - diff[2] = (diff[2] << 5); - diff[0] = diff[0] >> 5; - diff[1] = diff[1] >> 5; - diff[2] = diff[2] >> 5; - - red = color1[0] + diff[0]; - green = color1[1] + diff[1]; - blue = color1[2] + diff[2]; - - if(red < 0 || red > 31) - { - unsigned int block59_part1, block59_part2; - unstuff59bits(block_part1, block_part2, block59_part1, block59_part2); - decompressBlockTHUMB59Tc(block59_part1, block59_part2, img, width, height, startx, starty, channels); - } - else if (green < 0 || green > 31) - { - unsigned int block58_part1, block58_part2; - unstuff58bits(block_part1, block_part2, block58_part1, block58_part2); - decompressBlockTHUMB58Hc(block58_part1, block58_part2, img, width, height, startx, starty, channels); - } - else if(blue < 0 || blue > 31) - { - unsigned int block57_part1, block57_part2; - - unstuff57bits(block_part1, block_part2, block57_part1, block57_part2); - decompressBlockPlanar57c(block57_part1, block57_part2, img, width, height, startx, starty, channels); - } - else - { - decompressBlockDiffFlipC(block_part1, block_part2, img, width, height, startx, starty, channels); - } - } - else - { - // We have diffbit = 0; - decompressBlockDiffFlipC(block_part1, block_part2, img, width, height, startx, starty, channels); - } -} -void decompressBlockETC2(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) -{ - decompressBlockETC2c(block_part1, block_part2, img, width, height, startx, starty, 3); -} -// Decompress an ETC2 block with punchthrough alpha -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockDifferentialWithAlphaC(unsigned int block_part1, unsigned int block_part2, uint8* img, uint8* alpha, int width, int height, int startx, int starty, int channelsRGB) -{ - - uint8 avg_color[3], enc_color1[3], enc_color2[3]; - signed char diff[3]; - int table; - int index,shift; - int r,g,b; - int diffbit; - int flipbit; - int channelsA; - - if(channelsRGB == 3) - { - // We will decode the alpha data to a separate memory area. - channelsA = 1; - } - else - { - // We will decode the RGB data and the alpha data to the same memory area, - // interleaved as RGBA. - channelsA = 4; - alpha = &img[0+3]; - } - - //the diffbit now encodes whether or not the entire alpha channel is 255. - diffbit = (GETBITSHIGH(block_part1, 1, 33)); - flipbit = (GETBITSHIGH(block_part1, 1, 32)); - - // First decode left part of block. - enc_color1[0]= GETBITSHIGH(block_part1, 5, 63); - enc_color1[1]= GETBITSHIGH(block_part1, 5, 55); - enc_color1[2]= GETBITSHIGH(block_part1, 5, 47); - - // Expand from 5 to 8 bits - avg_color[0] = (enc_color1[0] <<3) | (enc_color1[0] >> 2); - avg_color[1] = (enc_color1[1] <<3) | (enc_color1[1] >> 2); - avg_color[2] = (enc_color1[2] <<3) | (enc_color1[2] >> 2); - - table = GETBITSHIGH(block_part1, 3, 39) << 1; - - unsigned int pixel_indices_MSB, pixel_indices_LSB; - - pixel_indices_MSB = GETBITS(block_part2, 16, 31); - pixel_indices_LSB = GETBITS(block_part2, 16, 15); - - if( (flipbit) == 0 ) - { - // We should not flip - shift = 0; - for(int x=startx; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - - int mod = compressParams[table][index]; - if(diffbit==0&&(index==1||index==2)) - { - mod=0; - } - - r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); - g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); - b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); - if(diffbit==0&&index==1) - { - alpha[(y*width+x)*channelsA]=0; - r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; - g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; - b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; - } - else - { - alpha[(y*width+x)*channelsA]=255; - } - - } - } - } - else - { - // We should flip - shift = 0; - for(int x=startx; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - int mod = compressParams[table][index]; - if(diffbit==0&&(index==1||index==2)) - { - mod=0; - } - r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); - g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); - b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); - if(diffbit==0&&index==1) - { - alpha[(y*width+x)*channelsA]=0; - r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; - g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; - b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; - } - else - { - alpha[(y*width+x)*channelsA]=255; - } - } - shift+=2; - } - } - // Now decode right part of block. - diff[0]= GETBITSHIGH(block_part1, 3, 58); - diff[1]= GETBITSHIGH(block_part1, 3, 50); - diff[2]= GETBITSHIGH(block_part1, 3, 42); - - // Extend sign bit to entire byte. - diff[0] = (diff[0] << 5); - diff[1] = (diff[1] << 5); - diff[2] = (diff[2] << 5); - diff[0] = diff[0] >> 5; - diff[1] = diff[1] >> 5; - diff[2] = diff[2] >> 5; - - // Calculate second color - enc_color2[0]= enc_color1[0] + diff[0]; - enc_color2[1]= enc_color1[1] + diff[1]; - enc_color2[2]= enc_color1[2] + diff[2]; - - // Expand from 5 to 8 bits - avg_color[0] = (enc_color2[0] <<3) | (enc_color2[0] >> 2); - avg_color[1] = (enc_color2[1] <<3) | (enc_color2[1] >> 2); - avg_color[2] = (enc_color2[2] <<3) | (enc_color2[2] >> 2); - - table = GETBITSHIGH(block_part1, 3, 36) << 1; - pixel_indices_MSB = GETBITS(block_part2, 16, 31); - pixel_indices_LSB = GETBITS(block_part2, 16, 15); - - if( (flipbit) == 0 ) - { - // We should not flip - shift=8; - for(int x=startx+2; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - int mod = compressParams[table][index]; - if(diffbit==0&&(index==1||index==2)) - { - mod=0; - } - - r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); - g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); - b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); - if(diffbit==0&&index==1) - { - alpha[(y*width+x)*channelsA]=0; - r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; - g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; - b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; - } - else - { - alpha[(y*width+x)*channelsA]=255; - } - } - } - } - else - { - // We should flip - shift=2; - for(int x=startx; x> shift) & 1) << 1; - index |= ((pixel_indices_LSB >> shift) & 1); - shift++; - index=unscramble[index]; - int mod = compressParams[table][index]; - if(diffbit==0&&(index==1||index==2)) - { - mod=0; - } - - r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); - g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); - b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); - if(diffbit==0&&index==1) - { - alpha[(y*width+x)*channelsA]=0; - r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; - g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; - b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; - } - else - { - alpha[(y*width+x)*channelsA]=255; - } - } - shift += 2; - } - } -} -void decompressBlockDifferentialWithAlpha(unsigned int block_part1, unsigned int block_part2, uint8* img, uint8* alpha, int width, int height, int startx, int starty) -{ - decompressBlockDifferentialWithAlphaC(block_part1, block_part2, img, alpha, width, height, startx, starty, 3); -} - - -// similar to regular decompression, but alpha channel is set to 0 if pixel index is 2, otherwise 255. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockTHUMB59TAlphaC(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty, int channelsRGB) -{ - - uint8 colorsRGB444[2][3]; - uint8 colors[2][3]; - uint8 paint_colors[4][3]; - uint8 distance; - uint8 block_mask[4][4]; - int channelsA; - - if(channelsRGB == 3) - { - // We will decode the alpha data to a separate memory area. - channelsA = 1; - } - else - { - // We will decode the RGB data and the alpha data to the same memory area, - // interleaved as RGBA. - channelsA = 4; - alpha = &img[0+3]; - } - - // First decode left part of block. - colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 58); - colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 54); - colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 50); - - colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 46); - colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 42); - colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 38); - - distance = GETBITSHIGH(block_part1, TABLE_BITS_59T, 34); - - // Extend the two colors to RGB888 - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - calculatePaintColors59T(distance, PATTERN_T, colors, paint_colors); - - // Choose one of the four paint colors for each texel - for (uint8 x = 0; x < BLOCKWIDTH; ++x) - { - for (uint8 y = 0; y < BLOCKHEIGHT; ++y) - { - //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); - block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; - block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); - img[channelsRGB*((starty+y)*width+startx+x)+R] = - CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED - img[channelsRGB*((starty+y)*width+startx+x)+G] = - CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN - img[channelsRGB*((starty+y)*width+startx+x)+B] = - CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE - if(block_mask[x][y]==2) - { - alpha[channelsA*(x+startx+(y+starty)*width)]=0; - img[channelsRGB*((starty+y)*width+startx+x)+R] =0; - img[channelsRGB*((starty+y)*width+startx+x)+G] =0; - img[channelsRGB*((starty+y)*width+startx+x)+B] =0; - } - else - alpha[channelsA*(x+startx+(y+starty)*width)]=255; - } - } -} -void decompressBlockTHUMB59TAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty) -{ - decompressBlockTHUMB59TAlphaC(block_part1, block_part2, img, alpha, width, height, startx, starty, 3); -} - - -// Decompress an H-mode block with alpha -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockTHUMB58HAlphaC(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty, int channelsRGB) -{ - unsigned int col0, col1; - uint8 colors[2][3]; - uint8 colorsRGB444[2][3]; - uint8 paint_colors[4][3]; - uint8 distance; - uint8 block_mask[4][4]; - int channelsA; - - if(channelsRGB == 3) - { - // We will decode the alpha data to a separate memory area. - channelsA = 1; - } - else - { - // We will decode the RGB data and the alpha data to the same memory area, - // interleaved as RGBA. - channelsA = 4; - alpha = &img[0+3]; - } - - // First decode left part of block. - colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 57); - colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 53); - colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 49); - - colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 45); - colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 41); - colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 37); - - distance = 0; - distance = (GETBITSHIGH(block_part1, 2, 33)) << 1; - - col0 = GETBITSHIGH(block_part1, 12, 57); - col1 = GETBITSHIGH(block_part1, 12, 45); - - if(col0 >= col1) - { - distance |= 1; - } - - // Extend the two colors to RGB888 - decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); - - calculatePaintColors58H(distance, PATTERN_H, colors, paint_colors); - - // Choose one of the four paint colors for each texel - for (uint8 x = 0; x < BLOCKWIDTH; ++x) - { - for (uint8 y = 0; y < BLOCKHEIGHT; ++y) - { - //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); - block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; - block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); - img[channelsRGB*((starty+y)*width+startx+x)+R] = - CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED - img[channelsRGB*((starty+y)*width+startx+x)+G] = - CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN - img[channelsRGB*((starty+y)*width+startx+x)+B] = - CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE - - if(block_mask[x][y]==2) - { - alpha[channelsA*(x+startx+(y+starty)*width)]=0; - img[channelsRGB*((starty+y)*width+startx+x)+R] =0; - img[channelsRGB*((starty+y)*width+startx+x)+G] =0; - img[channelsRGB*((starty+y)*width+startx+x)+B] =0; - } - else - alpha[channelsA*(x+startx+(y+starty)*width)]=255; - } - } -} -void decompressBlockTHUMB58HAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty) -{ - decompressBlockTHUMB58HAlphaC(block_part1, block_part2, img, alpha, width, height, startx, starty, 3); -} -// Decompression function for ETC2_RGBA1 format. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockETC21BitAlphaC(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alphaimg, int width, int height, int startx, int starty, int channelsRGB) -{ - int diffbit; - signed char color1[3]; - signed char diff[3]; - signed char red, green, blue; - int channelsA; - - if(channelsRGB == 3) - { - // We will decode the alpha data to a separate memory area. - channelsA = 1; - } - else - { - // We will decode the RGB data and the alpha data to the same memory area, - // interleaved as RGBA. - channelsA = 4; - alphaimg = &img[0+3]; - } - - diffbit = (GETBITSHIGH(block_part1, 1, 33)); - - if( diffbit ) - { - // We have diffbit = 1, meaning no transparent pixels. regular decompression. - - // Base color - color1[0]= GETBITSHIGH(block_part1, 5, 63); - color1[1]= GETBITSHIGH(block_part1, 5, 55); - color1[2]= GETBITSHIGH(block_part1, 5, 47); - - // Diff color - diff[0]= GETBITSHIGH(block_part1, 3, 58); - diff[1]= GETBITSHIGH(block_part1, 3, 50); - diff[2]= GETBITSHIGH(block_part1, 3, 42); - - // Extend sign bit to entire byte. - diff[0] = (diff[0] << 5); - diff[1] = (diff[1] << 5); - diff[2] = (diff[2] << 5); - diff[0] = diff[0] >> 5; - diff[1] = diff[1] >> 5; - diff[2] = diff[2] >> 5; - - red = color1[0] + diff[0]; - green = color1[1] + diff[1]; - blue = color1[2] + diff[2]; - - if(red < 0 || red > 31) - { - unsigned int block59_part1, block59_part2; - unstuff59bits(block_part1, block_part2, block59_part1, block59_part2); - decompressBlockTHUMB59Tc(block59_part1, block59_part2, img, width, height, startx, starty, channelsRGB); - } - else if (green < 0 || green > 31) - { - unsigned int block58_part1, block58_part2; - unstuff58bits(block_part1, block_part2, block58_part1, block58_part2); - decompressBlockTHUMB58Hc(block58_part1, block58_part2, img, width, height, startx, starty, channelsRGB); - } - else if(blue < 0 || blue > 31) - { - unsigned int block57_part1, block57_part2; - - unstuff57bits(block_part1, block_part2, block57_part1, block57_part2); - decompressBlockPlanar57c(block57_part1, block57_part2, img, width, height, startx, starty, channelsRGB); - } - else - { - decompressBlockDifferentialWithAlphaC(block_part1, block_part2, img, alphaimg, width, height, startx, starty, channelsRGB); - } - for(int x=startx; x> 5; - diff[1] = diff[1] >> 5; - diff[2] = diff[2] >> 5; - - red = color1[0] + diff[0]; - green = color1[1] + diff[1]; - blue = color1[2] + diff[2]; - if(red < 0 || red > 31) - { - unsigned int block59_part1, block59_part2; - unstuff59bits(block_part1, block_part2, block59_part1, block59_part2); - decompressBlockTHUMB59TAlphaC(block59_part1, block59_part2, img, alphaimg, width, height, startx, starty, channelsRGB); - } - else if(green < 0 || green > 31) - { - unsigned int block58_part1, block58_part2; - unstuff58bits(block_part1, block_part2, block58_part1, block58_part2); - decompressBlockTHUMB58HAlphaC(block58_part1, block58_part2, img, alphaimg, width, height, startx, starty, channelsRGB); - } - else if(blue < 0 || blue > 31) - { - unsigned int block57_part1, block57_part2; - - unstuff57bits(block_part1, block_part2, block57_part1, block57_part2); - decompressBlockPlanar57c(block57_part1, block57_part2, img, width, height, startx, starty, channelsRGB); - for(int x=startx; xtopos) - return ((1<>(frompos-topos); - return ((1<255) - val=255; - return val; -} - -// Decodes tha alpha component in a block coded with GL_COMPRESSED_RGBA8_ETC2_EAC. -// Note that this decoding is slightly different from that of GL_COMPRESSED_R11_EAC. -// However, a hardware decoder can share gates between the two formats as explained -// in the specification under GL_COMPRESSED_R11_EAC. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockAlphaC(uint8* data, uint8* img, int width, int height, int ix, int iy, int channels) -{ - int alpha = data[0]; - int table = data[1]; - - int bit=0; - int byte=2; - //extract an alpha value for each pixel. - for(int x=0; x<4; x++) - { - for(int y=0; y<4; y++) - { - //Extract table index - int index=0; - for(int bitpos=0; bitpos<3; bitpos++) - { - index|=getbit(data[byte],7-bit,2-bitpos); - bit++; - if(bit>7) - { - bit=0; - byte++; - } - } - img[(ix+x+(iy+y)*width)*channels]=clamp(alpha +alphaTable[table][index]); - } - } -} -void decompressBlockAlpha(uint8* data, uint8* img, int width, int height, int ix, int iy) -{ - decompressBlockAlphaC(data, img, width, height, ix, iy, 1); -} - -// Does decompression and then immediately converts from 11 bit signed to a 16-bit format. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int16 get16bits11signed(int base, int table, int mul, int index) -{ - int elevenbase = base-128; - if(elevenbase==-128) - elevenbase=-127; - elevenbase*=8; - //i want the positive value here - int tabVal = -alphaBase[table][3-index%4]-1; - //and the sign, please - int sign = 1-(index/4); - - if(sign) - tabVal=tabVal+1; - int elevenTabVal = tabVal*8; - - if(mul!=0) - elevenTabVal*=mul; - else - elevenTabVal/=8; - - if(sign) - elevenTabVal=-elevenTabVal; - - //calculate sum - int elevenbits = elevenbase+elevenTabVal; - - //clamp.. - if(elevenbits>=1024) - elevenbits=1023; - else if(elevenbits<-1023) - elevenbits=-1023; - //this is the value we would actually output.. - //but there aren't any good 11-bit file or uncompressed GL formats - //so we extend to 15 bits signed. - sign = elevenbits<0; - elevenbits=abs(elevenbits); - int16 fifteenbits = (elevenbits<<5)+(elevenbits>>5); - int16 sixteenbits=fifteenbits; - - if(sign) - sixteenbits=-sixteenbits; - - return sixteenbits; -} - -// Does decompression and then immediately converts from 11 bit signed to a 16-bit format -// Calculates the 11 bit value represented by base, table, mul and index, and extends it to 16 bits. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -uint16 get16bits11bits(int base, int table, int mul, int index) -{ - int elevenbase = base*8+4; - - //i want the positive value here - int tabVal = -alphaBase[table][3-index%4]-1; - //and the sign, please - int sign = 1-(index/4); - - if(sign) - tabVal=tabVal+1; - int elevenTabVal = tabVal*8; - - if(mul!=0) - elevenTabVal*=mul; - else - elevenTabVal/=8; - - if(sign) - elevenTabVal=-elevenTabVal; - - //calculate sum - int elevenbits = elevenbase+elevenTabVal; - - //clamp.. - if(elevenbits>=256*8) - elevenbits=256*8-1; - else if(elevenbits<0) - elevenbits=0; - //elevenbits now contains the 11 bit alpha value as defined in the spec. - - //extend to 16 bits before returning, since we don't have any good 11-bit file formats. - uint16 sixteenbits = (elevenbits<<5)+(elevenbits>>6); - - return sixteenbits; -} - -// Decompresses a block using one of the GL_COMPRESSED_R11_EAC or GL_COMPRESSED_SIGNED_R11_EAC-formats -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockAlpha16bitC(uint8* data, uint8* img, int width, int height, int ix, int iy, int channels) -{ - int alpha = data[0]; - int table = data[1]; - - if(formatSigned) - { - //if we have a signed format, the base value is given as a signed byte. We convert it to (0-255) here, - //so more code can be shared with the unsigned mode. - alpha = *((signed char*)(&data[0])); - alpha = alpha+128; - } - - int bit=0; - int byte=2; - //extract an alpha value for each pixel. - for(int x=0; x<4; x++) - { - for(int y=0; y<4; y++) - { - //Extract table index - int index=0; - for(int bitpos=0; bitpos<3; bitpos++) - { - index|=getbit(data[byte],7-bit,2-bitpos); - bit++; - if(bit>7) - { - bit=0; - byte++; - } - } - int windex = channels*(2*(ix+x+(iy+y)*width)); -#if !PGMOUT - if(formatSigned) - { - *(int16 *)&img[windex] = get16bits11signed(alpha,(table%16),(table/16),index); - } - else - { - *(uint16 *)&img[windex] = get16bits11bits(alpha,(table%16),(table/16),index); - } -#else - //make data compatible with the .pgm format. See the comment in compressBlockAlpha16() for details. - uint16 uSixteen; - if (formatSigned) - { - //the pgm-format only allows unsigned images, - //so we add 2^15 to get a 16-bit value. - uSixteen = get16bits11signed(alpha,(table%16),(table/16),index) + 256*128; - } - else - { - uSixteen = get16bits11bits(alpha,(table%16),(table/16),index); - } - //byte swap for pgm - img[windex] = uSixteen/256; - img[windex+1] = uSixteen%256; -#endif - - } - } -} - -void decompressBlockAlpha16bit(uint8* data, uint8* img, int width, int height, int ix, int iy) -{ - decompressBlockAlpha16bitC(data, img, width, height, ix, iy, 1); -} +/** + +@~English +@page licensing Licensing + +@section etcdec etcdec.cxx License + +etcdec.cxx is made available under the terms and conditions of the following +License Agreement. + +Software License Agreement + +PLEASE REVIEW THE FOLLOWING TERMS AND CONDITIONS PRIOR TO USING THE +ERICSSON TEXTURE COMPRESSION CODEC SOFTWARE (THE "SOFTWARE"). THE USE +OF THE SOFTWARE IS SUBJECT TO THE TERMS AND CONDITIONS OF THE +FOLLOWING SOFTWARE LICENSE AGREEMENT (THE "SLA"). IF YOU DO NOT ACCEPT +SUCH TERMS AND CONDITIONS YOU MAY NOT USE THE SOFTWARE. + +Subject to the terms and conditions of the SLA, the licensee of the +Software (the "Licensee") hereby, receives a non-exclusive, +non-transferable, limited, free-of-charge, perpetual and worldwide +license, to copy, use, distribute and modify the Software, but only +for the purpose of developing, manufacturing, selling, using and +distributing products including the Software in binary form, which +products are used for compression and/or decompression according to +the Khronos standard specifications OpenGL, OpenGL ES and +WebGL. Notwithstanding anything of the above, Licensee may distribute +[etcdec.cxx] in source code form provided (i) it is in unmodified +form; and (ii) it is included in software owned by Licensee. + +If Licensee institutes, or threatens to institute, patent litigation +against Ericsson or Ericsson's affiliates for using the Software for +developing, having developed, manufacturing, having manufactured, +selling, offer for sale, importing, using, leasing, operating, +repairing and/or distributing products (i) within the scope of the +Khronos framework; or (ii) using software or other intellectual +property rights owned by Ericsson or its affiliates and provided under +the Khronos framework, Ericsson shall have the right to terminate this +SLA with immediate effect. Moreover, if Licensee institutes, or +threatens to institute, patent litigation against any other licensee +of the Software for using the Software in products within the scope of +the Khronos framework, Ericsson shall have the right to terminate this +SLA with immediate effect. However, should Licensee institute, or +threaten to institute, patent litigation against any other licensee of +the Software based on such other licensee's use of any other software +together with the Software, then Ericsson shall have no right to +terminate this SLA. + +This SLA does not transfer to Licensee any ownership to any Ericsson +or third party intellectual property rights. All rights not expressly +granted by Ericsson under this SLA are hereby expressly +reserved. Furthermore, nothing in this SLA shall be construed as a +right to use or sell products in a manner which conveys or purports to +convey whether explicitly, by principles of implied license, or +otherwise, any rights to any third party, under any patent of Ericsson +or of Ericsson's affiliates covering or relating to any combination of +the Software with any other software or product (not licensed +hereunder) where the right applies specifically to the combination and +not to the software or product itself. + +THE SOFTWARE IS PROVIDED "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF +ANY KIND, EXTENDS NO WARRANTIES OR CONDITIONS OF ANY KIND, EITHER +EXPRESS, IMPLIED OR STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, +IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS OF TITLE, +MERCHANTABILITY, SATISFACTORY QUALITY, SUITABILITY, AND FITNESS FOR A +PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE SOFTWARE IS WITH THE LICENSEE. SHOULD THE SOFTWARE PROVE +DEFECTIVE, THE LICENSEE ASSUMES THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. ERICSSON MAKES NO WARRANTY THAT THE MANUFACTURE, +SALE, OFFERING FOR SALE, DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER +THE SLA WILL BE FREE FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER +INTELLECTUAL PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE +LICENSE AND THE SLA ARE SUBJECT TO LICENSEE'S SOLE RESPONSIBILITY TO +MAKE SUCH DETERMINATION AND ACQUIRE SUCH LICENSES AS MAY BE NECESSARY +WITH RESPECT TO PATENTS, COPYRIGHT AND OTHER INTELLECTUAL PROPERTY OF +THIRD PARTIES. + +THE LICENSEE ACKNOWLEDGES AND ACCEPTS THAT THE SOFTWARE (I) IS NOT +LICENSED FOR; (II) IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY +NOT BE USED FOR; ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT +LIMITED TO OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR +NETWORKS, AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR +ANY OTHER COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR +COMMUNICATION SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE SOFTWARE +COULD LEAD TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR +ENVIRONMENTAL DAMAGE. LICENSEE'S RIGHTS UNDER THIS LICENSE WILL +TERMINATE AUTOMATICALLY AND IMMEDIATELY WITHOUT NOTICE IF LICENSEE +FAILS TO COMPLY WITH THIS PARAGRAPH. + +IN NO EVENT SHALL ERICSSON BE LIABLE FOR ANY DAMAGES WHATSOEVER, +INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, +INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING +BUT NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY +OTHER COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY THE LICENSEE OR THIRD +PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER +SOFTWARE) REGARDLESS OF THE THEORY OF LIABILITY (CONTRACT, TORT, OR +OTHERWISE), EVEN IF THE LICENSEE OR ANY OTHER PARTY HAS BEEN ADVISED +OF THE POSSIBILITY OF SUCH DAMAGES. + +Licensee acknowledges that "ERICSSON ///" is the corporate trademark +of Telefonaktiebolaget LM Ericsson and that both "Ericsson" and the +figure "///" are important features of the trade names of +Telefonaktiebolaget LM Ericsson. Nothing contained in these terms and +conditions shall be deemed to grant Licensee any right, title or +interest in the word "Ericsson" or the figure "///". No delay or +omission by Ericsson to exercise any right or power shall impair any +such right or power to be construed to be a waiver thereof. Consent by +Ericsson to, or waiver of, a breach by the Licensee shall not +constitute consent to, waiver of, or excuse for any other different or +subsequent breach. + +This SLA shall be governed by the substantive law of Sweden. Any +dispute, controversy or claim arising out of or in connection with +this SLA, or the breach, termination or invalidity thereof, shall be +submitted to the exclusive jurisdiction of the Swedish Courts. + +*/ + +//// etcpack v2.74 +//// +//// NO WARRANTY +//// +//// BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE THE PROGRAM IS PROVIDED +//// "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF ANY KIND, EXTENDS NO +//// WARRANTIES OR CONDITIONS OF ANY KIND; EITHER EXPRESS, IMPLIED OR +//// STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, IMPLIED OR +//// STATUTORY WARRANTIES OR CONDITIONS OF TITLE, MERCHANTABILITY, +//// SATISFACTORY QUALITY, SUITABILITY AND FITNESS FOR A PARTICULAR +//// PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +//// PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +//// THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ERICSSON +//// MAKES NO WARRANTY THAT THE MANUFACTURE, SALE, OFFERING FOR SALE, +//// DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER THE LICENSE WILL BE FREE +//// FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER INTELLECTUAL +//// PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE LICENSE IS SUBJECT +//// TO YOUR SOLE RESPONSIBILITY TO MAKE SUCH DETERMINATION AND ACQUIRE +//// SUCH LICENSES AS MAY BE NECESSARY WITH RESPECT TO PATENTS, COPYRIGHT +//// AND OTHER INTELLECTUAL PROPERTY OF THIRD PARTIES. +//// +//// FOR THE AVOIDANCE OF DOUBT THE PROGRAM (I) IS NOT LICENSED FOR; (II) +//// IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY NOT BE USED FOR; +//// ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT LIMITED TO +//// OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR NETWORKS, +//// AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR ANY OTHER +//// COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR COMMUNICATION +//// SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE PROGRAM COULD LEAD TO +//// DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR ENVIRONMENTAL +//// DAMAGE. YOUR RIGHTS UNDER THIS LICENSE WILL TERMINATE AUTOMATICALLY +//// AND IMMEDIATELY WITHOUT NOTICE IF YOU FAIL TO COMPLY WITH THIS +//// PARAGRAPH. +//// +//// IN NO EVENT WILL ERICSSON, BE LIABLE FOR ANY DAMAGES WHATSOEVER, +//// INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, +//// INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN +//// CONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +//// NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY OTHER +//// COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING RENDERED +//// INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +//// THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) REGARDLESS OF THE +//// THEORY OF LIABILITY (CONTRACT, TORT OR OTHERWISE), EVEN IF SUCH HOLDER +//// OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +//// +//// (C) Ericsson AB 2005-2013. All Rights Reserved. +//// + +#include +#include + +// Typedefs +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef short int16; + +// Macros to help with bit extraction/insertion +#define SHIFT(size,startpos) ((startpos)-(size)+1) +#define MASK(size, startpos) (((2<<(size-1))-1) << SHIFT(size,startpos)) +#define PUTBITS( dest, data, size, startpos) dest = ((dest & ~MASK(size, startpos)) | ((data << SHIFT(size, startpos)) & MASK(size,startpos))) +#define SHIFTHIGH(size, startpos) (((startpos)-32)-(size)+1) +#define MASKHIGH(size, startpos) (((1<<(size))-1) << SHIFTHIGH(size,startpos)) +#define PUTBITSHIGH(dest, data, size, startpos) dest = ((dest & ~MASKHIGH(size, startpos)) | ((data << SHIFTHIGH(size, startpos)) & MASKHIGH(size,startpos))) +#define GETBITS(source, size, startpos) (( (source) >> ((startpos)-(size)+1) ) & ((1<<(size)) -1)) +#define GETBITSHIGH(source, size, startpos) (( (source) >> (((startpos)-32)-(size)+1) ) & ((1<<(size)) -1)) +#ifndef PGMOUT +#define PGMOUT 1 +#endif +// Thumb macros and definitions +#define R_BITS59T 4 +#define G_BITS59T 4 +#define B_BITS59T 4 +#define R_BITS58H 4 +#define G_BITS58H 4 +#define B_BITS58H 4 +#define MAXIMUM_ERROR (255*255*16*1000) +#define R 0 +#define G 1 +#define B 2 +#define BLOCKHEIGHT 4 +#define BLOCKWIDTH 4 +#define BINPOW(power) (1<<(power)) +#define TABLE_BITS_59T 3 +#define TABLE_BITS_58H 3 + +// Helper Macros +#define CLAMP(ll,x,ul) (((x)<(ll)) ? (ll) : (((x)>(ul)) ? (ul) : (x))) +#define JAS_ROUND(x) (((x) < 0.0 ) ? ((int)((x)-0.5)) : ((int)((x)+0.5))) + +#define RED_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+0] +#define GREEN_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+1] +#define BLUE_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+2] +#define ALPHA_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+3] + + +// Global tables +static uint8 table59T[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 59 bit T-mode +static uint8 table58H[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 58 bit H-mode +static int compressParams[16][4] = {{-8, -2, 2, 8}, {-8, -2, 2, 8}, {-17, -5, 5, 17}, {-17, -5, 5, 17}, {-29, -9, 9, 29}, {-29, -9, 9, 29}, {-42, -13, 13, 42}, {-42, -13, 13, 42}, {-60, -18, 18, 60}, {-60, -18, 18, 60}, {-80, -24, 24, 80}, {-80, -24, 24, 80}, {-106, -33, 33, 106}, {-106, -33, 33, 106}, {-183, -47, 47, 183}, {-183, -47, 47, 183}}; +static int unscramble[4] = {2, 3, 1, 0}; +int alphaTableInitialized = 0; +int alphaTable[256][8]; +int alphaBase[16][4] = { + {-15, -9 , -6 , -3}, + {-13, -10, -7 , -3}, + {-13, -8 , -5 , -2}, + {-13, -6 , -4 , -2}, + {-12, -8 , -6 , -3}, + {-11, -9 , -7 , -3}, + {-11, -8 , -7 , -4}, + {-11, -8 , -5 , -3}, + {-10, -8 , -6 , -2}, + {-10, -8 , -5 , -2}, + {-10, -8 , -4 , -2}, + {-10, -7 , -5 , -2}, + {-10, -7 , -4 , -3}, + {-10, -3 , -2 , -1}, + { -9, -8 , -6 , -4}, + { -9, -7 , -5 , -3} +}; + +// Global variables +int formatSigned = 0; + +// Enums +enum { + PATTERN_H = 0, + PATTERN_T = 1 +}; + + +// Code used to create the valtab +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void setupAlphaTable() +{ + if(alphaTableInitialized) + return; + alphaTableInitialized = 1; + + //read table used for alpha compression + int buf; + for(int i = 16; i<32; i++) + { + for(int j=0; j<8; j++) + { + buf=alphaBase[i-16][3-j%4]; + if(j<4) + alphaTable[i][j]=buf; + else + alphaTable[i][j]=(-buf-1); + } + } + + //beyond the first 16 values, the rest of the table is implicit.. so calculate that! + for(int i=0; i<256; i++) + { + //fill remaining slots in table with multiples of the first ones. + int mul = i/16; + int old = 16+i%16; + for(int j = 0; j<8; j++) + { + alphaTable[i][j]=alphaTable[old][j]*mul; + //note: we don't do clamping here, though we could, because we'll be clamped afterwards anyway. + } + } +} + +// Read a word in big endian style +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void read_big_endian_2byte_word(unsigned short *blockadr, FILE *f) +{ + uint8 bytes[2]; + unsigned short block; + + fread(&bytes[0], 1, 1, f); + fread(&bytes[1], 1, 1, f); + + block = 0; + block |= bytes[0]; + block = block << 8; + block |= bytes[1]; + + blockadr[0] = block; +} + +// Read a word in big endian style +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void read_big_endian_4byte_word(unsigned int *blockadr, FILE *f) +{ + uint8 bytes[4]; + unsigned int block; + + fread(&bytes[0], 1, 1, f); + fread(&bytes[1], 1, 1, f); + fread(&bytes[2], 1, 1, f); + fread(&bytes[3], 1, 1, f); + + block = 0; + block |= bytes[0]; + block = block << 8; + block |= bytes[1]; + block = block << 8; + block |= bytes[2]; + block = block << 8; + block |= bytes[3]; + + blockadr[0] = block; +} + +// The format stores the bits for the three extra modes in a roundabout way to be able to +// fit them without increasing the bit rate. This function converts them into something +// that is easier to work with. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void unstuff57bits(unsigned int planar_word1, unsigned int planar_word2, unsigned int &planar57_word1, unsigned int &planar57_word2) +{ + // Get bits from twotimer configuration for 57 bits + // + // Go to this bit layout: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // |R0 |G01G02 |B01B02 ;B03 |RH1 |RH2|GH | + // ----------------------------------------------------------------------------------------------- + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // ----------------------------------------------------------------------------------------------- + // |BH |RV |GV |BV | not used | + // ----------------------------------------------------------------------------------------------- + // + // From this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ------------------------------------------------------------------------------------------------ + // |//|R0 |G01|/|G02 |B01|/ // //|B02 |//|B03 |RH1 |df|RH2| + // ------------------------------------------------------------------------------------------------ + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // ----------------------------------------------------------------------------------------------- + // |GH |BH |RV |GV |BV | + // ----------------------------------------------------------------------------------------------- + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + uint8 RO, GO1, GO2, BO1, BO2, BO3, RH1, RH2, GH, BH, RV, GV, BV; + + RO = GETBITSHIGH( planar_word1, 6, 62); + GO1 = GETBITSHIGH( planar_word1, 1, 56); + GO2 = GETBITSHIGH( planar_word1, 6, 54); + BO1 = GETBITSHIGH( planar_word1, 1, 48); + BO2 = GETBITSHIGH( planar_word1, 2, 44); + BO3 = GETBITSHIGH( planar_word1, 3, 41); + RH1 = GETBITSHIGH( planar_word1, 5, 38); + RH2 = GETBITSHIGH( planar_word1, 1, 32); + GH = GETBITS( planar_word2, 7, 31); + BH = GETBITS( planar_word2, 6, 24); + RV = GETBITS( planar_word2, 6, 18); + GV = GETBITS( planar_word2, 7, 12); + BV = GETBITS( planar_word2, 6, 5); + + planar57_word1 = 0; planar57_word2 = 0; + PUTBITSHIGH( planar57_word1, RO, 6, 63); + PUTBITSHIGH( planar57_word1, GO1, 1, 57); + PUTBITSHIGH( planar57_word1, GO2, 6, 56); + PUTBITSHIGH( planar57_word1, BO1, 1, 50); + PUTBITSHIGH( planar57_word1, BO2, 2, 49); + PUTBITSHIGH( planar57_word1, BO3, 3, 47); + PUTBITSHIGH( planar57_word1, RH1, 5, 44); + PUTBITSHIGH( planar57_word1, RH2, 1, 39); + PUTBITSHIGH( planar57_word1, GH, 7, 38); + PUTBITS( planar57_word2, BH, 6, 31); + PUTBITS( planar57_word2, RV, 6, 25); + PUTBITS( planar57_word2, GV, 7, 19); + PUTBITS( planar57_word2, BV, 6, 12); +} + +// The format stores the bits for the three extra modes in a roundabout way to be able to +// fit them without increasing the bit rate. This function converts them into something +// that is easier to work with. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void unstuff58bits(unsigned int thumbH_word1, unsigned int thumbH_word2, unsigned int &thumbH58_word1, unsigned int &thumbH58_word2) +{ + // Go to this layout: + // + // |63 62 61 60 59 58|57 56 55 54 53 52 51|50 49|48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33|32 | + // |-------empty-----|part0---------------|part1|part2------------------------------------------|part3| + // + // from this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------| + // |//|part0 |// // //|part1|//|part2 |df|part3| + // --------------------------------------------------------------------------------------------------| + + unsigned int part0, part1, part2, part3; + + // move parts + part0 = GETBITSHIGH( thumbH_word1, 7, 62); + part1 = GETBITSHIGH( thumbH_word1, 2, 52); + part2 = GETBITSHIGH( thumbH_word1,16, 49); + part3 = GETBITSHIGH( thumbH_word1, 1, 32); + thumbH58_word1 = 0; + PUTBITSHIGH( thumbH58_word1, part0, 7, 57); + PUTBITSHIGH( thumbH58_word1, part1, 2, 50); + PUTBITSHIGH( thumbH58_word1, part2, 16, 48); + PUTBITSHIGH( thumbH58_word1, part3, 1, 32); + + thumbH58_word2 = thumbH_word2; +} + +// The format stores the bits for the three extra modes in a roundabout way to be able to +// fit them without increasing the bit rate. This function converts them into something +// that is easier to work with. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void unstuff59bits(unsigned int thumbT_word1, unsigned int thumbT_word2, unsigned int &thumbT59_word1, unsigned int &thumbT59_word2) +{ + // Get bits from twotimer configuration 59 bits. + // + // Go to this bit layout: + // + // |63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| + // |----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |----------------------------------------index bits---------------------------------------------| + // + // + // From this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // |// // //|R0a |//|R0b |G0 |B0 |R1 |G1 |B1 |da |df|db| + // ----------------------------------------------------------------------------------------------- + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |----------------------------------------index bits---------------------------------------------| + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt| + // ------------------------------------------------------------------------------------------------ + + uint8 R0a; + + // Fix middle part + thumbT59_word1 = thumbT_word1 >> 1; + // Fix db (lowest bit of d) + PUTBITSHIGH( thumbT59_word1, thumbT_word1, 1, 32); + // Fix R0a (top two bits of R0) + R0a = GETBITSHIGH( thumbT_word1, 2, 60); + PUTBITSHIGH( thumbT59_word1, R0a, 2, 58); + + // Zero top part (not needed) + PUTBITSHIGH( thumbT59_word1, 0, 5, 63); + + thumbT59_word2 = thumbT_word2; +} + +// The color bits are expanded to the full color +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressColor(int R_B, int G_B, int B_B, uint8 (colors_RGB444)[2][3], uint8 (colors)[2][3]) +{ + // The color should be retrieved as: + // + // c = round(255/(r_bits^2-1))*comp_color + // + // This is similar to bit replication + // + // Note -- this code only work for bit replication from 4 bits and up --- 3 bits needs + // two copy operations. + + colors[0][R] = (colors_RGB444[0][R] << (8 - R_B)) | (colors_RGB444[0][R] >> (R_B - (8-R_B)) ); + colors[0][G] = (colors_RGB444[0][G] << (8 - G_B)) | (colors_RGB444[0][G] >> (G_B - (8-G_B)) ); + colors[0][B] = (colors_RGB444[0][B] << (8 - B_B)) | (colors_RGB444[0][B] >> (B_B - (8-B_B)) ); + colors[1][R] = (colors_RGB444[1][R] << (8 - R_B)) | (colors_RGB444[1][R] >> (R_B - (8-R_B)) ); + colors[1][G] = (colors_RGB444[1][G] << (8 - G_B)) | (colors_RGB444[1][G] >> (G_B - (8-G_B)) ); + colors[1][B] = (colors_RGB444[1][B] << (8 - B_B)) | (colors_RGB444[1][B] >> (B_B - (8-B_B)) ); +} + +void calculatePaintColors59T(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]) +{ + ////////////////////////////////////////////// + // + // C3 C1 C4----C1---C2 + // | | | + // | | | + // |-------| | + // | | | + // | | | + // C4 C2 C3 + // + ////////////////////////////////////////////// + + // C4 + possible_colors[3][R] = CLAMP(0,colors[1][R] - table59T[d],255); + possible_colors[3][G] = CLAMP(0,colors[1][G] - table59T[d],255); + possible_colors[3][B] = CLAMP(0,colors[1][B] - table59T[d],255); + + if (p == PATTERN_T) + { + // C3 + possible_colors[0][R] = colors[0][R]; + possible_colors[0][G] = colors[0][G]; + possible_colors[0][B] = colors[0][B]; + // C2 + possible_colors[1][R] = CLAMP(0,colors[1][R] + table59T[d],255); + possible_colors[1][G] = CLAMP(0,colors[1][G] + table59T[d],255); + possible_colors[1][B] = CLAMP(0,colors[1][B] + table59T[d],255); + // C1 + possible_colors[2][R] = colors[1][R]; + possible_colors[2][G] = colors[1][G]; + possible_colors[2][B] = colors[1][B]; + + } + else + { + printf("Invalid pattern. Terminating"); + exit(1); + } +} +// Decompress a T-mode block (simple packing) +// Simple 59T packing: +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockTHUMB59Tc(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty, int channels) +{ + uint8 colorsRGB444[2][3]; + uint8 colors[2][3]; + uint8 paint_colors[4][3]; + uint8 distance; + uint8 block_mask[4][4]; + + // First decode left part of block. + colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 58); + colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 54); + colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 50); + + colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 46); + colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 42); + colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 38); + + distance = GETBITSHIGH(block_part1, TABLE_BITS_59T, 34); + + // Extend the two colors to RGB888 + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + calculatePaintColors59T(distance, PATTERN_T, colors, paint_colors); + + // Choose one of the four paint colors for each texel + for (uint8 x = 0; x < BLOCKWIDTH; ++x) + { + for (uint8 y = 0; y < BLOCKHEIGHT; ++y) + { + //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); + block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; + block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); + img[channels*((starty+y)*width+startx+x)+R] = + CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED + img[channels*((starty+y)*width+startx+x)+G] = + CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN + img[channels*((starty+y)*width+startx+x)+B] = + CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE + } + } +} + +void decompressBlockTHUMB59T(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockTHUMB59Tc(block_part1, block_part2, img, width, height, startx, starty, 3); +} + +// Calculate the paint colors from the block colors +// using a distance d and one of the H- or T-patterns. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void calculatePaintColors58H(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]) +{ + + ////////////////////////////////////////////// + // + // C3 C1 C4----C1---C2 + // | | | + // | | | + // |-------| | + // | | | + // | | | + // C4 C2 C3 + // + ////////////////////////////////////////////// + + // C4 + possible_colors[3][R] = CLAMP(0,colors[1][R] - table58H[d],255); + possible_colors[3][G] = CLAMP(0,colors[1][G] - table58H[d],255); + possible_colors[3][B] = CLAMP(0,colors[1][B] - table58H[d],255); + + if (p == PATTERN_H) + { + // C1 + possible_colors[0][R] = CLAMP(0,colors[0][R] + table58H[d],255); + possible_colors[0][G] = CLAMP(0,colors[0][G] + table58H[d],255); + possible_colors[0][B] = CLAMP(0,colors[0][B] + table58H[d],255); + // C2 + possible_colors[1][R] = CLAMP(0,colors[0][R] - table58H[d],255); + possible_colors[1][G] = CLAMP(0,colors[0][G] - table58H[d],255); + possible_colors[1][B] = CLAMP(0,colors[0][B] - table58H[d],255); + // C3 + possible_colors[2][R] = CLAMP(0,colors[1][R] + table58H[d],255); + possible_colors[2][G] = CLAMP(0,colors[1][G] + table58H[d],255); + possible_colors[2][B] = CLAMP(0,colors[1][B] + table58H[d],255); + } + else + { + printf("Invalid pattern. Terminating"); + exit(1); + } +} + +// Decompress an H-mode block +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockTHUMB58Hc(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty, int channels) +{ + unsigned int col0, col1; + uint8 colors[2][3]; + uint8 colorsRGB444[2][3]; + uint8 paint_colors[4][3]; + uint8 distance; + uint8 block_mask[4][4]; + + // First decode left part of block. + colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 57); + colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 53); + colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 49); + + colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 45); + colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 41); + colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 37); + + distance = 0; + distance = (GETBITSHIGH(block_part1, 2, 33)) << 1; + + col0 = GETBITSHIGH(block_part1, 12, 57); + col1 = GETBITSHIGH(block_part1, 12, 45); + + if(col0 >= col1) + { + distance |= 1; + } + + // Extend the two colors to RGB888 + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + calculatePaintColors58H(distance, PATTERN_H, colors, paint_colors); + + // Choose one of the four paint colors for each texel + for (uint8 x = 0; x < BLOCKWIDTH; ++x) + { + for (uint8 y = 0; y < BLOCKHEIGHT; ++y) + { + //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); + block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; + block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); + img[channels*((starty+y)*width+startx+x)+R] = + CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED + img[channels*((starty+y)*width+startx+x)+G] = + CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN + img[channels*((starty+y)*width+startx+x)+B] = + CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE + } + } +} +void decompressBlockTHUMB58H(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockTHUMB58Hc(block_part1, block_part2, img, width, height, startx, starty, 3); +} + +// Decompress the planar mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockPlanar57c(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img, int width, int height, int startx, int starty, int channels) +{ + uint8 colorO[3], colorH[3], colorV[3]; + + colorO[0] = GETBITSHIGH( compressed57_1, 6, 63); + colorO[1] = GETBITSHIGH( compressed57_1, 7, 57); + colorO[2] = GETBITSHIGH( compressed57_1, 6, 50); + colorH[0] = GETBITSHIGH( compressed57_1, 6, 44); + colorH[1] = GETBITSHIGH( compressed57_1, 7, 38); + colorH[2] = GETBITS( compressed57_2, 6, 31); + colorV[0] = GETBITS( compressed57_2, 6, 25); + colorV[1] = GETBITS( compressed57_2, 7, 19); + colorV[2] = GETBITS( compressed57_2, 6, 12); + + colorO[0] = (colorO[0] << 2) | (colorO[0] >> 4); + colorO[1] = (colorO[1] << 1) | (colorO[1] >> 6); + colorO[2] = (colorO[2] << 2) | (colorO[2] >> 4); + + colorH[0] = (colorH[0] << 2) | (colorH[0] >> 4); + colorH[1] = (colorH[1] << 1) | (colorH[1] >> 6); + colorH[2] = (colorH[2] << 2) | (colorH[2] >> 4); + + colorV[0] = (colorV[0] << 2) | (colorV[0] >> 4); + colorV[1] = (colorV[1] << 1) | (colorV[1] >> 6); + colorV[2] = (colorV[2] << 2) | (colorV[2] >> 4); + + int xx, yy; + + for( xx=0; xx<4; xx++) + { + for( yy=0; yy<4; yy++) + { + img[channels*width*(starty+yy) + channels*(startx+xx) + 0] = CLAMP(0, ((xx*(colorH[0]-colorO[0]) + yy*(colorV[0]-colorO[0]) + 4*colorO[0] + 2) >> 2),255); + img[channels*width*(starty+yy) + channels*(startx+xx) + 1] = CLAMP(0, ((xx*(colorH[1]-colorO[1]) + yy*(colorV[1]-colorO[1]) + 4*colorO[1] + 2) >> 2),255); + img[channels*width*(starty+yy) + channels*(startx+xx) + 2] = CLAMP(0, ((xx*(colorH[2]-colorO[2]) + yy*(colorV[2]-colorO[2]) + 4*colorO[2] + 2) >> 2),255); + + //Equivalent method + /*img[channels*width*(starty+yy) + channels*(startx+xx) + 0] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[0]-colorO[0])/4.0 + yy*(colorV[0]-colorO[0])/4.0 + colorO[0])), 255); + img[channels*width*(starty+yy) + channels*(startx+xx) + 1] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[1]-colorO[1])/4.0 + yy*(colorV[1]-colorO[1])/4.0 + colorO[1])), 255); + img[channels*width*(starty+yy) + channels*(startx+xx) + 2] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[2]-colorO[2])/4.0 + yy*(colorV[2]-colorO[2])/4.0 + colorO[2])), 255);*/ + + } + } +} +void decompressBlockPlanar57(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockPlanar57c(compressed57_1, compressed57_2, img, width, height, startx, starty, 3); +} +// Decompress an ETC1 block (or ETC2 using individual or differential mode). +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockDiffFlipC(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty, int channels) +{ + uint8 avg_color[3], enc_color1[3], enc_color2[3]; + signed char diff[3]; + int table; + int index,shift; + int r,g,b; + int diffbit; + int flipbit; + + diffbit = (GETBITSHIGH(block_part1, 1, 33)); + flipbit = (GETBITSHIGH(block_part1, 1, 32)); + + if( !diffbit ) + { + // We have diffbit = 0. + + // First decode left part of block. + avg_color[0]= GETBITSHIGH(block_part1, 4, 63); + avg_color[1]= GETBITSHIGH(block_part1, 4, 55); + avg_color[2]= GETBITSHIGH(block_part1, 4, 47); + + // Here, we should really multiply by 17 instead of 16. This can + // be done by just copying the four lower bits to the upper ones + // while keeping the lower bits. + avg_color[0] |= (avg_color[0] <<4); + avg_color[1] |= (avg_color[1] <<4); + avg_color[2] |= (avg_color[2] <<4); + + table = GETBITSHIGH(block_part1, 3, 39) << 1; + + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + } + } + else + { + // We should flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift+=2; + } + } + + // Now decode other part of block. + avg_color[0]= GETBITSHIGH(block_part1, 4, 59); + avg_color[1]= GETBITSHIGH(block_part1, 4, 51); + avg_color[2]= GETBITSHIGH(block_part1, 4, 43); + + // Here, we should really multiply by 17 instead of 16. This can + // be done by just copying the four lower bits to the upper ones + // while keeping the lower bits. + avg_color[0] |= (avg_color[0] <<4); + avg_color[1] |= (avg_color[1] <<4); + avg_color[2] |= (avg_color[2] <<4); + + table = GETBITSHIGH(block_part1, 3, 36) << 1; + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift=8; + for(int x=startx+2; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + } + } + else + { + // We should flip + shift=2; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift += 2; + } + } + } + else + { + // We have diffbit = 1. + +// 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 +// --------------------------------------------------------------------------------------------------- +// | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| +// | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | +// --------------------------------------------------------------------------------------------------- +// +// +// c) bit layout in bits 31 through 0 (in both cases) +// +// 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// -------------------------------------------------------------------------------------------------- +// | most significant pixel index bits | least significant pixel index bits | +// | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | +// -------------------------------------------------------------------------------------------------- + + // First decode left part of block. + enc_color1[0]= GETBITSHIGH(block_part1, 5, 63); + enc_color1[1]= GETBITSHIGH(block_part1, 5, 55); + enc_color1[2]= GETBITSHIGH(block_part1, 5, 47); + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color1[0] <<3) | (enc_color1[0] >> 2); + avg_color[1] = (enc_color1[1] <<3) | (enc_color1[1] >> 2); + avg_color[2] = (enc_color1[2] <<3) | (enc_color1[2] >> 2); + + table = GETBITSHIGH(block_part1, 3, 39) << 1; + + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + } + } + else + { + // We should flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift+=2; + } + } + + // Now decode right part of block. + diff[0]= GETBITSHIGH(block_part1, 3, 58); + diff[1]= GETBITSHIGH(block_part1, 3, 50); + diff[2]= GETBITSHIGH(block_part1, 3, 42); + + // Extend sign bit to entire byte. + diff[0] = (diff[0] << 5); + diff[1] = (diff[1] << 5); + diff[2] = (diff[2] << 5); + diff[0] = diff[0] >> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + // Calculale second color + enc_color2[0]= enc_color1[0] + diff[0]; + enc_color2[1]= enc_color1[1] + diff[1]; + enc_color2[2]= enc_color1[2] + diff[2]; + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color2[0] <<3) | (enc_color2[0] >> 2); + avg_color[1] = (enc_color2[1] <<3) | (enc_color2[1] >> 2); + avg_color[2] = (enc_color2[2] <<3) | (enc_color2[2] >> 2); + + table = GETBITSHIGH(block_part1, 3, 36) << 1; + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift=8; + for(int x=startx+2; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + } + } + else + { + // We should flip + shift=2; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift += 2; + } + } + } +} +void decompressBlockDiffFlip(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockDiffFlipC(block_part1, block_part2, img, width, height, startx, starty, 3); +} + +// Decompress an ETC2 RGB block +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockETC2c(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty, int channels) +{ + int diffbit; + signed char color1[3]; + signed char diff[3]; + signed char red, green, blue; + + diffbit = (GETBITSHIGH(block_part1, 1, 33)); + + if( diffbit ) + { + // We have diffbit = 1; + + // Base color + color1[0]= GETBITSHIGH(block_part1, 5, 63); + color1[1]= GETBITSHIGH(block_part1, 5, 55); + color1[2]= GETBITSHIGH(block_part1, 5, 47); + + // Diff color + diff[0]= GETBITSHIGH(block_part1, 3, 58); + diff[1]= GETBITSHIGH(block_part1, 3, 50); + diff[2]= GETBITSHIGH(block_part1, 3, 42); + + // Extend sign bit to entire byte. + diff[0] = (diff[0] << 5); + diff[1] = (diff[1] << 5); + diff[2] = (diff[2] << 5); + diff[0] = diff[0] >> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + red = color1[0] + diff[0]; + green = color1[1] + diff[1]; + blue = color1[2] + diff[2]; + + if(red < 0 || red > 31) + { + unsigned int block59_part1, block59_part2; + unstuff59bits(block_part1, block_part2, block59_part1, block59_part2); + decompressBlockTHUMB59Tc(block59_part1, block59_part2, img, width, height, startx, starty, channels); + } + else if (green < 0 || green > 31) + { + unsigned int block58_part1, block58_part2; + unstuff58bits(block_part1, block_part2, block58_part1, block58_part2); + decompressBlockTHUMB58Hc(block58_part1, block58_part2, img, width, height, startx, starty, channels); + } + else if(blue < 0 || blue > 31) + { + unsigned int block57_part1, block57_part2; + + unstuff57bits(block_part1, block_part2, block57_part1, block57_part2); + decompressBlockPlanar57c(block57_part1, block57_part2, img, width, height, startx, starty, channels); + } + else + { + decompressBlockDiffFlipC(block_part1, block_part2, img, width, height, startx, starty, channels); + } + } + else + { + // We have diffbit = 0; + decompressBlockDiffFlipC(block_part1, block_part2, img, width, height, startx, starty, channels); + } +} +void decompressBlockETC2(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockETC2c(block_part1, block_part2, img, width, height, startx, starty, 3); +} +// Decompress an ETC2 block with punchthrough alpha +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockDifferentialWithAlphaC(unsigned int block_part1, unsigned int block_part2, uint8* img, uint8* alpha, int width, int height, int startx, int starty, int channelsRGB) +{ + + uint8 avg_color[3], enc_color1[3], enc_color2[3]; + signed char diff[3]; + int table; + int index,shift; + int r,g,b; + int diffbit; + int flipbit; + int channelsA; + + if(channelsRGB == 3) + { + // We will decode the alpha data to a separate memory area. + channelsA = 1; + } + else + { + // We will decode the RGB data and the alpha data to the same memory area, + // interleaved as RGBA. + channelsA = 4; + alpha = &img[0+3]; + } + + //the diffbit now encodes whether or not the entire alpha channel is 255. + diffbit = (GETBITSHIGH(block_part1, 1, 33)); + flipbit = (GETBITSHIGH(block_part1, 1, 32)); + + // First decode left part of block. + enc_color1[0]= GETBITSHIGH(block_part1, 5, 63); + enc_color1[1]= GETBITSHIGH(block_part1, 5, 55); + enc_color1[2]= GETBITSHIGH(block_part1, 5, 47); + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color1[0] <<3) | (enc_color1[0] >> 2); + avg_color[1] = (enc_color1[1] <<3) | (enc_color1[1] >> 2); + avg_color[2] = (enc_color1[2] <<3) | (enc_color1[2] >> 2); + + table = GETBITSHIGH(block_part1, 3, 39) << 1; + + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + int mod = compressParams[table][index]; + if(diffbit==0&&(index==1||index==2)) + { + mod=0; + } + + r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); + b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); + if(diffbit==0&&index==1) + { + alpha[(y*width+x)*channelsA]=0; + r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; + b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; + } + else + { + alpha[(y*width+x)*channelsA]=255; + } + + } + } + } + else + { + // We should flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + int mod = compressParams[table][index]; + if(diffbit==0&&(index==1||index==2)) + { + mod=0; + } + r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); + b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); + if(diffbit==0&&index==1) + { + alpha[(y*width+x)*channelsA]=0; + r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; + b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; + } + else + { + alpha[(y*width+x)*channelsA]=255; + } + } + shift+=2; + } + } + // Now decode right part of block. + diff[0]= GETBITSHIGH(block_part1, 3, 58); + diff[1]= GETBITSHIGH(block_part1, 3, 50); + diff[2]= GETBITSHIGH(block_part1, 3, 42); + + // Extend sign bit to entire byte. + diff[0] = (diff[0] << 5); + diff[1] = (diff[1] << 5); + diff[2] = (diff[2] << 5); + diff[0] = diff[0] >> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + // Calculate second color + enc_color2[0]= enc_color1[0] + diff[0]; + enc_color2[1]= enc_color1[1] + diff[1]; + enc_color2[2]= enc_color1[2] + diff[2]; + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color2[0] <<3) | (enc_color2[0] >> 2); + avg_color[1] = (enc_color2[1] <<3) | (enc_color2[1] >> 2); + avg_color[2] = (enc_color2[2] <<3) | (enc_color2[2] >> 2); + + table = GETBITSHIGH(block_part1, 3, 36) << 1; + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift=8; + for(int x=startx+2; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + int mod = compressParams[table][index]; + if(diffbit==0&&(index==1||index==2)) + { + mod=0; + } + + r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); + b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); + if(diffbit==0&&index==1) + { + alpha[(y*width+x)*channelsA]=0; + r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; + b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; + } + else + { + alpha[(y*width+x)*channelsA]=255; + } + } + } + } + else + { + // We should flip + shift=2; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + int mod = compressParams[table][index]; + if(diffbit==0&&(index==1||index==2)) + { + mod=0; + } + + r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); + b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); + if(diffbit==0&&index==1) + { + alpha[(y*width+x)*channelsA]=0; + r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; + b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; + } + else + { + alpha[(y*width+x)*channelsA]=255; + } + } + shift += 2; + } + } +} +void decompressBlockDifferentialWithAlpha(unsigned int block_part1, unsigned int block_part2, uint8* img, uint8* alpha, int width, int height, int startx, int starty) +{ + decompressBlockDifferentialWithAlphaC(block_part1, block_part2, img, alpha, width, height, startx, starty, 3); +} + + +// similar to regular decompression, but alpha channel is set to 0 if pixel index is 2, otherwise 255. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockTHUMB59TAlphaC(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty, int channelsRGB) +{ + + uint8 colorsRGB444[2][3]; + uint8 colors[2][3]; + uint8 paint_colors[4][3]; + uint8 distance; + uint8 block_mask[4][4]; + int channelsA; + + if(channelsRGB == 3) + { + // We will decode the alpha data to a separate memory area. + channelsA = 1; + } + else + { + // We will decode the RGB data and the alpha data to the same memory area, + // interleaved as RGBA. + channelsA = 4; + alpha = &img[0+3]; + } + + // First decode left part of block. + colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 58); + colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 54); + colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 50); + + colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 46); + colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 42); + colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 38); + + distance = GETBITSHIGH(block_part1, TABLE_BITS_59T, 34); + + // Extend the two colors to RGB888 + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + calculatePaintColors59T(distance, PATTERN_T, colors, paint_colors); + + // Choose one of the four paint colors for each texel + for (uint8 x = 0; x < BLOCKWIDTH; ++x) + { + for (uint8 y = 0; y < BLOCKHEIGHT; ++y) + { + //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); + block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; + block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); + img[channelsRGB*((starty+y)*width+startx+x)+R] = + CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED + img[channelsRGB*((starty+y)*width+startx+x)+G] = + CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN + img[channelsRGB*((starty+y)*width+startx+x)+B] = + CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE + if(block_mask[x][y]==2) + { + alpha[channelsA*(x+startx+(y+starty)*width)]=0; + img[channelsRGB*((starty+y)*width+startx+x)+R] =0; + img[channelsRGB*((starty+y)*width+startx+x)+G] =0; + img[channelsRGB*((starty+y)*width+startx+x)+B] =0; + } + else + alpha[channelsA*(x+startx+(y+starty)*width)]=255; + } + } +} +void decompressBlockTHUMB59TAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty) +{ + decompressBlockTHUMB59TAlphaC(block_part1, block_part2, img, alpha, width, height, startx, starty, 3); +} + + +// Decompress an H-mode block with alpha +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockTHUMB58HAlphaC(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty, int channelsRGB) +{ + unsigned int col0, col1; + uint8 colors[2][3]; + uint8 colorsRGB444[2][3]; + uint8 paint_colors[4][3]; + uint8 distance; + uint8 block_mask[4][4]; + int channelsA; + + if(channelsRGB == 3) + { + // We will decode the alpha data to a separate memory area. + channelsA = 1; + } + else + { + // We will decode the RGB data and the alpha data to the same memory area, + // interleaved as RGBA. + channelsA = 4; + alpha = &img[0+3]; + } + + // First decode left part of block. + colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 57); + colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 53); + colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 49); + + colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 45); + colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 41); + colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 37); + + distance = 0; + distance = (GETBITSHIGH(block_part1, 2, 33)) << 1; + + col0 = GETBITSHIGH(block_part1, 12, 57); + col1 = GETBITSHIGH(block_part1, 12, 45); + + if(col0 >= col1) + { + distance |= 1; + } + + // Extend the two colors to RGB888 + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + calculatePaintColors58H(distance, PATTERN_H, colors, paint_colors); + + // Choose one of the four paint colors for each texel + for (uint8 x = 0; x < BLOCKWIDTH; ++x) + { + for (uint8 y = 0; y < BLOCKHEIGHT; ++y) + { + //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); + block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; + block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); + img[channelsRGB*((starty+y)*width+startx+x)+R] = + CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED + img[channelsRGB*((starty+y)*width+startx+x)+G] = + CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN + img[channelsRGB*((starty+y)*width+startx+x)+B] = + CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE + + if(block_mask[x][y]==2) + { + alpha[channelsA*(x+startx+(y+starty)*width)]=0; + img[channelsRGB*((starty+y)*width+startx+x)+R] =0; + img[channelsRGB*((starty+y)*width+startx+x)+G] =0; + img[channelsRGB*((starty+y)*width+startx+x)+B] =0; + } + else + alpha[channelsA*(x+startx+(y+starty)*width)]=255; + } + } +} +void decompressBlockTHUMB58HAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty) +{ + decompressBlockTHUMB58HAlphaC(block_part1, block_part2, img, alpha, width, height, startx, starty, 3); +} +// Decompression function for ETC2_RGBA1 format. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockETC21BitAlphaC(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alphaimg, int width, int height, int startx, int starty, int channelsRGB) +{ + int diffbit; + signed char color1[3]; + signed char diff[3]; + signed char red, green, blue; + int channelsA; + + if(channelsRGB == 3) + { + // We will decode the alpha data to a separate memory area. + channelsA = 1; + } + else + { + // We will decode the RGB data and the alpha data to the same memory area, + // interleaved as RGBA. + channelsA = 4; + alphaimg = &img[0+3]; + } + + diffbit = (GETBITSHIGH(block_part1, 1, 33)); + + if( diffbit ) + { + // We have diffbit = 1, meaning no transparent pixels. regular decompression. + + // Base color + color1[0]= GETBITSHIGH(block_part1, 5, 63); + color1[1]= GETBITSHIGH(block_part1, 5, 55); + color1[2]= GETBITSHIGH(block_part1, 5, 47); + + // Diff color + diff[0]= GETBITSHIGH(block_part1, 3, 58); + diff[1]= GETBITSHIGH(block_part1, 3, 50); + diff[2]= GETBITSHIGH(block_part1, 3, 42); + + // Extend sign bit to entire byte. + diff[0] = (diff[0] << 5); + diff[1] = (diff[1] << 5); + diff[2] = (diff[2] << 5); + diff[0] = diff[0] >> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + red = color1[0] + diff[0]; + green = color1[1] + diff[1]; + blue = color1[2] + diff[2]; + + if(red < 0 || red > 31) + { + unsigned int block59_part1, block59_part2; + unstuff59bits(block_part1, block_part2, block59_part1, block59_part2); + decompressBlockTHUMB59Tc(block59_part1, block59_part2, img, width, height, startx, starty, channelsRGB); + } + else if (green < 0 || green > 31) + { + unsigned int block58_part1, block58_part2; + unstuff58bits(block_part1, block_part2, block58_part1, block58_part2); + decompressBlockTHUMB58Hc(block58_part1, block58_part2, img, width, height, startx, starty, channelsRGB); + } + else if(blue < 0 || blue > 31) + { + unsigned int block57_part1, block57_part2; + + unstuff57bits(block_part1, block_part2, block57_part1, block57_part2); + decompressBlockPlanar57c(block57_part1, block57_part2, img, width, height, startx, starty, channelsRGB); + } + else + { + decompressBlockDifferentialWithAlphaC(block_part1, block_part2, img, alphaimg, width, height, startx, starty, channelsRGB); + } + for(int x=startx; x> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + red = color1[0] + diff[0]; + green = color1[1] + diff[1]; + blue = color1[2] + diff[2]; + if(red < 0 || red > 31) + { + unsigned int block59_part1, block59_part2; + unstuff59bits(block_part1, block_part2, block59_part1, block59_part2); + decompressBlockTHUMB59TAlphaC(block59_part1, block59_part2, img, alphaimg, width, height, startx, starty, channelsRGB); + } + else if(green < 0 || green > 31) + { + unsigned int block58_part1, block58_part2; + unstuff58bits(block_part1, block_part2, block58_part1, block58_part2); + decompressBlockTHUMB58HAlphaC(block58_part1, block58_part2, img, alphaimg, width, height, startx, starty, channelsRGB); + } + else if(blue < 0 || blue > 31) + { + unsigned int block57_part1, block57_part2; + + unstuff57bits(block_part1, block_part2, block57_part1, block57_part2); + decompressBlockPlanar57c(block57_part1, block57_part2, img, width, height, startx, starty, channelsRGB); + for(int x=startx; xtopos) + return ((1<>(frompos-topos); + return ((1<255) + val=255; + return val; +} + +// Decodes tha alpha component in a block coded with GL_COMPRESSED_RGBA8_ETC2_EAC. +// Note that this decoding is slightly different from that of GL_COMPRESSED_R11_EAC. +// However, a hardware decoder can share gates between the two formats as explained +// in the specification under GL_COMPRESSED_R11_EAC. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockAlphaC(uint8* data, uint8* img, int width, int height, int ix, int iy, int channels) +{ + int alpha = data[0]; + int table = data[1]; + + int bit=0; + int byte=2; + //extract an alpha value for each pixel. + for(int x=0; x<4; x++) + { + for(int y=0; y<4; y++) + { + //Extract table index + int index=0; + for(int bitpos=0; bitpos<3; bitpos++) + { + index|=getbit(data[byte],7-bit,2-bitpos); + bit++; + if(bit>7) + { + bit=0; + byte++; + } + } + img[(ix+x+(iy+y)*width)*channels]=clamp(alpha +alphaTable[table][index]); + } + } +} +void decompressBlockAlpha(uint8* data, uint8* img, int width, int height, int ix, int iy) +{ + decompressBlockAlphaC(data, img, width, height, ix, iy, 1); +} + +// Does decompression and then immediately converts from 11 bit signed to a 16-bit format. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int16 get16bits11signed(int base, int table, int mul, int index) +{ + int elevenbase = base-128; + if(elevenbase==-128) + elevenbase=-127; + elevenbase*=8; + //i want the positive value here + int tabVal = -alphaBase[table][3-index%4]-1; + //and the sign, please + int sign = 1-(index/4); + + if(sign) + tabVal=tabVal+1; + int elevenTabVal = tabVal*8; + + if(mul!=0) + elevenTabVal*=mul; + else + elevenTabVal/=8; + + if(sign) + elevenTabVal=-elevenTabVal; + + //calculate sum + int elevenbits = elevenbase+elevenTabVal; + + //clamp.. + if(elevenbits>=1024) + elevenbits=1023; + else if(elevenbits<-1023) + elevenbits=-1023; + //this is the value we would actually output.. + //but there aren't any good 11-bit file or uncompressed GL formats + //so we extend to 15 bits signed. + sign = elevenbits<0; + elevenbits=abs(elevenbits); + int16 fifteenbits = (elevenbits<<5)+(elevenbits>>5); + int16 sixteenbits=fifteenbits; + + if(sign) + sixteenbits=-sixteenbits; + + return sixteenbits; +} + +// Does decompression and then immediately converts from 11 bit signed to a 16-bit format +// Calculates the 11 bit value represented by base, table, mul and index, and extends it to 16 bits. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +uint16 get16bits11bits(int base, int table, int mul, int index) +{ + int elevenbase = base*8+4; + + //i want the positive value here + int tabVal = -alphaBase[table][3-index%4]-1; + //and the sign, please + int sign = 1-(index/4); + + if(sign) + tabVal=tabVal+1; + int elevenTabVal = tabVal*8; + + if(mul!=0) + elevenTabVal*=mul; + else + elevenTabVal/=8; + + if(sign) + elevenTabVal=-elevenTabVal; + + //calculate sum + int elevenbits = elevenbase+elevenTabVal; + + //clamp.. + if(elevenbits>=256*8) + elevenbits=256*8-1; + else if(elevenbits<0) + elevenbits=0; + //elevenbits now contains the 11 bit alpha value as defined in the spec. + + //extend to 16 bits before returning, since we don't have any good 11-bit file formats. + uint16 sixteenbits = (elevenbits<<5)+(elevenbits>>6); + + return sixteenbits; +} + +// Decompresses a block using one of the GL_COMPRESSED_R11_EAC or GL_COMPRESSED_SIGNED_R11_EAC-formats +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockAlpha16bitC(uint8* data, uint8* img, int width, int height, int ix, int iy, int channels) +{ + int alpha = data[0]; + int table = data[1]; + + if(formatSigned) + { + //if we have a signed format, the base value is given as a signed byte. We convert it to (0-255) here, + //so more code can be shared with the unsigned mode. + alpha = *((signed char*)(&data[0])); + alpha = alpha+128; + } + + int bit=0; + int byte=2; + //extract an alpha value for each pixel. + for(int x=0; x<4; x++) + { + for(int y=0; y<4; y++) + { + //Extract table index + int index=0; + for(int bitpos=0; bitpos<3; bitpos++) + { + index|=getbit(data[byte],7-bit,2-bitpos); + bit++; + if(bit>7) + { + bit=0; + byte++; + } + } + int windex = channels*(2*(ix+x+(iy+y)*width)); +#if !PGMOUT + if(formatSigned) + { + *(int16 *)&img[windex] = get16bits11signed(alpha,(table%16),(table/16),index); + } + else + { + *(uint16 *)&img[windex] = get16bits11bits(alpha,(table%16),(table/16),index); + } +#else + //make data compatible with the .pgm format. See the comment in compressBlockAlpha16() for details. + uint16 uSixteen; + if (formatSigned) + { + //the pgm-format only allows unsigned images, + //so we add 2^15 to get a 16-bit value. + uSixteen = get16bits11signed(alpha,(table%16),(table/16),index) + 256*128; + } + else + { + uSixteen = get16bits11bits(alpha,(table%16),(table/16),index); + } + //byte swap for pgm + img[windex] = uSixteen/256; + img[windex+1] = uSixteen%256; +#endif + + } + } +} + +void decompressBlockAlpha16bit(uint8* data, uint8* img, int width, int height, int ix, int iy) +{ + decompressBlockAlpha16bitC(data, img, width, height, ix, iy, 1); +} diff --git a/source/etcpack.cxx b/source/etcpack.cpp old mode 100755 new mode 100644 similarity index 97% rename from source/etcpack.cxx rename to source/etcpack.cpp index f9cf60d..8250d3b --- a/source/etcpack.cxx +++ b/source/etcpack.cpp @@ -1,16085 +1,16087 @@ -//// etcpack v2.74 -//// -//// NO WARRANTY -//// -//// BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE THE PROGRAM IS PROVIDED -//// "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF ANY KIND, EXTENDS NO -//// WARRANTIES OR CONDITIONS OF ANY KIND; EITHER EXPRESS, IMPLIED OR -//// STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, IMPLIED OR -//// STATUTORY WARRANTIES OR CONDITIONS OF TITLE, MERCHANTABILITY, -//// SATISFACTORY QUALITY, SUITABILITY AND FITNESS FOR A PARTICULAR -//// PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -//// PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME -//// THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ERICSSON -//// MAKES NO WARRANTY THAT THE MANUFACTURE, SALE, OFFERING FOR SALE, -//// DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER THE LICENSE WILL BE FREE -//// FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER INTELLECTUAL -//// PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE LICENSE IS SUBJECT -//// TO YOUR SOLE RESPONSIBILITY TO MAKE SUCH DETERMINATION AND ACQUIRE -//// SUCH LICENSES AS MAY BE NECESSARY WITH RESPECT TO PATENTS, COPYRIGHT -//// AND OTHER INTELLECTUAL PROPERTY OF THIRD PARTIES. -//// -//// FOR THE AVOIDANCE OF DOUBT THE PROGRAM (I) IS NOT LICENSED FOR; (II) -//// IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY NOT BE USED FOR; -//// ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT LIMITED TO -//// OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR NETWORKS, -//// AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR ANY OTHER -//// COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR COMMUNICATION -//// SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE PROGRAM COULD LEAD TO -//// DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR ENVIRONMENTAL -//// DAMAGE. YOUR RIGHTS UNDER THIS LICENSE WILL TERMINATE AUTOMATICALLY -//// AND IMMEDIATELY WITHOUT NOTICE IF YOU FAIL TO COMPLY WITH THIS -//// PARAGRAPH. -//// -//// IN NO EVENT WILL ERICSSON, BE LIABLE FOR ANY DAMAGES WHATSOEVER, -//// INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, -//// INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN -//// CONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT -//// NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY OTHER -//// COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING RENDERED -//// INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF -//// THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) REGARDLESS OF THE -//// THEORY OF LIABILITY (CONTRACT, TORT OR OTHERWISE), EVEN IF SUCH HOLDER -//// OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -//// -//// (C) Ericsson AB 2005-2013. All Rights Reserved. -//// - -#include -#include -#include -#include -#include -#include -#include "image.h" - -// Typedefs -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef short int16; - -// Functions needed for decrompession ---- in etcdec.cxx -void read_big_endian_2byte_word(unsigned short *blockadr, FILE *f); -void read_big_endian_4byte_word(unsigned int *blockadr, FILE *f); -void unstuff57bits(unsigned int planar_word1, unsigned int planar_word2, unsigned int &planar57_word1, unsigned int &planar57_word2); -void unstuff59bits(unsigned int thumbT_word1, unsigned int thumbT_word2, unsigned int &thumbT59_word1, unsigned int &thumbT59_word2); -void unstuff58bits(unsigned int thumbH_word1, unsigned int thumbH_word2, unsigned int &thumbH58_word1, unsigned int &thumbH58_word2); -void decompressColor(int R_B, int G_B, int B_B, uint8 (colors_RGB444)[2][3], uint8 (colors)[2][3]); -void calculatePaintColors59T(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]); -void calculatePaintColors58H(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]); -void decompressBlockTHUMB59T(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty); -void decompressBlockTHUMB58H(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty); -void decompressBlockPlanar57(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img,int width,int height,int startx,int starty); -void decompressBlockDiffFlip(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty); -void decompressBlockETC2(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty); -void decompressBlockDifferentialWithAlpha(unsigned int block_part1,unsigned int block_part2, uint8* img, uint8* alpha, int width, int height, int startx, int starty); -void decompressBlockETC21BitAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alphaimg, int width,int height,int startx,int starty); -void decompressBlockTHUMB58HAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha,int width,int height,int startx,int starty); -void decompressBlockTHUMB59TAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha,int width,int height,int startx,int starty); -uint8 getbit(uint8 input, int frompos, int topos); -int clamp(int val); -void decompressBlockAlpha(uint8* data,uint8* img,int width,int height,int ix,int iy); -uint16 get16bits11bits(int base, int table, int mul, int index); -void decompressBlockAlpha16bit(uint8* data,uint8* img,int width,int height,int ix,int iy); -int16 get16bits11signed(int base, int table, int mul, int index); -void setupAlphaTable(); - - -// This source code is quite long. You can make it shorter by not including the -// code doing the exhaustive code. Then the -slow modes will not work, but the -// code will be approximately half the number of lines of code. -// Then the lines between "exhaustive code starts here" and "exhaustive code ends here" -// can then be removed. -#define EXHAUSTIVE_CODE_ACTIVE 1 - -// Remove warnings for unsafe functions such as strcpy -#pragma warning(disable : 4996) -// Remove warnings for conversions between different time variables -#pragma warning(disable : 4244) -// Remove warnings for negative or too big shifts -//#pragma warning(disable : 4293) - -#define CLAMP(ll,x,ul) (((x)<(ll)) ? (ll) : (((x)>(ul)) ? (ul) : (x))) -// The below code works as CLAMP(0, x, 255) if x < 255 -#define CLAMP_LEFT_ZERO(x) ((~(((int)(x))>>31))&(x)) -// The below code works as CLAMP(0, x, 255) if x is in [0,511] -#define CLAMP_RIGHT_255(x) (((( ((((int)(x))<<23)>>31) ))|(x))&0x000000ff) - -#define SQUARE(x) ((x)*(x)) -#define JAS_ROUND(x) (((x) < 0.0 ) ? ((int)((x)-0.5)) : ((int)((x)+0.5))) -#define JAS_MIN(a,b) ((a) < (b) ? (a) : (b)) -#define JAS_MAX(a,b) ((a) > (b) ? (a) : (b)) - -// The error metric Wr Wg Wb should be definied so that Wr^2 + Wg^2 + Wb^2 = 1. -// Hence it is easier to first define the squared values and derive the weights -// as their square-roots. - -#define PERCEPTUAL_WEIGHT_R_SQUARED 0.299 -#define PERCEPTUAL_WEIGHT_G_SQUARED 0.587 -#define PERCEPTUAL_WEIGHT_B_SQUARED 0.114 - -#define PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000 299 -#define PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 587 -#define PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000 114 - -#define RED(img,width,x,y) img[3*(y*width+x)+0] -#define GREEN(img,width,x,y) img[3*(y*width+x)+1] -#define BLUE(img,width,x,y) img[3*(y*width+x)+2] - -#define SHIFT(size,startpos) ((startpos)-(size)+1) -#define MASK(size, startpos) (((2<<(size-1))-1) << SHIFT(size,startpos)) -#define PUTBITS( dest, data, size, startpos) dest = ((dest & ~MASK(size, startpos)) | ((data << SHIFT(size, startpos)) & MASK(size,startpos))) -#define SHIFTHIGH(size, startpos) (((startpos)-32)-(size)+1) -#define MASKHIGH(size, startpos) (((1<<(size))-1) << SHIFTHIGH(size,startpos)) -#define PUTBITSHIGH(dest, data, size, startpos) dest = ((dest & ~MASKHIGH(size, startpos)) | ((data << SHIFTHIGH(size, startpos)) & MASKHIGH(size,startpos))) -#define GETBITS(source, size, startpos) (( (source) >> ((startpos)-(size)+1) ) & ((1<<(size)) -1)) -#define GETBITSHIGH(source, size, startpos) (( (source) >> (((startpos)-32)-(size)+1) ) & ((1<<(size)) -1)) - -// Thumb macros and definitions -#define R_BITS59T 4 -#define G_BITS59T 4 -#define B_BITS59T 4 -#define R_BITS58H 4 -#define G_BITS58H 4 -#define B_BITS58H 4 -#define MAXIMUM_ERROR (255*255*16*1000) -#define R 0 -#define G 1 -#define B 2 -#define BLOCKHEIGHT 4 -#define BLOCKWIDTH 4 -#define BINPOW(power) (1<<(power)) -//#define RADIUS 2 -#define TABLE_BITS_59T 3 -#define TABLE_BITS_58H 3 - -// Global tables -static uint8 table59T[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 59 bit T-mode -static uint8 table58H[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 58 bit H-mode -uint8 weight[3] = {1,1,1}; // Color weight - -// Enums -static enum{PATTERN_H = 0, - PATTERN_T = 1}; - -static enum{MODE_ETC1, MODE_THUMB_T, MODE_THUMB_H, MODE_PLANAR}; -// The ETC2 package of codecs includes the following codecs: -// -// codec enum -// -------------------------------------------------------- -// GL_COMPRESSED_R11_EAC 0x9270 -// GL_COMPRESSED_SIGNED_R11_EAC 0x9271 -// GL_COMPRESSED_RG11_EAC 0x9272 -// GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 -// GL_COMPRESSED_RGB8_ETC2 0x9274 -// GL_COMPRESSED_SRGB8_ETC2 0x9275 -// GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 -// GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 -// GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 -// GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 -// -// The older codec ETC1 is not included in the package -// GL_ETC1_RGB8_OES 0x8d64 -// but since ETC2 is backwards compatible an ETC1 texture can -// be decoded using the RGB8_ETC2 enum (0x9274) -// -// In a PKM-file, the codecs are stored using the following identifiers -// -// identifier value codec -// -------------------------------------------------------------------- -// ETC1_RGB_NO_MIPMAPS 0 GL_ETC1_RGB8_OES -// ETC2PACKAGE_RGB_NO_MIPMAPS 1 GL_COMPRESSED_RGB8_ETC2 -// ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD 2, not used - -// ETC2PACKAGE_RGBA_NO_MIPMAPS 3 GL_COMPRESSED_RGBA8_ETC2_EAC -// ETC2PACKAGE_RGBA1_NO_MIPMAPS 4 GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 -// ETC2PACKAGE_R_NO_MIPMAPS 5 GL_COMPRESSED_R11_EAC -// ETC2PACKAGE_RG_NO_MIPMAPS 6 GL_COMPRESSED_RG11_EAC -// ETC2PACKAGE_R_SIGNED_NO_MIPMAPS 7 GL_COMPRESSED_SIGNED_R11_EAC -// ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS 8 GL_COMPRESSED_SIGNED_RG11_EAC -// -// In the code, the identifiers are not always used strictly. For instance, the -// identifier ETC2PACKAGE_R_NO_MIPMAPS is sometimes used for both the unsigned -// (GL_COMPRESSED_R11_EAC) and signed (GL_COMPRESSED_SIGNED_R11_EAC) version of -// the codec. -// -static enum{ETC1_RGB_NO_MIPMAPS,ETC2PACKAGE_RGB_NO_MIPMAPS,ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD,ETC2PACKAGE_RGBA_NO_MIPMAPS,ETC2PACKAGE_RGBA1_NO_MIPMAPS,ETC2PACKAGE_R_NO_MIPMAPS,ETC2PACKAGE_RG_NO_MIPMAPS,ETC2PACKAGE_R_SIGNED_NO_MIPMAPS,ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS,ETC2PACKAGE_sRGB_NO_MIPMAPS,ETC2PACKAGE_sRGBA_NO_MIPMAPS,ETC2PACKAGE_sRGBA1_NO_MIPMAPS}; -static enum {MODE_COMPRESS, MODE_UNCOMPRESS, MODE_PSNR}; -static enum {SPEED_SLOW, SPEED_FAST, SPEED_MEDIUM}; -static enum {METRIC_PERCEPTUAL, METRIC_NONPERCEPTUAL}; -static enum {CODEC_ETC, CODEC_ETC2}; - -int mode = MODE_COMPRESS; -int speed = SPEED_FAST; -int metric = METRIC_PERCEPTUAL; -int codec = CODEC_ETC2; -int format = ETC2PACKAGE_RGB_NO_MIPMAPS; -int verbose = true; -extern int formatSigned; -int ktxFile=0; -bool first_time_message = true; - -static int scramble[4] = {3, 2, 0, 1}; -static int unscramble[4] = {2, 3, 1, 0}; - -typedef struct KTX_header_t -{ - uint8 identifier[12]; - unsigned int endianness; - unsigned int glType; - unsigned int glTypeSize; - unsigned int glFormat; - unsigned int glInternalFormat; - unsigned int glBaseInternalFormat; - unsigned int pixelWidth; - unsigned int pixelHeight; - unsigned int pixelDepth; - unsigned int numberOfArrayElements; - unsigned int numberOfFaces; - unsigned int numberOfMipmapLevels; - unsigned int bytesOfKeyValueData; -} -KTX_header; -#define KTX_IDENTIFIER_REF { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A } - -#define KTX_ENDIAN_REF (0x04030201) -#define KTX_ENDIAN_REF_REV (0x01020304) - -static enum {GL_R=0x1903,GL_RG=0x8227,GL_RGB=0x1907,GL_RGBA=0x1908}; -#define GL_SRGB 0x8C40 -#define GL_SRGB8 0x8C41 -#define GL_SRGB8_ALPHA8 0x8C43 -#define GL_ETC1_RGB8_OES 0x8d64 -#define GL_COMPRESSED_R11_EAC 0x9270 -#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 -#define GL_COMPRESSED_RG11_EAC 0x9272 -#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 -#define GL_COMPRESSED_RGB8_ETC2 0x9274 -#define GL_COMPRESSED_SRGB8_ETC2 0x9275 -#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 -#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 -#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 -#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 - - -int ktx_identifier[] = KTX_IDENTIFIER_REF; - - -//converts indices from |a0|a1|e0|e1|i0|i1|m0|m1|b0|b1|f0|f1|j0|j1|n0|n1|c0|c1|g0|g1|k0|k1|o0|o1|d0|d1|h0|h1|l0|l1|p0|p1| previously used by T- and H-modes -// into |p0|o0|n0|m0|l0|k0|j0|i0|h0|g0|f0|e0|d0|c0|b0|a0|p1|o1|n1|m1|l1|k1|j1|i1|h1|g1|f1|e1|d1|c1|b1|a1| which should be used for all modes. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int indexConversion(int pixelIndices) -{ - int correctIndices = 0; - int LSB[4][4]; - int MSB[4][4]; - int shift=0; - for(int y=3; y>=0; y--) - { - for(int x=3; x>=0; x--) - { - LSB[x][y] = (pixelIndices>>shift)&1; - shift++; - MSB[x][y] = (pixelIndices>>shift)&1; - shift++; - } - } - shift=0; - for(int x=0; x<4; x++) - { - for(int y=0; y<4; y++) - { - correctIndices|=(LSB[x][y]<=0) // find file name extension - { - if(src[q]=='.') break; - q--; - } - if(q<0) - return -1; - else - return q; -} - -// Read source file. Does conversion if file format is not .ppm. -// Will expand file to be divisible by four in the x- and y- dimension. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -bool readSrcFile(char *filename,uint8 *&img,int &width,int &height, int &expandedwidth, int &expandedheight) -{ - int w1,h1; - int wdiv4, hdiv4; - char str[255]; - - - // Delete temp file if it exists. - if(fileExist("tmp.ppm")) - { - sprintf(str, "del tmp.ppm\n"); - system(str); - } - - int q = find_pos_of_extension(filename); - if(!strcmp(&filename[q],".ppm")) - { - // Already a .ppm file. Just copy. - sprintf(str,"copy %s tmp.ppm \n", filename); - printf("Copying source file to tmp.ppm\n", filename); - } - else - { - // Converting from other format to .ppm - // - // Use your favorite command line image converter program, - // for instance Image Magick. Just make sure the syntax can - // be written as below: - // - // C:\magick convert source.jpg dest.ppm - // - sprintf(str,"magick convert %s tmp.ppm\n", filename); - printf("Converting source file from %s to .ppm\n", filename); - } - // Execute system call - system(str); - - int bitrate=8; - if(format==ETC2PACKAGE_RG_NO_MIPMAPS) - bitrate=16; - if(fReadPPM("tmp.ppm",w1,h1,img,bitrate)) - { - width=w1; - height=h1; - system("del tmp.ppm"); - - // Width must be divisible by 4 and height must be - // divisible by 4. Otherwise, we will expand the image - - wdiv4 = width / 4; - hdiv4 = height / 4; - - expandedwidth = width; - expandedheight = height; - - if( !(wdiv4 * 4 == width) ) - { - printf(" Width = %d is not divisible by four... ", width); - printf(" expanding image in x-dir... "); - if(expandToWidthDivByFour(img, width, height, expandedwidth, expandedheight,bitrate)) - { - printf("OK.\n"); - } - else - { - printf("\n Error: could not expand image\n"); - return false; - } - } - if( !(hdiv4 * 4 == height)) - { - printf(" Height = %d is not divisible by four... ", height); - printf(" expanding image in y-dir..."); - if(expandToHeightDivByFour(img, expandedwidth, height, expandedwidth, expandedheight,bitrate)) - { - printf("OK.\n"); - } - else - { - printf("\n Error: could not expand image\n"); - return false; - } - } - if(!(expandedwidth == width && expandedheight == height)) - printf("Active pixels: %dx%d. Expanded image: %dx%d\n",width,height,expandedwidth,expandedheight); - return true; - } - else - { - printf("Could not read tmp.ppm file\n"); - exit(1); - } - return false; - -} - -// Reads a file without expanding it to be divisible by 4. -// Is used when doing PSNR calculation between two files. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -bool readSrcFileNoExpand(char *filename,uint8 *&img,int &width,int &height) -{ - int w1,h1; - char str[255]; - - - // Delete temp file if it exists. - if(fileExist("tmp.ppm")) - { - sprintf(str, "del tmp.ppm\n"); - system(str); - } - - - int q = find_pos_of_extension(filename); - if(!strcmp(&filename[q],".ppm")) - { - // Already a .ppm file. Just copy. - sprintf(str,"copy %s tmp.ppm \n", filename); - printf("Copying source file to tmp.ppm\n", filename); - } - else - { - // Converting from other format to .ppm - // - // Use your favorite command line image converter program, - // for instance Image Magick. Just make sure the syntax can - // be written as below: - // - // C:\magick convert source.jpg dest.ppm - // - sprintf(str,"magick convert %s tmp.ppm\n", filename); -// printf("Converting source file from %s to .ppm\n", filename); - } - // Execute system call - system(str); - - if(fReadPPM("tmp.ppm",w1,h1,img,8)) - { - width=w1; - height=h1; - system("del tmp.ppm"); - - return true; - } - return false; -} - -// Parses the arguments from the command line. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void readArguments(int argc,char *argv[],char* src,char *dst) -{ - int q; - - //new code!! do this in a more nicer way! - bool srcfound=false,dstfound=false; - for(int i=1; i> 1), 1, i); - PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); - - i++; - - // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} - // so that first bit is sign bit and the other bit is size bit (4 or 12). - // This means that we have to scramble the bits before storing them. - sum_error+=min_error; - } - } - - *pixel_indices_MSBp = pixel_indices_MSB; - *pixel_indices_LSBp = pixel_indices_LSB; - return sum_error; -} - -#define MAXERR1000 1000*255*255*16 - -// Finds all pixel indices for a 2x4 block using perceptual weighting of error. -// Done using fixed poinit arithmetics where weights are multiplied by 1000. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockWithTable2x4percep1000(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) -{ - uint8 orig[3],approx[3]; - unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; - unsigned int sum_error=0; - int q, i; - - i = 0; - for(int x=startx; x> 1), 1, i); - PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); - - i++; - - // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} - // so that first bit is sign bit and the other bit is size bit (4 or 12). - // This means that we have to scramble the bits before storing them. - - - sum_error+=min_error; - } - - } - - *pixel_indices_MSBp = pixel_indices_MSB; - *pixel_indices_LSBp = pixel_indices_LSB; - - return sum_error; -} - -// Finds all pixel indices for a 2x4 block using perceptual weighting of error. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -float compressBlockWithTable2x4percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) -{ - uint8 orig[3],approx[3]; - unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; - float sum_error=0; - int q, i; - - double wR2 = PERCEPTUAL_WEIGHT_R_SQUARED; - double wG2 = PERCEPTUAL_WEIGHT_G_SQUARED; - double wB2 = PERCEPTUAL_WEIGHT_B_SQUARED; - - i = 0; - for(int x=startx; x> 1), 1, i); - PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); - - i++; - - // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} - // so that first bit is sign bit and the other bit is size bit (4 or 12). - // This means that we have to scramble the bits before storing them. - - sum_error+=min_error; - } - } - - *pixel_indices_MSBp = pixel_indices_MSB; - *pixel_indices_LSBp = pixel_indices_LSB; - - return sum_error; -} - -// Finds all pixel indices for a 4x2 block. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int compressBlockWithTable4x2(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) -{ - uint8 orig[3],approx[3]; - unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; - int sum_error=0; - int q; - int i; - - i = 0; - for(int x=startx; x> 1), 1, i); - PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); - i++; - - // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} - // so that first bit is sign bit and the other bit is size bit (4 or 12). - // This means that we have to scramble the bits before storing them. - - sum_error+=min_error; - } - i+=2; - } - - *pixel_indices_MSBp = pixel_indices_MSB; - *pixel_indices_LSBp = pixel_indices_LSB; - - return sum_error; -} - -// Finds all pixel indices for a 4x2 block using perceptual weighting of error. -// Done using fixed point arithmetics where 1000 corresponds to 1.0. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockWithTable4x2percep1000(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) -{ - uint8 orig[3],approx[3]; - unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; - unsigned int sum_error=0; - int q; - int i; - - i = 0; - for(int x=startx; x> 1), 1, i); - PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); - i++; - - // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} - // so that first bit is sign bit and the other bit is size bit (4 or 12). - // This means that we have to scramble the bits before storing them. - - sum_error+=min_error; - } - i+=2; - - } - - *pixel_indices_MSBp = pixel_indices_MSB; - *pixel_indices_LSBp = pixel_indices_LSB; - - return sum_error; -} - -// Finds all pixel indices for a 4x2 block using perceptual weighting of error. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -float compressBlockWithTable4x2percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) -{ - uint8 orig[3],approx[3]; - unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; - float sum_error=0; - int q; - int i; - float wR2 = (float) PERCEPTUAL_WEIGHT_R_SQUARED; - float wG2 = (float) PERCEPTUAL_WEIGHT_G_SQUARED; - float wB2 = (float) PERCEPTUAL_WEIGHT_B_SQUARED; - - i = 0; - for(int x=startx; x> 1), 1, i); - PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); - i++; - - // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} - // so that first bit is sign bit and the other bit is size bit (4 or 12). - // This means that we have to scramble the bits before storing them. - - sum_error+=min_error; - } - i+=2; - } - - *pixel_indices_MSBp = pixel_indices_MSB; - *pixel_indices_LSBp = pixel_indices_LSB; - - return sum_error; -} - -// Table for fast implementation of clamping to the interval [0,255] followed by addition of 255. -const int clamp_table_plus_255[768] = {0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, - 0+255, 1+255, 2+255, 3+255, 4+255, 5+255, 6+255, 7+255, 8+255, 9+255, 10+255, 11+255, 12+255, 13+255, 14+255, 15+255, 16+255, 17+255, 18+255, 19+255, 20+255, 21+255, 22+255, 23+255, 24+255, 25+255, 26+255, 27+255, 28+255, 29+255, 30+255, 31+255, 32+255, 33+255, 34+255, 35+255, 36+255, 37+255, 38+255, 39+255, 40+255, 41+255, 42+255, 43+255, 44+255, 45+255, 46+255, 47+255, 48+255, 49+255, 50+255, 51+255, 52+255, 53+255, 54+255, 55+255, 56+255, 57+255, 58+255, 59+255, 60+255, 61+255, 62+255, 63+255, 64+255, 65+255, 66+255, 67+255, 68+255, 69+255, 70+255, 71+255, 72+255, 73+255, 74+255, 75+255, 76+255, 77+255, 78+255, 79+255, 80+255, 81+255, 82+255, 83+255, 84+255, 85+255, 86+255, 87+255, 88+255, 89+255, 90+255, 91+255, 92+255, 93+255, 94+255, 95+255, 96+255, 97+255, 98+255, 99+255, 100+255, 101+255, 102+255, 103+255, 104+255, 105+255, 106+255, 107+255, 108+255, 109+255, 110+255, 111+255, 112+255, 113+255, 114+255, 115+255, 116+255, 117+255, 118+255, 119+255, 120+255, 121+255, 122+255, 123+255, 124+255, 125+255, 126+255, 127+255, 128+255, 129+255, 130+255, 131+255, 132+255, 133+255, 134+255, 135+255, 136+255, 137+255, 138+255, 139+255, 140+255, 141+255, 142+255, 143+255, 144+255, 145+255, 146+255, 147+255, 148+255, 149+255, 150+255, 151+255, 152+255, 153+255, 154+255, 155+255, 156+255, 157+255, 158+255, 159+255, 160+255, 161+255, 162+255, 163+255, 164+255, 165+255, 166+255, 167+255, 168+255, 169+255, 170+255, 171+255, 172+255, 173+255, 174+255, 175+255, 176+255, 177+255, 178+255, 179+255, 180+255, 181+255, 182+255, 183+255, 184+255, 185+255, 186+255, 187+255, 188+255, 189+255, 190+255, 191+255, 192+255, 193+255, 194+255, 195+255, 196+255, 197+255, 198+255, 199+255, 200+255, 201+255, 202+255, 203+255, 204+255, 205+255, 206+255, 207+255, 208+255, 209+255, 210+255, 211+255, - 212+255, 213+255, 214+255, 215+255, 216+255, 217+255, 218+255, 219+255, 220+255, 221+255, 222+255, 223+255, 224+255, 225+255, 226+255, 227+255, 228+255, 229+255, 230+255, 231+255, 232+255, 233+255, 234+255, 235+255, 236+255, 237+255, 238+255, 239+255, 240+255, 241+255, 242+255, 243+255, 244+255, 245+255, 246+255, 247+255, 248+255, 249+255, 250+255, 251+255, 252+255, 253+255, 254+255, 255+255, - 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, - 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255}; - -// Table for fast implementationi of clamping to the interval [0,255] -const int clamp_table[768] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; - -// Table for fast implementation of squaring for numbers in the interval [-255, 255] -const unsigned int square_table[511] = {65025, 64516, 64009, 63504, 63001, 62500, 62001, 61504, 61009, 60516, 60025, 59536, 59049, 58564, 58081, 57600, - 57121, 56644, 56169, 55696, 55225, 54756, 54289, 53824, 53361, 52900, 52441, 51984, 51529, 51076, 50625, 50176, - 49729, 49284, 48841, 48400, 47961, 47524, 47089, 46656, 46225, 45796, 45369, 44944, 44521, 44100, 43681, 43264, - 42849, 42436, 42025, 41616, 41209, 40804, 40401, 40000, 39601, 39204, 38809, 38416, 38025, 37636, 37249, 36864, - 36481, 36100, 35721, 35344, 34969, 34596, 34225, 33856, 33489, 33124, 32761, 32400, 32041, 31684, 31329, 30976, - 30625, 30276, 29929, 29584, 29241, 28900, 28561, 28224, 27889, 27556, 27225, 26896, 26569, 26244, 25921, 25600, - 25281, 24964, 24649, 24336, 24025, 23716, 23409, 23104, 22801, 22500, 22201, 21904, 21609, 21316, 21025, 20736, - 20449, 20164, 19881, 19600, 19321, 19044, 18769, 18496, 18225, 17956, 17689, 17424, 17161, 16900, 16641, 16384, - 16129, 15876, 15625, 15376, 15129, 14884, 14641, 14400, 14161, 13924, 13689, 13456, 13225, 12996, 12769, 12544, - 12321, 12100, 11881, 11664, 11449, 11236, 11025, 10816, 10609, 10404, 10201, 10000, 9801, 9604, 9409, 9216, - 9025, 8836, 8649, 8464, 8281, 8100, 7921, 7744, 7569, 7396, 7225, 7056, 6889, 6724, 6561, 6400, - 6241, 6084, 5929, 5776, 5625, 5476, 5329, 5184, 5041, 4900, 4761, 4624, 4489, 4356, 4225, 4096, - 3969, 3844, 3721, 3600, 3481, 3364, 3249, 3136, 3025, 2916, 2809, 2704, 2601, 2500, 2401, 2304, - 2209, 2116, 2025, 1936, 1849, 1764, 1681, 1600, 1521, 1444, 1369, 1296, 1225, 1156, 1089, 1024, - 961, 900, 841, 784, 729, 676, 625, 576, 529, 484, 441, 400, 361, 324, 289, 256, - 225, 196, 169, 144, 121, 100, 81, 64, 49, 36, 25, 16, 9, 4, 1, - 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, - 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, - 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, - 2304, 2401, 2500, 2601, 2704, 2809, 2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, - 4096, 4225, 4356, 4489, 4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, - 6400, 6561, 6724, 6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, - 9216, 9409, 9604, 9801, 10000, 10201, 10404, 10609, 10816, 11025, 11236, 11449, 11664, 11881, 12100, 12321, - 12544, 12769, 12996, 13225, 13456, 13689, 13924, 14161, 14400, 14641, 14884, 15129, 15376, 15625, 15876, 16129, - 16384, 16641, 16900, 17161, 17424, 17689, 17956, 18225, 18496, 18769, 19044, 19321, 19600, 19881, 20164, 20449, - 20736, 21025, 21316, 21609, 21904, 22201, 22500, 22801, 23104, 23409, 23716, 24025, 24336, 24649, 24964, 25281, - 25600, 25921, 26244, 26569, 26896, 27225, 27556, 27889, 28224, 28561, 28900, 29241, 29584, 29929, 30276, 30625, - 30976, 31329, 31684, 32041, 32400, 32761, 33124, 33489, 33856, 34225, 34596, 34969, 35344, 35721, 36100, 36481, - 36864, 37249, 37636, 38025, 38416, 38809, 39204, 39601, 40000, 40401, 40804, 41209, 41616, 42025, 42436, 42849, - 43264, 43681, 44100, 44521, 44944, 45369, 45796, 46225, 46656, 47089, 47524, 47961, 48400, 48841, 49284, 49729, - 50176, 50625, 51076, 51529, 51984, 52441, 52900, 53361, 53824, 54289, 54756, 55225, 55696, 56169, 56644, 57121, - 57600, 58081, 58564, 59049, 59536, 60025, 60516, 61009, 61504, 62001, 62500, 63001, 63504, 64009, 64516, 65025}; - -// Abbreviated variable names to make below tables smaller in source code size -#define KR PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000 -#define KG PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 -#define KB PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000 - -// Table for fast implementation of squaring for numbers in the interval [-255, 255] multiplied by the perceptual weight for red. -const unsigned int square_table_percep_red[511] = { - 65025*KR, 64516*KR, 64009*KR, 63504*KR, 63001*KR, 62500*KR, 62001*KR, 61504*KR, 61009*KR, 60516*KR, 60025*KR, 59536*KR, 59049*KR, 58564*KR, 58081*KR, 57600*KR, - 57121*KR, 56644*KR, 56169*KR, 55696*KR, 55225*KR, 54756*KR, 54289*KR, 53824*KR, 53361*KR, 52900*KR, 52441*KR, 51984*KR, 51529*KR, 51076*KR, 50625*KR, 50176*KR, - 49729*KR, 49284*KR, 48841*KR, 48400*KR, 47961*KR, 47524*KR, 47089*KR, 46656*KR, 46225*KR, 45796*KR, 45369*KR, 44944*KR, 44521*KR, 44100*KR, 43681*KR, 43264*KR, - 42849*KR, 42436*KR, 42025*KR, 41616*KR, 41209*KR, 40804*KR, 40401*KR, 40000*KR, 39601*KR, 39204*KR, 38809*KR, 38416*KR, 38025*KR, 37636*KR, 37249*KR, 36864*KR, - 36481*KR, 36100*KR, 35721*KR, 35344*KR, 34969*KR, 34596*KR, 34225*KR, 33856*KR, 33489*KR, 33124*KR, 32761*KR, 32400*KR, 32041*KR, 31684*KR, 31329*KR, 30976*KR, - 30625*KR, 30276*KR, 29929*KR, 29584*KR, 29241*KR, 28900*KR, 28561*KR, 28224*KR, 27889*KR, 27556*KR, 27225*KR, 26896*KR, 26569*KR, 26244*KR, 25921*KR, 25600*KR, - 25281*KR, 24964*KR, 24649*KR, 24336*KR, 24025*KR, 23716*KR, 23409*KR, 23104*KR, 22801*KR, 22500*KR, 22201*KR, 21904*KR, 21609*KR, 21316*KR, 21025*KR, 20736*KR, - 20449*KR, 20164*KR, 19881*KR, 19600*KR, 19321*KR, 19044*KR, 18769*KR, 18496*KR, 18225*KR, 17956*KR, 17689*KR, 17424*KR, 17161*KR, 16900*KR, 16641*KR, 16384*KR, - 16129*KR, 15876*KR, 15625*KR, 15376*KR, 15129*KR, 14884*KR, 14641*KR, 14400*KR, 14161*KR, 13924*KR, 13689*KR, 13456*KR, 13225*KR, 12996*KR, 12769*KR, 12544*KR, - 12321*KR, 12100*KR, 11881*KR, 11664*KR, 11449*KR, 11236*KR, 11025*KR, 10816*KR, 10609*KR, 10404*KR, 10201*KR, 10000*KR, 9801*KR, 9604*KR, 9409*KR, 9216*KR, - 9025*KR, 8836*KR, 8649*KR, 8464*KR, 8281*KR, 8100*KR, 7921*KR, 7744*KR, 7569*KR, 7396*KR, 7225*KR, 7056*KR, 6889*KR, 6724*KR, 6561*KR, 6400*KR, - 6241*KR, 6084*KR, 5929*KR, 5776*KR, 5625*KR, 5476*KR, 5329*KR, 5184*KR, 5041*KR, 4900*KR, 4761*KR, 4624*KR, 4489*KR, 4356*KR, 4225*KR, 4096*KR, - 3969*KR, 3844*KR, 3721*KR, 3600*KR, 3481*KR, 3364*KR, 3249*KR, 3136*KR, 3025*KR, 2916*KR, 2809*KR, 2704*KR, 2601*KR, 2500*KR, 2401*KR, 2304*KR, - 2209*KR, 2116*KR, 2025*KR, 1936*KR, 1849*KR, 1764*KR, 1681*KR, 1600*KR, 1521*KR, 1444*KR, 1369*KR, 1296*KR, 1225*KR, 1156*KR, 1089*KR, 1024*KR, - 961*KR, 900*KR, 841*KR, 784*KR, 729*KR, 676*KR, 625*KR, 576*KR, 529*KR, 484*KR, 441*KR, 400*KR, 361*KR, 324*KR, 289*KR, 256*KR, - 225*KR, 196*KR, 169*KR, 144*KR, 121*KR, 100*KR, 81*KR, 64*KR, 49*KR, 36*KR, 25*KR, 16*KR, 9*KR, 4*KR, 1*KR, - 0*KR, 1*KR, 4*KR, 9*KR, 16*KR, 25*KR, 36*KR, 49*KR, 64*KR, 81*KR, 100*KR, 121*KR, 144*KR, 169*KR, 196*KR, 225*KR, - 256*KR, 289*KR, 324*KR, 361*KR, 400*KR, 441*KR, 484*KR, 529*KR, 576*KR, 625*KR, 676*KR, 729*KR, 784*KR, 841*KR, 900*KR, 961*KR, - 1024*KR, 1089*KR, 1156*KR, 1225*KR, 1296*KR, 1369*KR, 1444*KR, 1521*KR, 1600*KR, 1681*KR, 1764*KR, 1849*KR, 1936*KR, 2025*KR, 2116*KR, 2209*KR, - 2304*KR, 2401*KR, 2500*KR, 2601*KR, 2704*KR, 2809*KR, 2916*KR, 3025*KR, 3136*KR, 3249*KR, 3364*KR, 3481*KR, 3600*KR, 3721*KR, 3844*KR, 3969*KR, - 4096*KR, 4225*KR, 4356*KR, 4489*KR, 4624*KR, 4761*KR, 4900*KR, 5041*KR, 5184*KR, 5329*KR, 5476*KR, 5625*KR, 5776*KR, 5929*KR, 6084*KR, 6241*KR, - 6400*KR, 6561*KR, 6724*KR, 6889*KR, 7056*KR, 7225*KR, 7396*KR, 7569*KR, 7744*KR, 7921*KR, 8100*KR, 8281*KR, 8464*KR, 8649*KR, 8836*KR, 9025*KR, - 9216*KR, 9409*KR, 9604*KR, 9801*KR, 10000*KR, 10201*KR, 10404*KR, 10609*KR, 10816*KR, 11025*KR, 11236*KR, 11449*KR, 11664*KR, 11881*KR, 12100*KR, 12321*KR, - 12544*KR, 12769*KR, 12996*KR, 13225*KR, 13456*KR, 13689*KR, 13924*KR, 14161*KR, 14400*KR, 14641*KR, 14884*KR, 15129*KR, 15376*KR, 15625*KR, 15876*KR, 16129*KR, - 16384*KR, 16641*KR, 16900*KR, 17161*KR, 17424*KR, 17689*KR, 17956*KR, 18225*KR, 18496*KR, 18769*KR, 19044*KR, 19321*KR, 19600*KR, 19881*KR, 20164*KR, 20449*KR, - 20736*KR, 21025*KR, 21316*KR, 21609*KR, 21904*KR, 22201*KR, 22500*KR, 22801*KR, 23104*KR, 23409*KR, 23716*KR, 24025*KR, 24336*KR, 24649*KR, 24964*KR, 25281*KR, - 25600*KR, 25921*KR, 26244*KR, 26569*KR, 26896*KR, 27225*KR, 27556*KR, 27889*KR, 28224*KR, 28561*KR, 28900*KR, 29241*KR, 29584*KR, 29929*KR, 30276*KR, 30625*KR, - 30976*KR, 31329*KR, 31684*KR, 32041*KR, 32400*KR, 32761*KR, 33124*KR, 33489*KR, 33856*KR, 34225*KR, 34596*KR, 34969*KR, 35344*KR, 35721*KR, 36100*KR, 36481*KR, - 36864*KR, 37249*KR, 37636*KR, 38025*KR, 38416*KR, 38809*KR, 39204*KR, 39601*KR, 40000*KR, 40401*KR, 40804*KR, 41209*KR, 41616*KR, 42025*KR, 42436*KR, 42849*KR, - 43264*KR, 43681*KR, 44100*KR, 44521*KR, 44944*KR, 45369*KR, 45796*KR, 46225*KR, 46656*KR, 47089*KR, 47524*KR, 47961*KR, 48400*KR, 48841*KR, 49284*KR, 49729*KR, - 50176*KR, 50625*KR, 51076*KR, 51529*KR, 51984*KR, 52441*KR, 52900*KR, 53361*KR, 53824*KR, 54289*KR, 54756*KR, 55225*KR, 55696*KR, 56169*KR, 56644*KR, 57121*KR, - 57600*KR, 58081*KR, 58564*KR, 59049*KR, 59536*KR, 60025*KR, 60516*KR, 61009*KR, 61504*KR, 62001*KR, 62500*KR, 63001*KR, 63504*KR, 64009*KR, 64516*KR, 65025*KR}; - -// Table for fast implementation of squaring for numbers in the interval [-255, 255] multiplied by the perceptual weight for green. -const unsigned int square_table_percep_green[511] = { - 65025*KG, 64516*KG, 64009*KG, 63504*KG, 63001*KG, 62500*KG, 62001*KG, 61504*KG, 61009*KG, 60516*KG, 60025*KG, 59536*KG, 59049*KG, 58564*KG, 58081*KG, 57600*KG, - 57121*KG, 56644*KG, 56169*KG, 55696*KG, 55225*KG, 54756*KG, 54289*KG, 53824*KG, 53361*KG, 52900*KG, 52441*KG, 51984*KG, 51529*KG, 51076*KG, 50625*KG, 50176*KG, - 49729*KG, 49284*KG, 48841*KG, 48400*KG, 47961*KG, 47524*KG, 47089*KG, 46656*KG, 46225*KG, 45796*KG, 45369*KG, 44944*KG, 44521*KG, 44100*KG, 43681*KG, 43264*KG, - 42849*KG, 42436*KG, 42025*KG, 41616*KG, 41209*KG, 40804*KG, 40401*KG, 40000*KG, 39601*KG, 39204*KG, 38809*KG, 38416*KG, 38025*KG, 37636*KG, 37249*KG, 36864*KG, - 36481*KG, 36100*KG, 35721*KG, 35344*KG, 34969*KG, 34596*KG, 34225*KG, 33856*KG, 33489*KG, 33124*KG, 32761*KG, 32400*KG, 32041*KG, 31684*KG, 31329*KG, 30976*KG, - 30625*KG, 30276*KG, 29929*KG, 29584*KG, 29241*KG, 28900*KG, 28561*KG, 28224*KG, 27889*KG, 27556*KG, 27225*KG, 26896*KG, 26569*KG, 26244*KG, 25921*KG, 25600*KG, - 25281*KG, 24964*KG, 24649*KG, 24336*KG, 24025*KG, 23716*KG, 23409*KG, 23104*KG, 22801*KG, 22500*KG, 22201*KG, 21904*KG, 21609*KG, 21316*KG, 21025*KG, 20736*KG, - 20449*KG, 20164*KG, 19881*KG, 19600*KG, 19321*KG, 19044*KG, 18769*KG, 18496*KG, 18225*KG, 17956*KG, 17689*KG, 17424*KG, 17161*KG, 16900*KG, 16641*KG, 16384*KG, - 16129*KG, 15876*KG, 15625*KG, 15376*KG, 15129*KG, 14884*KG, 14641*KG, 14400*KG, 14161*KG, 13924*KG, 13689*KG, 13456*KG, 13225*KG, 12996*KG, 12769*KG, 12544*KG, - 12321*KG, 12100*KG, 11881*KG, 11664*KG, 11449*KG, 11236*KG, 11025*KG, 10816*KG, 10609*KG, 10404*KG, 10201*KG, 10000*KG, 9801*KG, 9604*KG, 9409*KG, 9216*KG, - 9025*KG, 8836*KG, 8649*KG, 8464*KG, 8281*KG, 8100*KG, 7921*KG, 7744*KG, 7569*KG, 7396*KG, 7225*KG, 7056*KG, 6889*KG, 6724*KG, 6561*KG, 6400*KG, - 6241*KG, 6084*KG, 5929*KG, 5776*KG, 5625*KG, 5476*KG, 5329*KG, 5184*KG, 5041*KG, 4900*KG, 4761*KG, 4624*KG, 4489*KG, 4356*KG, 4225*KG, 4096*KG, - 3969*KG, 3844*KG, 3721*KG, 3600*KG, 3481*KG, 3364*KG, 3249*KG, 3136*KG, 3025*KG, 2916*KG, 2809*KG, 2704*KG, 2601*KG, 2500*KG, 2401*KG, 2304*KG, - 2209*KG, 2116*KG, 2025*KG, 1936*KG, 1849*KG, 1764*KG, 1681*KG, 1600*KG, 1521*KG, 1444*KG, 1369*KG, 1296*KG, 1225*KG, 1156*KG, 1089*KG, 1024*KG, - 961*KG, 900*KG, 841*KG, 784*KG, 729*KG, 676*KG, 625*KG, 576*KG, 529*KG, 484*KG, 441*KG, 400*KG, 361*KG, 324*KG, 289*KG, 256*KG, - 225*KG, 196*KG, 169*KG, 144*KG, 121*KG, 100*KG, 81*KG, 64*KG, 49*KG, 36*KG, 25*KG, 16*KG, 9*KG, 4*KG, 1*KG, - 0*KG, 1*KG, 4*KG, 9*KG, 16*KG, 25*KG, 36*KG, 49*KG, 64*KG, 81*KG, 100*KG, 121*KG, 144*KG, 169*KG, 196*KG, 225*KG, - 256*KG, 289*KG, 324*KG, 361*KG, 400*KG, 441*KG, 484*KG, 529*KG, 576*KG, 625*KG, 676*KG, 729*KG, 784*KG, 841*KG, 900*KG, 961*KG, - 1024*KG, 1089*KG, 1156*KG, 1225*KG, 1296*KG, 1369*KG, 1444*KG, 1521*KG, 1600*KG, 1681*KG, 1764*KG, 1849*KG, 1936*KG, 2025*KG, 2116*KG, 2209*KG, - 2304*KG, 2401*KG, 2500*KG, 2601*KG, 2704*KG, 2809*KG, 2916*KG, 3025*KG, 3136*KG, 3249*KG, 3364*KG, 3481*KG, 3600*KG, 3721*KG, 3844*KG, 3969*KG, - 4096*KG, 4225*KG, 4356*KG, 4489*KG, 4624*KG, 4761*KG, 4900*KG, 5041*KG, 5184*KG, 5329*KG, 5476*KG, 5625*KG, 5776*KG, 5929*KG, 6084*KG, 6241*KG, - 6400*KG, 6561*KG, 6724*KG, 6889*KG, 7056*KG, 7225*KG, 7396*KG, 7569*KG, 7744*KG, 7921*KG, 8100*KG, 8281*KG, 8464*KG, 8649*KG, 8836*KG, 9025*KG, - 9216*KG, 9409*KG, 9604*KG, 9801*KG, 10000*KG, 10201*KG, 10404*KG, 10609*KG, 10816*KG, 11025*KG, 11236*KG, 11449*KG, 11664*KG, 11881*KG, 12100*KG, 12321*KG, - 12544*KG, 12769*KG, 12996*KG, 13225*KG, 13456*KG, 13689*KG, 13924*KG, 14161*KG, 14400*KG, 14641*KG, 14884*KG, 15129*KG, 15376*KG, 15625*KG, 15876*KG, 16129*KG, - 16384*KG, 16641*KG, 16900*KG, 17161*KG, 17424*KG, 17689*KG, 17956*KG, 18225*KG, 18496*KG, 18769*KG, 19044*KG, 19321*KG, 19600*KG, 19881*KG, 20164*KG, 20449*KG, - 20736*KG, 21025*KG, 21316*KG, 21609*KG, 21904*KG, 22201*KG, 22500*KG, 22801*KG, 23104*KG, 23409*KG, 23716*KG, 24025*KG, 24336*KG, 24649*KG, 24964*KG, 25281*KG, - 25600*KG, 25921*KG, 26244*KG, 26569*KG, 26896*KG, 27225*KG, 27556*KG, 27889*KG, 28224*KG, 28561*KG, 28900*KG, 29241*KG, 29584*KG, 29929*KG, 30276*KG, 30625*KG, - 30976*KG, 31329*KG, 31684*KG, 32041*KG, 32400*KG, 32761*KG, 33124*KG, 33489*KG, 33856*KG, 34225*KG, 34596*KG, 34969*KG, 35344*KG, 35721*KG, 36100*KG, 36481*KG, - 36864*KG, 37249*KG, 37636*KG, 38025*KG, 38416*KG, 38809*KG, 39204*KG, 39601*KG, 40000*KG, 40401*KG, 40804*KG, 41209*KG, 41616*KG, 42025*KG, 42436*KG, 42849*KG, - 43264*KG, 43681*KG, 44100*KG, 44521*KG, 44944*KG, 45369*KG, 45796*KG, 46225*KG, 46656*KG, 47089*KG, 47524*KG, 47961*KG, 48400*KG, 48841*KG, 49284*KG, 49729*KG, - 50176*KG, 50625*KG, 51076*KG, 51529*KG, 51984*KG, 52441*KG, 52900*KG, 53361*KG, 53824*KG, 54289*KG, 54756*KG, 55225*KG, 55696*KG, 56169*KG, 56644*KG, 57121*KG, - 57600*KG, 58081*KG, 58564*KG, 59049*KG, 59536*KG, 60025*KG, 60516*KG, 61009*KG, 61504*KG, 62001*KG, 62500*KG, 63001*KG, 63504*KG, 64009*KG, 64516*KG, 65025*KG}; - -// Table for fast implementation of squaring for numbers in the interval [-255, 255] multiplied by the perceptual weight for blue. -const unsigned int square_table_percep_blue[511] = { - 65025*KB, 64516*KB, 64009*KB, 63504*KB, 63001*KB, 62500*KB, 62001*KB, 61504*KB, 61009*KB, 60516*KB, 60025*KB, 59536*KB, 59049*KB, 58564*KB, 58081*KB, 57600*KB, - 57121*KB, 56644*KB, 56169*KB, 55696*KB, 55225*KB, 54756*KB, 54289*KB, 53824*KB, 53361*KB, 52900*KB, 52441*KB, 51984*KB, 51529*KB, 51076*KB, 50625*KB, 50176*KB, - 49729*KB, 49284*KB, 48841*KB, 48400*KB, 47961*KB, 47524*KB, 47089*KB, 46656*KB, 46225*KB, 45796*KB, 45369*KB, 44944*KB, 44521*KB, 44100*KB, 43681*KB, 43264*KB, - 42849*KB, 42436*KB, 42025*KB, 41616*KB, 41209*KB, 40804*KB, 40401*KB, 40000*KB, 39601*KB, 39204*KB, 38809*KB, 38416*KB, 38025*KB, 37636*KB, 37249*KB, 36864*KB, - 36481*KB, 36100*KB, 35721*KB, 35344*KB, 34969*KB, 34596*KB, 34225*KB, 33856*KB, 33489*KB, 33124*KB, 32761*KB, 32400*KB, 32041*KB, 31684*KB, 31329*KB, 30976*KB, - 30625*KB, 30276*KB, 29929*KB, 29584*KB, 29241*KB, 28900*KB, 28561*KB, 28224*KB, 27889*KB, 27556*KB, 27225*KB, 26896*KB, 26569*KB, 26244*KB, 25921*KB, 25600*KB, - 25281*KB, 24964*KB, 24649*KB, 24336*KB, 24025*KB, 23716*KB, 23409*KB, 23104*KB, 22801*KB, 22500*KB, 22201*KB, 21904*KB, 21609*KB, 21316*KB, 21025*KB, 20736*KB, - 20449*KB, 20164*KB, 19881*KB, 19600*KB, 19321*KB, 19044*KB, 18769*KB, 18496*KB, 18225*KB, 17956*KB, 17689*KB, 17424*KB, 17161*KB, 16900*KB, 16641*KB, 16384*KB, - 16129*KB, 15876*KB, 15625*KB, 15376*KB, 15129*KB, 14884*KB, 14641*KB, 14400*KB, 14161*KB, 13924*KB, 13689*KB, 13456*KB, 13225*KB, 12996*KB, 12769*KB, 12544*KB, - 12321*KB, 12100*KB, 11881*KB, 11664*KB, 11449*KB, 11236*KB, 11025*KB, 10816*KB, 10609*KB, 10404*KB, 10201*KB, 10000*KB, 9801*KB, 9604*KB, 9409*KB, 9216*KB, - 9025*KB, 8836*KB, 8649*KB, 8464*KB, 8281*KB, 8100*KB, 7921*KB, 7744*KB, 7569*KB, 7396*KB, 7225*KB, 7056*KB, 6889*KB, 6724*KB, 6561*KB, 6400*KB, - 6241*KB, 6084*KB, 5929*KB, 5776*KB, 5625*KB, 5476*KB, 5329*KB, 5184*KB, 5041*KB, 4900*KB, 4761*KB, 4624*KB, 4489*KB, 4356*KB, 4225*KB, 4096*KB, - 3969*KB, 3844*KB, 3721*KB, 3600*KB, 3481*KB, 3364*KB, 3249*KB, 3136*KB, 3025*KB, 2916*KB, 2809*KB, 2704*KB, 2601*KB, 2500*KB, 2401*KB, 2304*KB, - 2209*KB, 2116*KB, 2025*KB, 1936*KB, 1849*KB, 1764*KB, 1681*KB, 1600*KB, 1521*KB, 1444*KB, 1369*KB, 1296*KB, 1225*KB, 1156*KB, 1089*KB, 1024*KB, - 961*KB, 900*KB, 841*KB, 784*KB, 729*KB, 676*KB, 625*KB, 576*KB, 529*KB, 484*KB, 441*KB, 400*KB, 361*KB, 324*KB, 289*KB, 256*KB, - 225*KB, 196*KB, 169*KB, 144*KB, 121*KB, 100*KB, 81*KB, 64*KB, 49*KB, 36*KB, 25*KB, 16*KB, 9*KB, 4*KB, 1*KB, - 0*KB, 1*KB, 4*KB, 9*KB, 16*KB, 25*KB, 36*KB, 49*KB, 64*KB, 81*KB, 100*KB, 121*KB, 144*KB, 169*KB, 196*KB, 225*KB, - 256*KB, 289*KB, 324*KB, 361*KB, 400*KB, 441*KB, 484*KB, 529*KB, 576*KB, 625*KB, 676*KB, 729*KB, 784*KB, 841*KB, 900*KB, 961*KB, - 1024*KB, 1089*KB, 1156*KB, 1225*KB, 1296*KB, 1369*KB, 1444*KB, 1521*KB, 1600*KB, 1681*KB, 1764*KB, 1849*KB, 1936*KB, 2025*KB, 2116*KB, 2209*KB, - 2304*KB, 2401*KB, 2500*KB, 2601*KB, 2704*KB, 2809*KB, 2916*KB, 3025*KB, 3136*KB, 3249*KB, 3364*KB, 3481*KB, 3600*KB, 3721*KB, 3844*KB, 3969*KB, - 4096*KB, 4225*KB, 4356*KB, 4489*KB, 4624*KB, 4761*KB, 4900*KB, 5041*KB, 5184*KB, 5329*KB, 5476*KB, 5625*KB, 5776*KB, 5929*KB, 6084*KB, 6241*KB, - 6400*KB, 6561*KB, 6724*KB, 6889*KB, 7056*KB, 7225*KB, 7396*KB, 7569*KB, 7744*KB, 7921*KB, 8100*KB, 8281*KB, 8464*KB, 8649*KB, 8836*KB, 9025*KB, - 9216*KB, 9409*KB, 9604*KB, 9801*KB, 10000*KB, 10201*KB, 10404*KB, 10609*KB, 10816*KB, 11025*KB, 11236*KB, 11449*KB, 11664*KB, 11881*KB, 12100*KB, 12321*KB, - 12544*KB, 12769*KB, 12996*KB, 13225*KB, 13456*KB, 13689*KB, 13924*KB, 14161*KB, 14400*KB, 14641*KB, 14884*KB, 15129*KB, 15376*KB, 15625*KB, 15876*KB, 16129*KB, - 16384*KB, 16641*KB, 16900*KB, 17161*KB, 17424*KB, 17689*KB, 17956*KB, 18225*KB, 18496*KB, 18769*KB, 19044*KB, 19321*KB, 19600*KB, 19881*KB, 20164*KB, 20449*KB, - 20736*KB, 21025*KB, 21316*KB, 21609*KB, 21904*KB, 22201*KB, 22500*KB, 22801*KB, 23104*KB, 23409*KB, 23716*KB, 24025*KB, 24336*KB, 24649*KB, 24964*KB, 25281*KB, - 25600*KB, 25921*KB, 26244*KB, 26569*KB, 26896*KB, 27225*KB, 27556*KB, 27889*KB, 28224*KB, 28561*KB, 28900*KB, 29241*KB, 29584*KB, 29929*KB, 30276*KB, 30625*KB, - 30976*KB, 31329*KB, 31684*KB, 32041*KB, 32400*KB, 32761*KB, 33124*KB, 33489*KB, 33856*KB, 34225*KB, 34596*KB, 34969*KB, 35344*KB, 35721*KB, 36100*KB, 36481*KB, - 36864*KB, 37249*KB, 37636*KB, 38025*KB, 38416*KB, 38809*KB, 39204*KB, 39601*KB, 40000*KB, 40401*KB, 40804*KB, 41209*KB, 41616*KB, 42025*KB, 42436*KB, 42849*KB, - 43264*KB, 43681*KB, 44100*KB, 44521*KB, 44944*KB, 45369*KB, 45796*KB, 46225*KB, 46656*KB, 47089*KB, 47524*KB, 47961*KB, 48400*KB, 48841*KB, 49284*KB, 49729*KB, - 50176*KB, 50625*KB, 51076*KB, 51529*KB, 51984*KB, 52441*KB, 52900*KB, 53361*KB, 53824*KB, 54289*KB, 54756*KB, 55225*KB, 55696*KB, 56169*KB, 56644*KB, 57121*KB, - 57600*KB, 58081*KB, 58564*KB, 59049*KB, 59536*KB, 60025*KB, 60516*KB, 61009*KB, 61504*KB, 62001*KB, 62500*KB, 63001*KB, 63504*KB, 64009*KB, 64516*KB, 65025*KB}; - -// Find the best table to use for a 2x4 area by testing all. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int tryalltables_3bittable2x4(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) -{ - int min_error = 3*255*255*16; - int q; - int err; - unsigned int pixel_indices_MSB, pixel_indices_LSB; - - for(q=0;q<16;q+=2) // try all the 8 tables. - { - err=compressBlockWithTable2x4(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); - - if(err> 1; - } - } - return min_error; -} - -// Find the best table to use for a 2x4 area by testing all. -// Uses perceptual weighting. -// Uses fixed point implementation where 1000 equals 1.0 -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int tryalltables_3bittable2x4percep1000(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) -{ - unsigned int min_error = MAXERR1000; - int q; - unsigned int err; - unsigned int pixel_indices_MSB, pixel_indices_LSB; - - for(q=0;q<16;q+=2) // try all the 8 tables. - { - - err=compressBlockWithTable2x4percep1000(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); - - if(err> 1; - - } - } - return min_error; -} - -// Find the best table to use for a 2x4 area by testing all. -// Uses perceptual weighting. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int tryalltables_3bittable2x4percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) -{ - float min_error = 3*255*255*16; - int q; - float err; - unsigned int pixel_indices_MSB, pixel_indices_LSB; - - for(q=0;q<16;q+=2) // try all the 8 tables. - { - err=compressBlockWithTable2x4percep(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); - - if(err> 1; - } - } - return (int) min_error; -} - -// Find the best table to use for a 4x2 area by testing all. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int tryalltables_3bittable4x2(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) -{ - int min_error = 3*255*255*16; - int q; - int err; - unsigned int pixel_indices_MSB, pixel_indices_LSB; - - for(q=0;q<16;q+=2) // try all the 8 tables. - { - err=compressBlockWithTable4x2(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); - - if(err> 1; - } - } - return min_error; -} - -// Find the best table to use for a 4x2 area by testing all. -// Uses perceptual weighting. -// Uses fixed point implementation where 1000 equals 1.0 -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int tryalltables_3bittable4x2percep1000(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) -{ - unsigned int min_error = MAXERR1000; - int q; - unsigned int err; - unsigned int pixel_indices_MSB, pixel_indices_LSB; - - for(q=0;q<16;q+=2) // try all the 8 tables. - { - err=compressBlockWithTable4x2percep1000(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); - - if(err> 1; - } - } - return min_error; -} - -// Find the best table to use for a 4x2 area by testing all. -// Uses perceptual weighting. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int tryalltables_3bittable4x2percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) -{ - float min_error = 3*255*255*16; - int q; - float err; - unsigned int pixel_indices_MSB, pixel_indices_LSB; - - for(q=0;q<16;q+=2) // try all the 8 tables. - { - err=compressBlockWithTable4x2percep(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); - - if(err> 1; - } - } - return (int) min_error; -} - -// The below code quantizes a float RGB value to RGB444. -// -// The format often allows a pixel to completely compensate an intensity error of the base -// color. Hence the closest RGB444 point may not be the best, and the code below uses -// this fact to find a better RGB444 color as the base color. -// -// (See the presentation http://www.jacobstrom.com/publications/PACKMAN.ppt for more info.) -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void quantize444ColorCombined(float *avg_col_in, int *enc_color, uint8 *avg_color) -{ - float dr, dg, db; - float kr, kg, kb; - float wR2, wG2, wB2; - uint8 low_color[3]; - uint8 high_color[3]; - float min_error=255*255*8*3; - float lowhightable[8]; - unsigned int best_table=0; - unsigned int best_index=0; - int q; - float kval = (float) (255.0/15.0); - - // These are the values that we want to have: - float red_average, green_average, blue_average; - - int red_4bit_low, green_4bit_low, blue_4bit_low; - int red_4bit_high, green_4bit_high, blue_4bit_high; - - // These are the values that we approximate with: - int red_low, green_low, blue_low; - int red_high, green_high, blue_high; - - red_average = avg_col_in[0]; - green_average = avg_col_in[1]; - blue_average = avg_col_in[2]; - - // Find the 5-bit reconstruction levels red_low, red_high - // so that red_average is in interval [red_low, red_high]. - // (The same with green and blue.) - - red_4bit_low = (int) (red_average/kval); - green_4bit_low = (int) (green_average/kval); - blue_4bit_low = (int) (blue_average/kval); - - red_4bit_high = CLAMP(0, red_4bit_low + 1, 15); - green_4bit_high = CLAMP(0, green_4bit_low + 1, 15); - blue_4bit_high = CLAMP(0, blue_4bit_low + 1, 15); - - red_low = (red_4bit_low << 4) | (red_4bit_low >> 0); - green_low = (green_4bit_low << 4) | (green_4bit_low >> 0); - blue_low = (blue_4bit_low << 4) | (blue_4bit_low >> 0); - - red_high = (red_4bit_high << 4) | (red_4bit_high >> 0); - green_high = (green_4bit_high << 4) | (green_4bit_high >> 0); - blue_high = (blue_4bit_high << 4) | (blue_4bit_high >> 0); - - kr = (float)red_high - (float)red_low; - kg = (float)green_high - (float)green_low; - kb = (float)blue_high - (float)blue_low; - - // Note that dr, dg, and db are all negative. - dr = red_low - red_average; - dg = green_low - green_average; - db = blue_low - blue_average; - - // Use straight (nonperceptive) weights. - wR2 = (float) 1.0; - wG2 = (float) 1.0; - wB2 = (float) 1.0; - - lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); - lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); - lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); - lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); - lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); - lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); - lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); - lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); - - float min_value = lowhightable[0]; - int min_index = 0; - - for(q = 1; q<8; q++) - { - if(lowhightable[q] < min_value) - { - min_value = lowhightable[q]; - min_index = q; - } - } - - float drh = red_high-red_average; - float dgh = green_high-green_average; - float dbh = blue_high-blue_average; - - low_color[0] = red_4bit_low; - low_color[1] = green_4bit_low; - low_color[2] = blue_4bit_low; - - high_color[0] = red_4bit_high; - high_color[1] = green_4bit_high; - high_color[2] = blue_4bit_high; - - switch(min_index) - { - case 0: - // Since the step size is always 17 in RGB444 format (15*17=255), - // kr = kg = kb = 17, which means that case 0 and case 7 will - // always have equal projected error. Choose the one that is - // closer to the desired color. - if(dr*dr + dg*dg + db*db > 3*8*8) - { - enc_color[0] = high_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = high_color[2]; - } - else - { - enc_color[0] = low_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = low_color[2]; - } - break; - case 1: - enc_color[0] = high_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = low_color[2]; - break; - case 2: - enc_color[0] = low_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = low_color[2]; - break; - case 3: - enc_color[0] = low_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = high_color[2]; - break; - case 4: - enc_color[0] = high_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = low_color[2]; - break; - case 5: - enc_color[0] = high_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = high_color[2]; - break; - case 6: - enc_color[0] = low_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = high_color[2]; - break; - case 7: - if(dr*dr + dg*dg + db*db > 3*8*8) - { - enc_color[0] = high_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = high_color[2]; - } - else - { - enc_color[0] = low_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = low_color[2]; - } - break; - } - // Expand 5-bit encoded color to 8-bit color - avg_color[0] = (enc_color[0] << 3) | (enc_color[0] >> 2); - avg_color[1] = (enc_color[1] << 3) | (enc_color[1] >> 2); - avg_color[2] = (enc_color[2] << 3) | (enc_color[2] >> 2); -} - -// The below code quantizes a float RGB value to RGB555. -// -// The format often allows a pixel to completely compensate an intensity error of the base -// color. Hence the closest RGB555 point may not be the best, and the code below uses -// this fact to find a better RGB555 color as the base color. -// -// (See the presentation http://www.jacobstrom.com/publications/PACKMAN.ppt for more info.) -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void quantize555ColorCombined(float *avg_col_in, int *enc_color, uint8 *avg_color) -{ - float dr, dg, db; - float kr, kg, kb; - float wR2, wG2, wB2; - uint8 low_color[3]; - uint8 high_color[3]; - float min_error=255*255*8*3; - float lowhightable[8]; - unsigned int best_table=0; - unsigned int best_index=0; - int q; - float kval = (float) (255.0/31.0); - - // These are the values that we want to have: - float red_average, green_average, blue_average; - - int red_5bit_low, green_5bit_low, blue_5bit_low; - int red_5bit_high, green_5bit_high, blue_5bit_high; - - // These are the values that we approximate with: - int red_low, green_low, blue_low; - int red_high, green_high, blue_high; - - red_average = avg_col_in[0]; - green_average = avg_col_in[1]; - blue_average = avg_col_in[2]; - - // Find the 5-bit reconstruction levels red_low, red_high - // so that red_average is in interval [red_low, red_high]. - // (The same with green and blue.) - - red_5bit_low = (int) (red_average/kval); - green_5bit_low = (int) (green_average/kval); - blue_5bit_low = (int) (blue_average/kval); - - red_5bit_high = CLAMP(0, red_5bit_low + 1, 31); - green_5bit_high = CLAMP(0, green_5bit_low + 1, 31); - blue_5bit_high = CLAMP(0, blue_5bit_low + 1, 31); - - red_low = (red_5bit_low << 3) | (red_5bit_low >> 2); - green_low = (green_5bit_low << 3) | (green_5bit_low >> 2); - blue_low = (blue_5bit_low << 3) | (blue_5bit_low >> 2); - - red_high = (red_5bit_high << 3) | (red_5bit_high >> 2); - green_high = (green_5bit_high << 3) | (green_5bit_high >> 2); - blue_high = (blue_5bit_high << 3) | (blue_5bit_high >> 2); - - kr = (float)red_high - (float)red_low; - kg = (float)green_high - (float)green_low; - kb = (float)blue_high - (float)blue_low; - - // Note that dr, dg, and db are all negative. - dr = red_low - red_average; - dg = green_low - green_average; - db = blue_low - blue_average; - - // Use straight (nonperceptive) weights. - wR2 = (float) 1.0; - wG2 = (float) 1.0; - wB2 = (float) 1.0; - - lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); - lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); - lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); - lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); - lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); - lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); - lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); - lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); - - float min_value = lowhightable[0]; - int min_index = 0; - - for(q = 1; q<8; q++) - { - if(lowhightable[q] < min_value) - { - min_value = lowhightable[q]; - min_index = q; - } - } - - float drh = red_high-red_average; - float dgh = green_high-green_average; - float dbh = blue_high-blue_average; - - low_color[0] = red_5bit_low; - low_color[1] = green_5bit_low; - low_color[2] = blue_5bit_low; - - high_color[0] = red_5bit_high; - high_color[1] = green_5bit_high; - high_color[2] = blue_5bit_high; - - switch(min_index) - { - case 0: - enc_color[0] = low_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = low_color[2]; - break; - case 1: - enc_color[0] = high_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = low_color[2]; - break; - case 2: - enc_color[0] = low_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = low_color[2]; - break; - case 3: - enc_color[0] = low_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = high_color[2]; - break; - case 4: - enc_color[0] = high_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = low_color[2]; - break; - case 5: - enc_color[0] = high_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = high_color[2]; - break; - case 6: - enc_color[0] = low_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = high_color[2]; - break; - case 7: - enc_color[0] = high_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = high_color[2]; - break; - } - - // Expand 5-bit encoded color to 8-bit color - avg_color[0] = (enc_color[0] << 3) | (enc_color[0] >> 2); - avg_color[1] = (enc_color[1] << 3) | (enc_color[1] >> 2); - avg_color[2] = (enc_color[2] << 3) | (enc_color[2] >> 2); - -} - -// The below code quantizes a float RGB value to RGB444. -// -// The format often allows a pixel to completely compensate an intensity error of the base -// color. Hence the closest RGB444 point may not be the best, and the code below uses -// this fact to find a better RGB444 color as the base color. -// -// (See the presentation http://www.jacobstrom.com/publications/PACKMAN.ppt for more info.) -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void quantize444ColorCombinedPerceptual(float *avg_col_in, int *enc_color, uint8 *avg_color) -{ - float dr, dg, db; - float kr, kg, kb; - float wR2, wG2, wB2; - uint8 low_color[3]; - uint8 high_color[3]; - float min_error=255*255*8*3; - float lowhightable[8]; - unsigned int best_table=0; - unsigned int best_index=0; - int q; - float kval = (float) (255.0/15.0); - - // These are the values that we want to have: - float red_average, green_average, blue_average; - - int red_4bit_low, green_4bit_low, blue_4bit_low; - int red_4bit_high, green_4bit_high, blue_4bit_high; - - // These are the values that we approximate with: - int red_low, green_low, blue_low; - int red_high, green_high, blue_high; - - red_average = avg_col_in[0]; - green_average = avg_col_in[1]; - blue_average = avg_col_in[2]; - - // Find the 5-bit reconstruction levels red_low, red_high - // so that red_average is in interval [red_low, red_high]. - // (The same with green and blue.) - - red_4bit_low = (int) (red_average/kval); - green_4bit_low = (int) (green_average/kval); - blue_4bit_low = (int) (blue_average/kval); - - red_4bit_high = CLAMP(0, red_4bit_low + 1, 15); - green_4bit_high = CLAMP(0, green_4bit_low + 1, 15); - blue_4bit_high = CLAMP(0, blue_4bit_low + 1, 15); - - red_low = (red_4bit_low << 4) | (red_4bit_low >> 0); - green_low = (green_4bit_low << 4) | (green_4bit_low >> 0); - blue_low = (blue_4bit_low << 4) | (blue_4bit_low >> 0); - - red_high = (red_4bit_high << 4) | (red_4bit_high >> 0); - green_high = (green_4bit_high << 4) | (green_4bit_high >> 0); - blue_high = (blue_4bit_high << 4) | (blue_4bit_high >> 0); - - low_color[0] = red_4bit_low; - low_color[1] = green_4bit_low; - low_color[2] = blue_4bit_low; - - high_color[0] = red_4bit_high; - high_color[1] = green_4bit_high; - high_color[2] = blue_4bit_high; - - kr = (float)red_high - (float)red_low; - kg = (float)green_high - (float)green_low; - kb = (float)blue_high- (float)blue_low; - - // Note that dr, dg, and db are all negative. - dr = red_low - red_average; - dg = green_low - green_average; - db = blue_low - blue_average; - - // Perceptual weights to use - wR2 = (float) PERCEPTUAL_WEIGHT_R_SQUARED; - wG2 = (float) PERCEPTUAL_WEIGHT_G_SQUARED; - wB2 = (float) PERCEPTUAL_WEIGHT_B_SQUARED; - - lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); - lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); - lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); - lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); - lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); - lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); - lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); - lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); - - float min_value = lowhightable[0]; - int min_index = 0; - - for(q = 1; q<8; q++) - { - if(lowhightable[q] < min_value) - { - min_value = lowhightable[q]; - min_index = q; - } - } - - float drh = red_high-red_average; - float dgh = green_high-green_average; - float dbh = blue_high-blue_average; - - switch(min_index) - { - case 0: - enc_color[0] = low_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = low_color[2]; - break; - case 1: - enc_color[0] = high_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = low_color[2]; - break; - case 2: - enc_color[0] = low_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = low_color[2]; - break; - case 3: - enc_color[0] = low_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = high_color[2]; - break; - case 4: - enc_color[0] = high_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = low_color[2]; - break; - case 5: - enc_color[0] = high_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = high_color[2]; - break; - case 6: - enc_color[0] = low_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = high_color[2]; - break; - case 7: - enc_color[0] = high_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = high_color[2]; - break; - } - - // Expand encoded color to eight bits - avg_color[0] = (enc_color[0] << 4) | enc_color[0]; - avg_color[1] = (enc_color[1] << 4) | enc_color[1]; - avg_color[2] = (enc_color[2] << 4) | enc_color[2]; -} - -// The below code quantizes a float RGB value to RGB555. -// -// The format often allows a pixel to completely compensate an intensity error of the base -// color. Hence the closest RGB555 point may not be the best, and the code below uses -// this fact to find a better RGB555 color as the base color. -// -// (See the presentation http://www.jacobstrom.com/publications/PACKMAN.ppt for more info.) -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void quantize555ColorCombinedPerceptual(float *avg_col_in, int *enc_color, uint8 *avg_color) -{ - float dr, dg, db; - float kr, kg, kb; - float wR2, wG2, wB2; - uint8 low_color[3]; - uint8 high_color[3]; - float min_error=255*255*8*3; - float lowhightable[8]; - unsigned int best_table=0; - unsigned int best_index=0; - int q; - float kval = (float) (255.0/31.0); - - // These are the values that we want to have: - float red_average, green_average, blue_average; - - int red_5bit_low, green_5bit_low, blue_5bit_low; - int red_5bit_high, green_5bit_high, blue_5bit_high; - - // These are the values that we approximate with: - int red_low, green_low, blue_low; - int red_high, green_high, blue_high; - - red_average = avg_col_in[0]; - green_average = avg_col_in[1]; - blue_average = avg_col_in[2]; - - // Find the 5-bit reconstruction levels red_low, red_high - // so that red_average is in interval [red_low, red_high]. - // (The same with green and blue.) - - red_5bit_low = (int) (red_average/kval); - green_5bit_low = (int) (green_average/kval); - blue_5bit_low = (int) (blue_average/kval); - - red_5bit_high = CLAMP(0, red_5bit_low + 1, 31); - green_5bit_high = CLAMP(0, green_5bit_low + 1, 31); - blue_5bit_high = CLAMP(0, blue_5bit_low + 1, 31); - - red_low = (red_5bit_low << 3) | (red_5bit_low >> 2); - green_low = (green_5bit_low << 3) | (green_5bit_low >> 2); - blue_low = (blue_5bit_low << 3) | (blue_5bit_low >> 2); - - red_high = (red_5bit_high << 3) | (red_5bit_high >> 2); - green_high = (green_5bit_high << 3) | (green_5bit_high >> 2); - blue_high = (blue_5bit_high << 3) | (blue_5bit_high >> 2); - - low_color[0] = red_5bit_low; - low_color[1] = green_5bit_low; - low_color[2] = blue_5bit_low; - - high_color[0] = red_5bit_high; - high_color[1] = green_5bit_high; - high_color[2] = blue_5bit_high; - - kr = (float)red_high - (float)red_low; - kg = (float)green_high - (float)green_low; - kb = (float)blue_high - (float)blue_low; - - // Note that dr, dg, and db are all negative. - dr = red_low - red_average; - dg = green_low - green_average; - db = blue_low - blue_average; - - // Perceptual weights to use - wR2 = (float) PERCEPTUAL_WEIGHT_R_SQUARED; - wG2 = (float) PERCEPTUAL_WEIGHT_G_SQUARED; - wB2 = (float) PERCEPTUAL_WEIGHT_B_SQUARED; - - lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); - lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); - lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); - lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); - lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); - lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); - lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); - lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); - - float min_value = lowhightable[0]; - int min_index = 0; - - for(q = 1; q<8; q++) - { - if(lowhightable[q] < min_value) - { - min_value = lowhightable[q]; - min_index = q; - } - } - - float drh = red_high-red_average; - float dgh = green_high-green_average; - float dbh = blue_high-blue_average; - - switch(min_index) - { - case 0: - enc_color[0] = low_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = low_color[2]; - break; - case 1: - enc_color[0] = high_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = low_color[2]; - break; - case 2: - enc_color[0] = low_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = low_color[2]; - break; - case 3: - enc_color[0] = low_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = high_color[2]; - break; - case 4: - enc_color[0] = high_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = low_color[2]; - break; - case 5: - enc_color[0] = high_color[0]; - enc_color[1] = low_color[1]; - enc_color[2] = high_color[2]; - break; - case 6: - enc_color[0] = low_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = high_color[2]; - break; - case 7: - enc_color[0] = high_color[0]; - enc_color[1] = high_color[1]; - enc_color[2] = high_color[2]; - break; - } - - // Expand 5-bit encoded color to 8-bit color - avg_color[0] = (enc_color[0] << 3) | (enc_color[0] >> 2); - avg_color[1] = (enc_color[1] << 3) | (enc_color[1] >> 2); - avg_color[2] = (enc_color[2] << 3) | (enc_color[2] >> 2); -} - -// Compresses the block using only the individual mode in ETC1/ETC2 using the average color as the base color. -// Uses a perceptual error metric. -// Uses fixed point arithmetics where 1000 equals 1.0 -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockOnlyIndividualAveragePerceptual1000(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, int *best_enc_color1, int*best_enc_color2, int &best_flip, unsigned int &best_err_upper, unsigned int &best_err_lower, unsigned int &best_err_left, unsigned int &best_err_right, int *best_color_upper, int *best_color_lower, int *best_color_left, int *best_color_right) -{ - unsigned int compressed1_norm, compressed2_norm; - unsigned int compressed1_flip, compressed2_flip; - uint8 avg_color_quant1[3], avg_color_quant2[3]; - - float avg_color_float1[3],avg_color_float2[3]; - int enc_color1[3], enc_color2[3]; - unsigned int best_table_indices1=0, best_table_indices2=0; - unsigned int best_table1=0, best_table2=0; - int diffbit; - - unsigned int norm_err=0; - unsigned int flip_err=0; - unsigned int best_err; - - // First try normal blocks 2x4: - - computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); - - enc_color1[0] = int( JAS_ROUND(15.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(15.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(15.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(15.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(15.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(15.0*avg_color_float2[2]/255.0) ); - - diffbit = 0; - - avg_color_quant1[0] = enc_color1[0] << 4 | (enc_color1[0] ); - avg_color_quant1[1] = enc_color1[1] << 4 | (enc_color1[1] ); - avg_color_quant1[2] = enc_color1[2] << 4 | (enc_color1[2] ); - avg_color_quant2[0] = enc_color2[0] << 4 | (enc_color2[0] ); - avg_color_quant2[1] = enc_color2[1] << 4 | (enc_color2[1] ); - avg_color_quant2[2] = enc_color2[2] << 4 | (enc_color2[2] ); - - // Pack bits into the first word. - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - best_enc_color1[0] = enc_color1[0]; - best_enc_color1[1] = enc_color1[1]; - best_enc_color1[2] = enc_color1[2]; - best_enc_color2[0] = enc_color2[0]; - best_enc_color2[1] = enc_color2[1]; - best_enc_color2[2] = enc_color2[2]; - - best_color_left[0] = enc_color1[0]; - best_color_left[1] = enc_color1[1]; - best_color_left[2] = enc_color1[2]; - best_color_right[0] = enc_color2[0]; - best_color_right[1] = enc_color2[1]; - best_color_right[2] = enc_color2[2]; - - norm_err = 0; - - // left part of block - best_err_left = tryalltables_3bittable2x4percep1000(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - norm_err = best_err_left; - - // right part of block - best_err_right = tryalltables_3bittable2x4percep1000(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - norm_err += best_err_right; - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - - // Now try flipped blocks 4x2: - - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - enc_color1[0] = int( JAS_ROUND(15.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(15.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(15.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(15.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(15.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(15.0*avg_color_float2[2]/255.0) ); - - best_color_upper[0] = enc_color1[0]; - best_color_upper[1] = enc_color1[1]; - best_color_upper[2] = enc_color1[2]; - best_color_lower[0] = enc_color2[0]; - best_color_lower[1] = enc_color2[1]; - best_color_lower[2] = enc_color2[2]; - - diffbit = 0; - - avg_color_quant1[0] = enc_color1[0] << 4 | (enc_color1[0] ); - avg_color_quant1[1] = enc_color1[1] << 4 | (enc_color1[1] ); - avg_color_quant1[2] = enc_color1[2] << 4 | (enc_color1[2] ); - avg_color_quant2[0] = enc_color2[0] << 4 | (enc_color2[0] ); - avg_color_quant2[1] = enc_color2[1] << 4 | (enc_color2[1] ); - avg_color_quant2[2] = enc_color2[2] << 4 | (enc_color2[2] ); - - // Pack bits into the first word. - - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 49); - PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); - - // upper part of block - best_err_upper = tryalltables_3bittable4x2percep1000(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - flip_err = best_err_upper; - // lower part of block - best_err_lower = tryalltables_3bittable4x2percep1000(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - flip_err += best_err_lower; - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - - // Now lets see which is the best table to use. Only 8 tables are possible. - - if(norm_err <= flip_err) - { - compressed1 = compressed1_norm | 0; - compressed2 = compressed2_norm; - best_err = norm_err; - best_flip = 0; - } - else - { - compressed1 = compressed1_flip | 1; - compressed2 = compressed2_flip; - best_err = flip_err; - best_enc_color1[0] = enc_color1[0]; - best_enc_color1[1] = enc_color1[1]; - best_enc_color1[2] = enc_color1[2]; - best_enc_color2[0] = enc_color2[0]; - best_enc_color2[1] = enc_color2[1]; - best_enc_color2[2] = enc_color2[2]; - best_flip = 1; - } - return best_err; -} - -// Compresses the block using only the individual mode in ETC1/ETC2 using the average color as the base color. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int compressBlockOnlyIndividualAverage(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, int *best_enc_color1, int*best_enc_color2, int &best_flip, unsigned int &best_err_upper, unsigned int &best_err_lower, unsigned int &best_err_left, unsigned int &best_err_right, int *best_color_upper, int *best_color_lower, int *best_color_left, int *best_color_right) -{ - unsigned int compressed1_norm, compressed2_norm; - unsigned int compressed1_flip, compressed2_flip; - uint8 avg_color_quant1[3], avg_color_quant2[3]; - - float avg_color_float1[3],avg_color_float2[3]; - int enc_color1[3], enc_color2[3]; - int min_error=255*255*8*3; - unsigned int best_table_indices1=0, best_table_indices2=0; - unsigned int best_table1=0, best_table2=0; - int diffbit; - - int norm_err=0; - int flip_err=0; - int best_err; - - // First try normal blocks 2x4: - - computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); - - enc_color1[0] = int( JAS_ROUND(15.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(15.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(15.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(15.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(15.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(15.0*avg_color_float2[2]/255.0) ); - - diffbit = 0; - - avg_color_quant1[0] = enc_color1[0] << 4 | (enc_color1[0] ); - avg_color_quant1[1] = enc_color1[1] << 4 | (enc_color1[1] ); - avg_color_quant1[2] = enc_color1[2] << 4 | (enc_color1[2] ); - avg_color_quant2[0] = enc_color2[0] << 4 | (enc_color2[0] ); - avg_color_quant2[1] = enc_color2[1] << 4 | (enc_color2[1] ); - avg_color_quant2[2] = enc_color2[2] << 4 | (enc_color2[2] ); - - // Pack bits into the first word. - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - best_enc_color1[0] = enc_color1[0]; - best_enc_color1[1] = enc_color1[1]; - best_enc_color1[2] = enc_color1[2]; - best_enc_color2[0] = enc_color2[0]; - best_enc_color2[1] = enc_color2[1]; - best_enc_color2[2] = enc_color2[2]; - best_color_left[0] = enc_color1[0]; - best_color_left[1] = enc_color1[1]; - best_color_left[2] = enc_color1[2]; - best_color_right[0] = enc_color2[0]; - best_color_right[1] = enc_color2[1]; - best_color_right[2] = enc_color2[2]; - - norm_err = 0; - - // left part of block - best_err_left = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - norm_err = best_err_left; - - // right part of block - best_err_right = tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - norm_err += best_err_right; - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - - - // Now try flipped blocks 4x2: - - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - enc_color1[0] = int( JAS_ROUND(15.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(15.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(15.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(15.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(15.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(15.0*avg_color_float2[2]/255.0) ); - - best_color_upper[0] = enc_color1[0]; - best_color_upper[1] = enc_color1[1]; - best_color_upper[2] = enc_color1[2]; - best_color_lower[0] = enc_color2[0]; - best_color_lower[1] = enc_color2[1]; - best_color_lower[2] = enc_color2[2]; - - diffbit = 0; - - avg_color_quant1[0] = enc_color1[0] << 4 | (enc_color1[0] ); - avg_color_quant1[1] = enc_color1[1] << 4 | (enc_color1[1] ); - avg_color_quant1[2] = enc_color1[2] << 4 | (enc_color1[2] ); - avg_color_quant2[0] = enc_color2[0] << 4 | (enc_color2[0] ); - avg_color_quant2[1] = enc_color2[1] << 4 | (enc_color2[1] ); - avg_color_quant2[2] = enc_color2[2] << 4 | (enc_color2[2] ); - - // Pack bits into the first word. - - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 49); - PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); - - // upper part of block - best_err_upper = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - flip_err = best_err_upper; - // lower part of block - best_err_lower = tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - flip_err += best_err_lower; - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - - // Now lets see which is the best table to use. Only 8 tables are possible. - - if(norm_err <= flip_err) - { - compressed1 = compressed1_norm | 0; - compressed2 = compressed2_norm; - best_err = norm_err; - best_flip = 0; - } - else - { - compressed1 = compressed1_flip | 1; - compressed2 = compressed2_flip; - best_err = flip_err; - best_enc_color1[0] = enc_color1[0]; - best_enc_color1[1] = enc_color1[1]; - best_enc_color1[2] = enc_color1[2]; - best_enc_color2[0] = enc_color2[0]; - best_enc_color2[1] = enc_color2[1]; - best_enc_color2[2] = enc_color2[2]; - best_flip = 1; - } - return best_err; -} - -// Compresses the block using either the individual or differential mode in ETC1/ETC2 -// Uses the average color as the base color in each half-block. -// Tries both flipped and unflipped. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockDiffFlipAverage(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - unsigned int compressed1_norm, compressed2_norm; - unsigned int compressed1_flip, compressed2_flip; - uint8 avg_color_quant1[3], avg_color_quant2[3]; - - float avg_color_float1[3],avg_color_float2[3]; - int enc_color1[3], enc_color2[3], diff[3]; - int min_error=255*255*8*3; - unsigned int best_table_indices1=0, best_table_indices2=0; - unsigned int best_table1=0, best_table2=0; - int diffbit; - - int norm_err=0; - int flip_err=0; - - // First try normal blocks 2x4: - computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - float eps; - - enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) - { - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); - PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); - PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - norm_err = 0; - - // left part of block - norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - } - else - { - diffbit = 0; - // The difference is bigger than what fits in 555 plus delta-333, so we will have - // to deal with 444 444. - - eps = (float) 0.0001; - - enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); - enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); - enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); - enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); - enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); - enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); - avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; - avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; - avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; - avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; - avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; - avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; - - // Pack bits into the first word. - - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // left part of block - norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - } - - // Now try flipped blocks 4x2: - - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) - { - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); - PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); - PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // upper part of block - flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - else - { - diffbit = 0; - // The difference is bigger than what fits in 555 plus delta-333, so we will have - // to deal with 444 444. - eps = (float) 0.0001; - - enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); - enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); - enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); - enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); - enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); - enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); - - avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; - avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; - avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; - avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; - avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; - avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; - - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - - // Pack bits into the first word. - - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // upper part of block - flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - - // Now lets see which is the best table to use. Only 8 tables are possible. - - if(norm_err <= flip_err) - { - compressed1 = compressed1_norm | 0; - compressed2 = compressed2_norm; - } - else - { - compressed1 = compressed1_flip | 1; - compressed2 = compressed2_flip; - } -} - -// Compresses the block using only the differential mode in ETC1/ETC2 -// Uses the average color as the base color in each half-block. -// If average colors are too different, use the average color of the entire block in both half-blocks. -// Tries both flipped and unflipped. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int compressBlockOnlyDiffFlipAverage(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, int *best_enc_color1, int*best_enc_color2, int &best_flip) -{ - unsigned int compressed1_norm, compressed2_norm; - unsigned int compressed1_flip, compressed2_flip; - uint8 avg_color_quant1[3], avg_color_quant2[3]; - - float avg_color_float1[3],avg_color_float2[3]; - int enc_color1[3], enc_color2[3], diff[3]; - int min_error=255*255*8*3; - unsigned int best_table_indices1=0, best_table_indices2=0; - unsigned int best_table1=0, best_table2=0; - int diffbit; - - int norm_err=0; - int flip_err=0; - int best_err; - - // First try normal blocks 2x4: - - computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( !((diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3)) ) - { - // The colors are too different. Use the same color in both blocks. - enc_color1[0] = int( JAS_ROUND(31.0*((avg_color_float1[0]+avg_color_float2[0])/2.0)/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*((avg_color_float1[1]+avg_color_float2[1])/2.0)/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*((avg_color_float1[2]+avg_color_float2[2])/2.0)/255.0) ); - enc_color2[0] = enc_color1[0]; - enc_color2[1] = enc_color1[1]; - enc_color2[2] = enc_color1[2]; - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - } - - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); - PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); - PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - best_enc_color1[0] = enc_color1[0]; - best_enc_color1[1] = enc_color1[1]; - best_enc_color1[2] = enc_color1[2]; - best_enc_color2[0] = enc_color2[0]; - best_enc_color2[1] = enc_color2[1]; - best_enc_color2[2] = enc_color2[2]; - - norm_err = 0; - - // left part of block - norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - - // Now try flipped blocks 4x2: - - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( !((diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3)) ) - { - // The colors are too different. Use the same color in both blocks. - enc_color1[0] = int( JAS_ROUND(31.0*((avg_color_float1[0]+avg_color_float2[0])/2.0)/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*((avg_color_float1[1]+avg_color_float2[1])/2.0)/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*((avg_color_float1[2]+avg_color_float2[2])/2.0)/255.0) ); - enc_color2[0] = enc_color1[0]; - enc_color2[1] = enc_color1[1]; - enc_color2[2] = enc_color1[2]; - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - } - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); - PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); - PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); - - // upper part of block - flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - - // Now lets see which is the best table to use. Only 8 tables are possible. - - if(norm_err <= flip_err) - { - compressed1 = compressed1_norm | 0; - compressed2 = compressed2_norm; - best_err = norm_err; - best_flip = 0; - } - else - { - compressed1 = compressed1_flip | 1; - compressed2 = compressed2_flip; - best_err = flip_err; - best_enc_color1[0] = enc_color1[0]; - best_enc_color1[1] = enc_color1[1]; - best_enc_color1[2] = enc_color1[2]; - best_enc_color2[0] = enc_color2[0]; - best_enc_color2[1] = enc_color2[1]; - best_enc_color2[2] = enc_color2[2]; - best_flip = 1; - } - return best_err; -} - -// Compresses the block using only the differential mode in ETC1/ETC2 -// Uses the average color as the base color in each half-block. -// If average colors are too different, use the average color of the entire block in both half-blocks. -// Tries both flipped and unflipped. -// Uses fixed point arithmetics where 1000 represents 1.0. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockOnlyDiffFlipAveragePerceptual1000(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - unsigned int compressed1_norm, compressed2_norm; - unsigned int compressed1_flip, compressed2_flip; - uint8 avg_color_quant1[3], avg_color_quant2[3]; - - float avg_color_float1[3],avg_color_float2[3]; - int enc_color1[3], enc_color2[3], diff[3]; - unsigned int min_error=MAXERR1000; - unsigned int best_table_indices1=0, best_table_indices2=0; - unsigned int best_table1=0, best_table2=0; - int diffbit; - - int norm_err=0; - int flip_err=0; - - // First try normal blocks 2x4: - - computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( !((diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3)) ) - { - enc_color1[0] = (enc_color1[0] + enc_color2[0]) >> 1; - enc_color1[1] = (enc_color1[1] + enc_color2[1]) >> 1; - enc_color1[2] = (enc_color1[2] + enc_color2[2]) >> 1; - - enc_color2[0] = enc_color1[0]; - enc_color2[1] = enc_color1[1]; - enc_color2[2] = enc_color1[2]; - - } - - { - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); - PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); - PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - norm_err = 0; - - // left part of block - norm_err = tryalltables_3bittable2x4percep1000(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4percep1000(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - - } - // Now try flipped blocks 4x2: - - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( !((diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3)) ) - { - enc_color1[0] = (enc_color1[0] + enc_color2[0]) >> 1; - enc_color1[1] = (enc_color1[1] + enc_color2[1]) >> 1; - enc_color1[2] = (enc_color1[2] + enc_color2[2]) >> 1; - - enc_color2[0] = enc_color1[0]; - enc_color2[1] = enc_color1[1]; - enc_color2[2] = enc_color1[2]; - } - - { - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); - PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); - PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // upper part of block - flip_err = tryalltables_3bittable4x2percep1000(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2percep1000(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - unsigned int best_err; - - if(norm_err <= flip_err) - { - compressed1 = compressed1_norm | 0; - compressed2 = compressed2_norm; - best_err = norm_err; - } - else - { - compressed1 = compressed1_flip | 1; - compressed2 = compressed2_flip; - best_err = flip_err; - } - return best_err; -} - -// Compresses the block using both the individual and the differential mode in ETC1/ETC2 -// Uses the average color as the base color in each half-block. -// Uses a perceptual error metric. -// Tries both flipped and unflipped. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double compressBlockDiffFlipAveragePerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - unsigned int compressed1_norm, compressed2_norm; - unsigned int compressed1_flip, compressed2_flip; - uint8 avg_color_quant1[3], avg_color_quant2[3]; - - float avg_color_float1[3],avg_color_float2[3]; - int enc_color1[3], enc_color2[3], diff[3]; - int min_error=255*255*8*3; - unsigned int best_table_indices1=0, best_table_indices2=0; - unsigned int best_table1=0, best_table2=0; - int diffbit; - - int norm_err=0; - int flip_err=0; - - // First try normal blocks 2x4: - - computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - float eps; - - enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) - { - diffbit = 1; - - // The difference to be coded: - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); - PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); - PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - norm_err = 0; - - // left part of block - norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - } - else - { - diffbit = 0; - // The difference is bigger than what fits in 555 plus delta-333, so we will have - // to deal with 444 444. - - eps = (float) 0.0001; - - enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); - enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); - enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); - enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); - enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); - enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); - avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; - avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; - avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; - avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; - avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; - avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; - - // Pack bits into the first word. - - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // left part of block - norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - } - - // Now try flipped blocks 4x2: - - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); - enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); - enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); - enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); - enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); - enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) - { - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); - PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); - PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // upper part of block - flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - else - { - diffbit = 0; - // The difference is bigger than what fits in 555 plus delta-333, so we will have - // to deal with 444 444. - eps = (float) 0.0001; - - enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); - enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); - enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); - enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); - enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); - enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); - - avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; - avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; - avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; - avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; - avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; - avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; - - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - // Pack bits into the first word. - - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // upper part of block - flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - - // Now lets see which is the best table to use. Only 8 tables are possible. - - double best_err; - - if(norm_err <= flip_err) - { - compressed1 = compressed1_norm | 0; - compressed2 = compressed2_norm; - best_err = norm_err; - } - else - { - compressed1 = compressed1_flip | 1; - compressed2 = compressed2_flip; - best_err = flip_err; - } - return best_err; -} - -// This is our structure for matrix data -struct dMatrix -{ - int width; // The number of coloumns in the matrix - int height; // The number of rows in the matrix - double *data; // The matrix data in row order -}; - -// Multiplies two matrices -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -dMatrix *multiplyMatrices( dMatrix *Amat, dMatrix *Bmat) -{ - int xx,yy, q; - dMatrix *resmatrix; - - if(Amat->width != Bmat->height) - { - printf("Cannot multiply matrices -- dimensions do not agree.\n"); - exit(1); - } - - // Allocate space for result - resmatrix = (dMatrix*) malloc(sizeof(dMatrix)); - resmatrix->width = Bmat->width; - resmatrix->height = Amat->height; - resmatrix->data = (double*) malloc(sizeof(double)*(resmatrix->width)*(resmatrix->height)); - - for(yy = 0; yyheight; yy++) - for(xx = 0; xxwidth; xx++) - for(q=0, resmatrix->data[yy*resmatrix->width+xx] = 0.0; qwidth; q++) - resmatrix->data[yy*resmatrix->width+xx] += Amat->data[yy*Amat->width + q] * Bmat->data[q*Bmat->width+xx]; - - return(resmatrix); - -} - -// Transposes a matrix -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void transposeMatrix( dMatrix *mat) -{ - int xx, yy, zz; - double *temp; - int newwidth, newheight; - - temp = (double*) malloc (sizeof(double)*(mat->width)*(mat->height)); - - for(zz = 0; zz<((mat->width)*(mat->height)); zz++) - temp[zz] = mat->data[zz]; - - newwidth = mat->height; - newheight= mat->width; - - for(yy = 0; yydata[yy*newwidth+xx] = temp[xx*(mat->width)+yy]; - - mat->height = newheight; - mat->width = newwidth; - free(temp); -} - -// In the planar mode in ETC2, the block can be partitioned as follows: -// -// O A A A H -// B D1 D3 C3 -// B D2 C2 D5 -// B C1 D4 D6 -// V -// Here A-pixels, B-pixels and C-pixels only depend on two values. For instance, B-pixels only depend on O and V. -// This can be used to quickly rule out combinations of colors. -// Here we calculate the minimum error for the block if we know the red component for O and V. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcBBBred(uint8 *block, int colorO, int colorV) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error = 0; - - // Now first column: B B B - /* unroll loop for( yy=0; (yy<4) && (error <= best_error_sofar); yy++)*/ - { - error = error + square_table[(block[4*4 + 0] - clamp_table[ ((((colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*2 + 0] - clamp_table[ (((((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*3 + 0] - clamp_table[ (((3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - - return error; -} - -// Calculating the minimum error for the block if we know the red component for H and V. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcCCCred(uint8 *block, int colorH, int colorV) -{ - colorH = (colorH << 2) | (colorH >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error=0; - - error = error + square_table[(block[4*4*3 + 4 + 0] - clamp_table[ (((colorH + 3*colorV)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*2 + 4*2 + 0] - clamp_table[ (((2*colorH + 2*colorV)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4 + 4*3 + 0] - clamp_table[ (((3*colorH + colorV)+2)>>2) + 255])+255]; - - return error; -} - -// Calculating the minimum error for the block if we know the red component for O and H. -// Uses perceptual error metric. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcLowestPossibleRedOHperceptual(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorH = (colorH << 2) | (colorH >> 4); - - unsigned int error; - - error = square_table_percep_red[(block[0] - colorO) + 255]; - error = error + square_table_percep_red[(block[4] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table_percep_red[(block[4*2] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_red[(block[4*3] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcLowestPossibleRedOH(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorH = (colorH << 2) | (colorH >> 4); - - unsigned int error; - - error = square_table[(block[0] - colorO) + 255]; - error = error + square_table[(block[4] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table[(block[4*2] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*3] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H and V. -// Uses perceptual error metric. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcErrorPlanarOnlyRedPerceptual(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorH = (colorH << 2) | (colorH >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error; - - // The block can be partitioned into: O A A A - // B D1 D3 C3 - // B D2 C2 D5 - // B C1 D4 D6 - int xpart_times_4; - - // The first part: O A A A. It equals lowest_possible_error previously calculated. - // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. - error = lowest_possible_error + BBBvalue + CCCvalue; - - // The remaining pixels to cover are D1 through D6. - if(error <= best_error_sofar) - { - // Second column: D1 D2 but not C1 - xpart_times_4 = (colorH-colorO); - error = error + square_table_percep_red[(block[4*4 + 4 + 0] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_red[(block[4*4*2 + 4 + 0] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - // Third column: D3 notC2 D4 - xpart_times_4 = (colorH-colorO) << 1; - error = error + square_table_percep_red[(block[4*4 + 4*2 + 0] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table_percep_red[(block[4*4*3 + 4*2 + 0] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - // Forth column: notC3 D5 D6 - xpart_times_4 = 3*(colorH-colorO); - error = error + square_table_percep_red[(block[4*4*2 + 4*3 + 0] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_red[(block[4*4*3 + 4*3 + 0] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - } - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H and V. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcErrorPlanarOnlyRed(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorH = (colorH << 2) | (colorH >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error; - - // The block can be partitioned into: O A A A - // B D1 D3 C3 - // B D2 C2 D5 - // B C1 D4 D6 - int xpart_times_4; - - // The first part: O A A A. It equals lowest_possible_error previously calculated. - // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. - error = lowest_possible_error + BBBvalue + CCCvalue; - - // The remaining pixels to cover are D1 through D6. - if(error <= best_error_sofar) - { - // Second column: D1 D2 but not C1 - xpart_times_4 = (colorH-colorO); - error = error + square_table[(block[4*4 + 4 + 0] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*2 + 4 + 0] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - // Third column: D3 notC2 D4 - xpart_times_4 = (colorH-colorO) << 1; - error = error + square_table[(block[4*4 + 4*2 + 0] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table[(block[4*4*3 + 4*2 + 0] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - // Forth column: notC3 D5 D6 - xpart_times_4 = 3*(colorH-colorO); - error = error + square_table[(block[4*4*2 + 4*3 + 0] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*3 + 4*3 + 0] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - } - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H. -// Uses perceptual error metrics. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcLowestPossibleGreenOHperceptual(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) -{ - colorO = (colorO << 1) | (colorO >> 6); - colorH = (colorH << 1) | (colorH >> 6); - - unsigned int error; - - error = square_table_percep_green[(block[1] - colorO) + 255]; - error = error + square_table_percep_green[(block[4 + 1] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table_percep_green[(block[4*2 + 1] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_green[(block[4*3 + 1] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcLowestPossibleGreenOH(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) -{ - colorO = (colorO << 1) | (colorO >> 6); - colorH = (colorH << 1) | (colorH >> 6); - - unsigned int error; - - error = square_table[(block[1] - colorO) + 255]; - error = error + square_table[(block[4 + 1] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table[(block[4*2 + 1] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*3 + 1] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the green component for O and V. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcBBBgreen(uint8 *block, int colorO, int colorV) -{ - colorO = (colorO << 1) | (colorO >> 6); - colorV = (colorV << 1) | (colorV >> 6); - - unsigned int error = 0; - - // Now first column: B B B - /* unroll loop for( yy=0; (yy<4) && (error <= best_error_sofar); yy++)*/ - { - error = error + square_table[(block[4*4 + 1] - clamp_table[ ((((colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*2 + 1] - clamp_table[ (((((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*3 + 1] - clamp_table[ (((3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - - return error; - -} - -// Calculating the minimum error for the block (in planar mode) if we know the green component for H and V. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcCCCgreen(uint8 *block, int colorH, int colorV) -{ - colorH = (colorH << 1) | (colorH >> 6); - colorV = (colorV << 1) | (colorV >> 6); - - unsigned int error=0; - - error = error + square_table[(block[4*4*3 + 4 + 1] - clamp_table[ (((colorH + 3*colorV)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*2 + 4*2 + 1] - clamp_table[ (((2*colorH + 2*colorV)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4 + 4*3 + 1] - clamp_table[ (((3*colorH + colorV)+2)>>2) + 255])+255]; - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the green component for H V and O. -// Uses perceptual error metric. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcErrorPlanarOnlyGreenPerceptual(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) -{ - colorO = (colorO << 1) | (colorO >> 6); - colorH = (colorH << 1) | (colorH >> 6); - colorV = (colorV << 1) | (colorV >> 6); - - unsigned int error; - - // The block can be partitioned into: O A A A - // B D1 D3 C3 - // B D2 C2 D5 - // B C1 D4 D6 - - int xpart_times_4; - - // The first part: O A A A. It equals lowest_possible_error previously calculated. - // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. - error = lowest_possible_error + BBBvalue + CCCvalue; - - // The remaining pixels to cover are D1 through D6. - if(error <= best_error_sofar) - { - // Second column: D1 D2 but not C1 - xpart_times_4 = (colorH-colorO); - error = error + square_table_percep_green[(block[4*4 + 4 + 1] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_green[(block[4*4*2 + 4 + 1] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - // Third column: D3 notC2 D4 - xpart_times_4 = (colorH-colorO) << 1; - error = error + square_table_percep_green[(block[4*4 + 4*2 + 1] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table_percep_green[(block[4*4*3 + 4*2 + 1] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - // Forth column: notC3 D5 D6 - xpart_times_4 = 3*(colorH-colorO); - error = error + square_table_percep_green[(block[4*4*2 + 4*3 + 1] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_green[(block[4*4*3 + 4*3 + 1] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - } - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the green component for H V and O. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcErrorPlanarOnlyGreen(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) -{ - colorO = (colorO << 1) | (colorO >> 6); - colorH = (colorH << 1) | (colorH >> 6); - colorV = (colorV << 1) | (colorV >> 6); - - unsigned int error; - - // The block can be partitioned into: O A A A - // B D1 D3 C3 - // B D2 C2 D5 - // B C1 D4 D6 - int xpart_times_4; - - // The first part: O A A A. It equals lowest_possible_error previously calculated. - // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. - error = lowest_possible_error + BBBvalue + CCCvalue; - - // The remaining pixels to cover are D1 through D6. - if(error <= best_error_sofar) - { - // Second column: D1 D2 but not C1 - xpart_times_4 = (colorH-colorO); - error = error + square_table[(block[4*4 + 4 + 1] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*2 + 4 + 1] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - // Third column: D3 notC2 D4 - xpart_times_4 = (colorH-colorO) << 1; - error = error + square_table[(block[4*4 + 4*2 + 1] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table[(block[4*4*3 + 4*2 + 1] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - // Forth column: notC3 D5 D6 - xpart_times_4 = 3*(colorH-colorO); - error = error + square_table[(block[4*4*2 + 4*3 + 1] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*3 + 4*3 + 1] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - } - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and V. -// Uses perceptual error metric. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcBBBbluePerceptual(uint8 *block, int colorO, int colorV) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error = 0; - - // Now first column: B B B - /* unroll loop for( yy=0; (yy<4) && (error <= best_error_sofar); yy++)*/ - { - error = error + square_table_percep_blue[(block[4*4 + 2] - clamp_table[ ((((colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_blue[(block[4*4*2 + 2] - clamp_table[ (((((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_blue[(block[4*4*3 + 2] - clamp_table[ (((3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and V. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcBBBblue(uint8 *block, int colorO, int colorV) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error = 0; - - // Now first column: B B B - /* unroll loop for( yy=0; (yy<4) && (error <= best_error_sofar); yy++)*/ - { - error = error + square_table[(block[4*4 + 2] - clamp_table[ ((((colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*2 + 2] - clamp_table[ (((((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*3 + 2] - clamp_table[ (((3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the blue component for H and V. -// Uses perceptual error metric. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcCCCbluePerceptual(uint8 *block, int colorH, int colorV) -{ - colorH = (colorH << 2) | (colorH >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error=0; - - error = error + square_table_percep_blue[(block[4*4*3 + 4 + 2] - clamp_table[ (((colorH + 3*colorV)+2)>>2) + 255])+255]; - error = error + square_table_percep_blue[(block[4*4*2 + 4*2 + 2] - clamp_table[ (((2*colorH + 2*colorV)+2)>>2) + 255])+255]; - error = error + square_table_percep_blue[(block[4*4 + 4*3 + 2] - clamp_table[ (((3*colorH + colorV)+2)>>2) + 255])+255]; - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and V. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcCCCblue(uint8 *block, int colorH, int colorV) -{ - colorH = (colorH << 2) | (colorH >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error=0; - - error = error + square_table[(block[4*4*3 + 4 + 2] - clamp_table[ (((colorH + 3*colorV)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*2 + 4*2 + 2] - clamp_table[ (((2*colorH + 2*colorV)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4 + 4*3 + 2] - clamp_table[ (((3*colorH + colorV)+2)>>2) + 255])+255]; - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and H. -// Uses perceptual error metric. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcLowestPossibleBlueOHperceptual(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorH = (colorH << 2) | (colorH >> 4); - - unsigned int error; - - error = square_table_percep_blue[(block[2] - colorO) + 255]; - error = error + square_table_percep_blue[(block[4+2] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table_percep_blue[(block[4*2+2] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_blue[(block[4*3+2] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and H. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcLowestPossibleBlueOH(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorH = (colorH << 2) | (colorH >> 4); - - unsigned int error; - - error = square_table[(block[2] - colorO) + 255]; - error = error + square_table[(block[4+2] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table[(block[4*2+2] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*3+2] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the blue component for O, V and H. -// Uses perceptual error metric. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcErrorPlanarOnlyBluePerceptual(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorH = (colorH << 2) | (colorH >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error; - - // The block can be partitioned into: O A A A - // B D1 D3 C3 - // B D2 C2 D5 - // B C1 D4 D6 - int xpart_times_4; - - // The first part: O A A A. It equals lowest_possible_error previously calculated. - // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. - error = lowest_possible_error + BBBvalue + CCCvalue; - - // The remaining pixels to cover are D1 through D6. - if(error <= best_error_sofar) - { - // Second column: D1 D2 but not C1 - xpart_times_4 = (colorH-colorO); - error = error + square_table_percep_blue[(block[4*4 + 4 + 2] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_blue[(block[4*4*2 + 4 + 2] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - // Third column: D3 notC2 D4 - xpart_times_4 = (colorH-colorO) << 1; - error = error + square_table_percep_blue[(block[4*4 + 4*2 + 2] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table_percep_blue[(block[4*4*3 + 4*2 + 2] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - // Forth column: notC3 D5 D6 - xpart_times_4 = 3*(colorH-colorO); - error = error + square_table_percep_blue[(block[4*4*2 + 4*3 + 2] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table_percep_blue[(block[4*4*3 + 4*3 + 2] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - } - - return error; -} - -// Calculating the minimum error for the block (in planar mode) if we know the blue component for O, V and H. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calcErrorPlanarOnlyBlue(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) -{ - colorO = (colorO << 2) | (colorO >> 4); - colorH = (colorH << 2) | (colorH >> 4); - colorV = (colorV << 2) | (colorV >> 4); - - unsigned int error; - - // The block can be partitioned into: O A A A - // B D1 D3 C3 - // B D2 C2 D5 - // B C1 D4 D6 - int xpart_times_4; - - // The first part: O A A A. It equals lowest_possible_error previously calculated. - // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. - error = lowest_possible_error + BBBvalue + CCCvalue; - - // The remaining pixels to cover are D1 through D6. - if(error <= best_error_sofar) - { - // Second column: D1 D2 but not C1 - xpart_times_4 = (colorH-colorO); - error = error + square_table[(block[4*4 + 4 + 2] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*2 + 4 + 2] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - // Third column: D3 notC2 D4 - xpart_times_4 = (colorH-colorO) << 1; - error = error + square_table[(block[4*4 + 4*2 + 2] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - if(error <= best_error_sofar) - { - error = error + square_table[(block[4*4*3 + 4*2 + 2] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - // Forth column: notC3 D5 D6 - xpart_times_4 = 3*(colorH-colorO); - error = error + square_table[(block[4*4*2 + 4*3 + 2] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; - error = error + square_table[(block[4*4*3 + 4*3 + 2] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; - } - } - - return error; -} - - - -// This function uses least squares in order to determine the best values of the plane. -// This is close to optimal, but not quite, due to nonlinearities in the expantion from 6 and 7 bits to 8, and -// in the clamping to a number between 0 and the maximum. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockPlanar57(uint8 *img, int width,int height,int startx,int starty, unsigned int &compressed57_1, unsigned int &compressed57_2) -{ - // Use least squares to find the solution with the smallest error. - // That is, find the vector x so that |Ax-b|^2 is minimized, where - // x = [Ro Rr Rv]'; - // A = [1 3/4 2/4 1/4 3/4 2/4 1/4 0 2/4 1/4 0 -1/4 1/4 0 -1/4 -2/4 ; - // 0 1/4 2/4 3/4 0 1/4 2/4 3/4 0 1/4 2/4 3/4 0 1/4 2/4 3/4 ; - // 0 0 0 0 1/4 1/4 1/4 1/4 2/4 2/4 2/4 2/4; 3/4 3/4 3/4 3/4]'; - // b = [r11 r12 r13 r14 r21 r22 r23 r24 r31 r32 r33 r34 r41 r42 r43 r44]; - // - // That is, find solution x = inv(A' * A) * A' * b - // = C * A' * b; - // C is always the same, so we have calculated it off-line here. - // = C * D - int xx,yy, cc; - double coeffsA[48]= { 1.00, 0.00, 0.00, - 0.75, 0.25, 0.00, - 0.50, 0.50, 0.00, - 0.25, 0.75, 0.00, - 0.75, 0.00, 0.25, - 0.50, 0.25, 0.25, - 0.25, 0.50, 0.25, - 0.00, 0.75, 0.25, - 0.50, 0.00, 0.50, - 0.25, 0.25, 0.50, - 0.00, 0.50, 0.50, - -0.25, 0.75, 0.50, - 0.25, 0.00, 0.75, - 0.00, 0.25, 0.75, - -0.25, 0.50, 0.75, - -0.50, 0.75, 0.75}; - - double coeffsC[9] = {0.2875, -0.0125, -0.0125, -0.0125, 0.4875, -0.3125, -0.0125, -0.3125, 0.4875}; - double colorO[3], colorH[3], colorV[3]; - uint8 colorO8[3], colorH8[3], colorV8[3]; - - dMatrix *D_matrix; - dMatrix *x_vector; - - dMatrix A_matrix; A_matrix.width = 3; A_matrix.height = 16; - A_matrix.data = coeffsA; - dMatrix C_matrix; C_matrix.width = 3; C_matrix.height = 3; - C_matrix.data = coeffsC; - dMatrix b_vector; b_vector.width = 1; b_vector.height = 16; - b_vector.data = (double*) malloc(sizeof(double)*b_vector.width*b_vector.height); - transposeMatrix(&A_matrix); - - // Red component - - // Load color data into vector b: - for(cc = 0, yy = 0; yy<4; yy++) - for(xx = 0; xx<4; xx++) - b_vector.data[cc++] = img[3*width*(starty+yy) + 3*(startx+xx) + 0]; - - D_matrix = multiplyMatrices(&A_matrix, &b_vector); - x_vector = multiplyMatrices(&C_matrix, D_matrix); - - colorO[0] = CLAMP(0.0, x_vector->data[0], 255.0); - colorH[0] = CLAMP(0.0, x_vector->data[1], 255.0); - colorV[0] = CLAMP(0.0, x_vector->data[2], 255.0); - - free(D_matrix->data); free(D_matrix); - free(x_vector->data); free(x_vector); - - // Green component - - // Load color data into vector b: - for(cc = 0, yy = 0; yy<4; yy++) - for(xx = 0; xx<4; xx++) - b_vector.data[cc++] = img[3*width*(starty+yy) + 3*(startx+xx) + 1]; - - D_matrix = multiplyMatrices(&A_matrix, &b_vector); - x_vector = multiplyMatrices(&C_matrix, D_matrix); - - colorO[1] = CLAMP(0.0, x_vector->data[0], 255.0); - colorH[1] = CLAMP(0.0, x_vector->data[1], 255.0); - colorV[1] = CLAMP(0.0, x_vector->data[2], 255.0); - - free(D_matrix->data); free(D_matrix); - free(x_vector->data); free(x_vector); - - // Blue component - - // Load color data into vector b: - for(cc = 0, yy = 0; yy<4; yy++) - for(xx = 0; xx<4; xx++) - b_vector.data[cc++] = img[3*width*(starty+yy) + 3*(startx+xx) + 2]; - - D_matrix = multiplyMatrices(&A_matrix, &b_vector); - x_vector = multiplyMatrices(&C_matrix, D_matrix); - - colorO[2] = CLAMP(0.0, x_vector->data[0], 255.0); - colorH[2] = CLAMP(0.0, x_vector->data[1], 255.0); - colorV[2] = CLAMP(0.0, x_vector->data[2], 255.0); - - free(D_matrix->data); free(D_matrix); - free(x_vector->data); free(x_vector); - - // Quantize to 6 bits - double D = 255*(1.0/((1<<6)-1.0) ); - colorO8[0] = JAS_ROUND((1.0*colorO[0])/D); - colorO8[2] = JAS_ROUND((1.0*colorO[2])/D); - colorH8[0] = JAS_ROUND((1.0*colorH[0])/D); - colorH8[2] = JAS_ROUND((1.0*colorH[2])/D); - colorV8[0] = JAS_ROUND((1.0*colorV[0])/D); - colorV8[2] = JAS_ROUND((1.0*colorV[2])/D); - - // Quantize to 7 bits - D = 255*(1.0/((1<<7)-1.0) ); - colorO8[1] = JAS_ROUND((1.0*colorO[1])/D); - colorH8[1] = JAS_ROUND((1.0*colorH[1])/D); - colorV8[1] = JAS_ROUND((1.0*colorV[1])/D); - - // Pack bits in 57 bits - - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ------------------------------------------------------------------------------------------------ - // | R0 | G0 | B0 | RH | GH | - // ------------------------------------------------------------------------------------------------ - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // ------------------------------------------------------------------------------------------------ - // | BH | RV | GV | BV | not used | - // ------------------------------------------------------------------------------------------------ - - compressed57_1 = 0; - compressed57_2 = 0; - PUTBITSHIGH( compressed57_1, colorO8[0], 6, 63); - PUTBITSHIGH( compressed57_1, colorO8[1], 7, 57); - PUTBITSHIGH( compressed57_1, colorO8[2], 6, 50); - PUTBITSHIGH( compressed57_1, colorH8[0], 6, 44); - PUTBITSHIGH( compressed57_1, colorH8[1], 7, 38); - PUTBITS( compressed57_2, colorH8[2], 6, 31); - PUTBITS( compressed57_2, colorV8[0], 6, 25); - PUTBITS( compressed57_2, colorV8[1], 7, 19); - PUTBITS( compressed57_2, colorV8[2], 6, 12); -} - -// During search it is not convenient to store the bits the way they are stored in the -// file format. Hence, after search, it is converted to this format. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void stuff57bits(unsigned int planar57_word1, unsigned int planar57_word2, unsigned int &planar_word1, unsigned int &planar_word2) -{ - // Put bits in twotimer configuration for 57 bits (red and green dont overflow, green does) - // - // Go from this bit layout: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // |R0 |G01G02 |B01B02 ;B03 |RH1 |RH2|GH | - // ----------------------------------------------------------------------------------------------- - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // ----------------------------------------------------------------------------------------------- - // |BH |RV |GV |BV | not used | - // ----------------------------------------------------------------------------------------------- - // - // To this: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ------------------------------------------------------------------------------------------------ - // |//|R0 |G01|/|G02 |B01|/ // //|B02 |//|B03 |RH1 |df|RH2| - // ------------------------------------------------------------------------------------------------ - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // ----------------------------------------------------------------------------------------------- - // |GH |BH |RV |GV |BV | - // ----------------------------------------------------------------------------------------------- - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - uint8 RO, GO1, GO2, BO1, BO2, BO3, RH1, RH2, GH, BH, RV, GV, BV; - uint8 bit, a, b, c, d, bits; - - RO = GETBITSHIGH( planar57_word1, 6, 63); - GO1= GETBITSHIGH( planar57_word1, 1, 57); - GO2= GETBITSHIGH( planar57_word1, 6, 56); - BO1= GETBITSHIGH( planar57_word1, 1, 50); - BO2= GETBITSHIGH( planar57_word1, 2, 49); - BO3= GETBITSHIGH( planar57_word1, 3, 47); - RH1= GETBITSHIGH( planar57_word1, 5, 44); - RH2= GETBITSHIGH( planar57_word1, 1, 39); - GH = GETBITSHIGH( planar57_word1, 7, 38); - BH = GETBITS( planar57_word2, 6, 31); - RV = GETBITS( planar57_word2, 6, 25); - GV = GETBITS( planar57_word2, 7, 19); - BV = GETBITS( planar57_word2, 6, 12); - - planar_word1 = 0; planar_word2 = 0; - PUTBITSHIGH( planar_word1, RO, 6, 62); - PUTBITSHIGH( planar_word1, GO1, 1, 56); - PUTBITSHIGH( planar_word1, GO2, 6, 54); - PUTBITSHIGH( planar_word1, BO1, 1, 48); - PUTBITSHIGH( planar_word1, BO2, 2, 44); - PUTBITSHIGH( planar_word1, BO3, 3, 41); - PUTBITSHIGH( planar_word1, RH1, 5, 38); - PUTBITSHIGH( planar_word1, RH2, 1, 32); - PUTBITS( planar_word2, GH, 7, 31); - PUTBITS( planar_word2, BH, 6, 24); - PUTBITS( planar_word2, RV, 6, 18); - PUTBITS( planar_word2, GV, 7, 12); - PUTBITS( planar_word2, BV, 6, 5); - - // Make sure that red does not overflow: - bit = GETBITSHIGH( planar_word1, 1, 62); - PUTBITSHIGH( planar_word1, !bit, 1, 63); - - // Make sure that green does not overflow: - bit = GETBITSHIGH( planar_word1, 1, 54); - PUTBITSHIGH( planar_word1, !bit, 1, 55); - - // Make sure that blue overflows: - a = GETBITSHIGH( planar_word1, 1, 44); - b = GETBITSHIGH( planar_word1, 1, 43); - c = GETBITSHIGH( planar_word1, 1, 41); - d = GETBITSHIGH( planar_word1, 1, 40); - // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 - // The following logical expression checks for the presence of any of those: - bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); - bits = 0xf*bit; - PUTBITSHIGH( planar_word1, bits, 3, 47); - PUTBITSHIGH( planar_word1, !bit, 1, 42); - - // Set diffbit - PUTBITSHIGH( planar_word1, 1, 1, 33); -} - -// During search it is not convenient to store the bits the way they are stored in the -// file format. Hence, after search, it is converted to this format. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void stuff58bits(unsigned int thumbH58_word1, unsigned int thumbH58_word2, unsigned int &thumbH_word1, unsigned int &thumbH_word2) -{ - // Put bits in twotimer configuration for 58 (red doesn't overflow, green does) - // - // Go from this bit layout: - // - // - // |63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| - // |-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| - // - // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| - // |---------------------------------------index bits----------------------------------------------| - // - // To this: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // |//|R0 |G0 |// // //|G0|B0|//|B0b |R1 |G1 |B0 |d2|df|d1| - // ----------------------------------------------------------------------------------------------- - // - // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| - // |---------------------------------------index bits----------------------------------------------| - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt| - // ----------------------------------------------------------------------------------------------- - // - // - // Thus, what we are really doing is going from this bit layout: - // - // - // |63 62 61 60 59 58|57 56 55 54 53 52 51|50 49|48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33|32 | - // |-------empty-----|part0---------------|part1|part2------------------------------------------|part3| - // - // To this: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------| - // |//|part0 |// // //|part1|//|part2 |df|part3| - // --------------------------------------------------------------------------------------------------| - - unsigned int part0, part1, part2, part3; - uint8 bit, a, b, c, d, bits; - - // move parts - part0 = GETBITSHIGH( thumbH58_word1, 7, 57); - part1 = GETBITSHIGH( thumbH58_word1, 2, 50); - part2 = GETBITSHIGH( thumbH58_word1,16, 48); - part3 = GETBITSHIGH( thumbH58_word1, 1, 32); - thumbH_word1 = 0; - PUTBITSHIGH( thumbH_word1, part0, 7, 62); - PUTBITSHIGH( thumbH_word1, part1, 2, 52); - PUTBITSHIGH( thumbH_word1, part2, 16, 49); - PUTBITSHIGH( thumbH_word1, part3, 1, 32); - - // Make sure that red does not overflow: - bit = GETBITSHIGH( thumbH_word1, 1, 62); - PUTBITSHIGH( thumbH_word1, !bit, 1, 63); - - // Make sure that green overflows: - a = GETBITSHIGH( thumbH_word1, 1, 52); - b = GETBITSHIGH( thumbH_word1, 1, 51); - c = GETBITSHIGH( thumbH_word1, 1, 49); - d = GETBITSHIGH( thumbH_word1, 1, 48); - // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 - // The following logical expression checks for the presence of any of those: - bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); - bits = 0xf*bit; - PUTBITSHIGH( thumbH_word1, bits, 3, 55); - PUTBITSHIGH( thumbH_word1, !bit, 1, 50); - - // Set diffbit - PUTBITSHIGH( thumbH_word1, 1, 1, 33); - thumbH_word2 = thumbH58_word2; - -} - -// copy of above, but diffbit is 0 -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void stuff58bitsDiffFalse(unsigned int thumbH58_word1, unsigned int thumbH58_word2, unsigned int &thumbH_word1, unsigned int &thumbH_word2) -{ - unsigned int part0, part1, part2, part3; - uint8 bit, a, b, c, d, bits; - - // move parts - part0 = GETBITSHIGH( thumbH58_word1, 7, 57); - part1 = GETBITSHIGH( thumbH58_word1, 2, 50); - part2 = GETBITSHIGH( thumbH58_word1,16, 48); - part3 = GETBITSHIGH( thumbH58_word1, 1, 32); - thumbH_word1 = 0; - PUTBITSHIGH( thumbH_word1, part0, 7, 62); - PUTBITSHIGH( thumbH_word1, part1, 2, 52); - PUTBITSHIGH( thumbH_word1, part2, 16, 49); - PUTBITSHIGH( thumbH_word1, part3, 1, 32); - - // Make sure that red does not overflow: - bit = GETBITSHIGH( thumbH_word1, 1, 62); - PUTBITSHIGH( thumbH_word1, !bit, 1, 63); - - // Make sure that green overflows: - a = GETBITSHIGH( thumbH_word1, 1, 52); - b = GETBITSHIGH( thumbH_word1, 1, 51); - c = GETBITSHIGH( thumbH_word1, 1, 49); - d = GETBITSHIGH( thumbH_word1, 1, 48); - // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 - // The following logical expression checks for the presence of any of those: - bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); - bits = 0xf*bit; - PUTBITSHIGH( thumbH_word1, bits, 3, 55); - PUTBITSHIGH( thumbH_word1, !bit, 1, 50); - - // Set diffbit - PUTBITSHIGH( thumbH_word1, 0, 1, 33); - thumbH_word2 = thumbH58_word2; - -} - -// During search it is not convenient to store the bits the way they are stored in the -// file format. Hence, after search, it is converted to this format. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void stuff59bits(unsigned int thumbT59_word1, unsigned int thumbT59_word2, unsigned int &thumbT_word1, unsigned int &thumbT_word2) -{ - // Put bits in twotimer configuration for 59 (red overflows) - // - // Go from this bit layout: - // - // |63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| - // |----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| - // - // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| - // |----------------------------------------index bits---------------------------------------------| - // - // - // To this: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // |// // //|R0a |//|R0b |G0 |B0 |R1 |G1 |B1 |da |df|db| - // ----------------------------------------------------------------------------------------------- - // - // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| - // |----------------------------------------index bits---------------------------------------------| - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt| - // ------------------------------------------------------------------------------------------------ - - uint8 R0a; - uint8 bit, a, b, c, d, bits; - - R0a = GETBITSHIGH( thumbT59_word1, 2, 58); - - // Fix middle part - thumbT_word1 = thumbT59_word1 << 1; - // Fix R0a (top two bits of R0) - PUTBITSHIGH( thumbT_word1, R0a, 2, 60); - // Fix db (lowest bit of d) - PUTBITSHIGH( thumbT_word1, thumbT59_word1, 1, 32); - // - // Make sure that red overflows: - a = GETBITSHIGH( thumbT_word1, 1, 60); - b = GETBITSHIGH( thumbT_word1, 1, 59); - c = GETBITSHIGH( thumbT_word1, 1, 57); - d = GETBITSHIGH( thumbT_word1, 1, 56); - // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 - // The following logical expression checks for the presence of any of those: - bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); - bits = 0xf*bit; - PUTBITSHIGH( thumbT_word1, bits, 3, 63); - PUTBITSHIGH( thumbT_word1, !bit, 1, 58); - - // Set diffbit - PUTBITSHIGH( thumbT_word1, 1, 1, 33); - thumbT_word2 = thumbT59_word2; -} - - -// Decompress the planar mode and calculate the error per component compared to original image. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void decompressBlockPlanar57errorPerComponent(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img,int width,int height,int startx,int starty, uint8 *srcimg, unsigned int &error_red, unsigned int &error_green, unsigned int &error_blue) -{ - uint8 colorO[3], colorH[3], colorV[3]; - - colorO[0] = GETBITSHIGH( compressed57_1, 6, 63); - colorO[1] = GETBITSHIGH( compressed57_1, 7, 57); - colorO[2] = GETBITSHIGH( compressed57_1, 6, 50); - colorH[0] = GETBITSHIGH( compressed57_1, 6, 44); - colorH[1] = GETBITSHIGH( compressed57_1, 7, 38); - colorH[2] = GETBITS( compressed57_2, 6, 31); - colorV[0] = GETBITS( compressed57_2, 6, 25); - colorV[1] = GETBITS( compressed57_2, 7, 19); - colorV[2] = GETBITS( compressed57_2, 6, 12); - - colorO[0] = (colorO[0] << 2) | (colorO[0] >> 4); - colorO[1] = (colorO[1] << 1) | (colorO[1] >> 6); - colorO[2] = (colorO[2] << 2) | (colorO[2] >> 4); - - colorH[0] = (colorH[0] << 2) | (colorH[0] >> 4); - colorH[1] = (colorH[1] << 1) | (colorH[1] >> 6); - colorH[2] = (colorH[2] << 2) | (colorH[2] >> 4); - - colorV[0] = (colorV[0] << 2) | (colorV[0] >> 4); - colorV[1] = (colorV[1] << 1) | (colorV[1] >> 6); - colorV[2] = (colorV[2] << 2) | (colorV[2] >> 4); - - int xx, yy; - - for( xx=0; xx<4; xx++) - { - for( yy=0; yy<4; yy++) - { - img[3*width*(starty+yy) + 3*(startx+xx) + 0] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[0]-colorO[0])/4.0 + yy*(colorV[0]-colorO[0])/4.0 + colorO[0])), 255); - img[3*width*(starty+yy) + 3*(startx+xx) + 1] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[1]-colorO[1])/4.0 + yy*(colorV[1]-colorO[1])/4.0 + colorO[1])), 255); - img[3*width*(starty+yy) + 3*(startx+xx) + 2] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[2]-colorO[2])/4.0 + yy*(colorV[2]-colorO[2])/4.0 + colorO[2])), 255); - } - } - - error_red = 0; - error_green= 0; - error_blue = 0; - for( xx=0; xx<4; xx++) - { - for( yy=0; yy<4; yy++) - { - error_red = error_red + SQUARE(srcimg[3*width*(starty+yy) + 3*(startx+xx) + 0] - img[3*width*(starty+yy) + 3*(startx+xx) + 0]); - error_green = error_green + SQUARE(srcimg[3*width*(starty+yy) + 3*(startx+xx) + 1] - img[3*width*(starty+yy) + 3*(startx+xx) + 1]); - error_blue = error_blue + SQUARE(srcimg[3*width*(starty+yy) + 3*(startx+xx) + 2] - img[3*width*(starty+yy) + 3*(startx+xx) + 2]); - - } - } -} - -// Compress using both individual and differential mode in ETC1/ETC2 using combined color -// quantization. Both flip modes are tried. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockDiffFlipCombined(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - unsigned int compressed1_norm, compressed2_norm; - unsigned int compressed1_flip, compressed2_flip; - uint8 avg_color_quant1[3], avg_color_quant2[3]; - - float avg_color_float1[3],avg_color_float2[3]; - int enc_color1[3], enc_color2[3], diff[3]; - int min_error=255*255*8*3; - unsigned int best_table_indices1=0, best_table_indices2=0; - unsigned int best_table1=0, best_table2=0; - int diffbit; - - int norm_err=0; - int flip_err=0; - - // First try normal blocks 2x4: - - computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - float eps; - - uint8 dummy[3]; - - quantize555ColorCombined(avg_color_float1, enc_color1, dummy); - quantize555ColorCombined(avg_color_float2, enc_color2, dummy); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) - { - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); - PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); - PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - norm_err = 0; - - // left part of block - norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - - } - else - { - diffbit = 0; - // The difference is bigger than what fits in 555 plus delta-333, so we will have - // to deal with 444 444. - - eps = (float) 0.0001; - - uint8 dummy[3]; - quantize444ColorCombined(avg_color_float1, enc_color1, dummy); - quantize444ColorCombined(avg_color_float2, enc_color2, dummy); - - avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; - avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; - avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; - avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; - avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; - avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; - - - // Pack bits into the first word. - - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // left part of block - norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - } - - // Now try flipped blocks 4x2: - - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - quantize555ColorCombined(avg_color_float1, enc_color1, dummy); - quantize555ColorCombined(avg_color_float2, enc_color2, dummy); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) - { - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); - PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); - PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // upper part of block - flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - else - { - diffbit = 0; - // The difference is bigger than what fits in 555 plus delta-333, so we will have - // to deal with 444 444. - eps = (float) 0.0001; - - uint8 dummy[3]; - quantize444ColorCombined(avg_color_float1, enc_color1, dummy); - quantize444ColorCombined(avg_color_float2, enc_color2, dummy); - - avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; - avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; - avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; - avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; - avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; - avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; - - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - - // Pack bits into the first word. - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // upper part of block - flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - - // Now lets see which is the best table to use. Only 8 tables are possible. - - if(norm_err <= flip_err) - { - compressed1 = compressed1_norm | 0; - compressed2 = compressed2_norm; - } - else - { - compressed1 = compressed1_flip | 1; - compressed2 = compressed2_flip; - } -} - -// Calculation of the two block colors using the LBG-algorithm -// The following method scales down the intensity, since this can be compensated for anyway by both the H and T mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void computeColorLBGHalfIntensityFast(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) -{ - uint8 block_mask[4][4]; - - // reset rand so that we get predictable output per block - srand(10000); - //LBG-algorithm - double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; - double error_a, error_b; - int number_of_iterations = 10; - double t_color[2][3]; - double original_colors[4][4][3]; - double current_colors[2][3]; - double best_colors[2][3]; - double max_v[3]; - double min_v[3]; - int x,y,i; - double red, green, blue; - bool continue_seeding; - int maximum_number_of_seedings = 10; - int seeding; - bool continue_iterate; - - max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; - min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; - - // resolve trainingdata - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (x = 0; x < BLOCKWIDTH; ++x) - { - red = img[3*((starty+y)*width+startx+x)+R]; - green = img[3*((starty+y)*width+startx+x)+G]; - blue = img[3*((starty+y)*width+startx+x)+B]; - - // Use qrs representation instead of rgb - // qrs = Q * rgb where Q = [a a a ; b -b 0 ; c c -2c]; a = 1/sqrt(3), b= 1/sqrt(2), c = 1/sqrt(6); - // rgb = inv(Q)*qrs = Q' * qrs where ' denotes transpose. - // The q variable holds intensity. r and s hold chrominance. - // q = [0, sqrt(3)*255], r = [-255/sqrt(2), 255/sqrt(2)], s = [-2*255/sqrt(6), 2*255/sqrt(6)]; - // - // The LGB algorithm will only act on the r and s variables and not on q. - // - original_colors[x][y][R] = (1.0/sqrt(1.0*3))*red + (1.0/sqrt(1.0*3))*green + (1.0/sqrt(1.0*3))*blue; - original_colors[x][y][G] = (1.0/sqrt(1.0*2))*red - (1.0/sqrt(1.0*2))*green; - original_colors[x][y][B] = (1.0/sqrt(1.0*6))*red + (1.0/sqrt(1.0*6))*green - (2.0/sqrt(1.0*6))*blue; - - // find max - if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; - // find min - if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; - } - } - - D = 512*512*3*16.0; - bestD = 512*512*3*16.0; - - continue_seeding = true; - - // loop seeds - for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) - { - // hopefully we will not need more seedings: - continue_seeding = false; - - // calculate seeds - for (uint8 s = 0; s < 2; ++s) - { - for (uint8 c = 0; c < 3; ++c) - { - current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; - } - } - - // divide into two quantization sets and calculate distortion - - continue_iterate = true; - for(i = 0; (i < number_of_iterations) && continue_iterate; i++) - { - oldD = D; - D = 0; - int n = 0; - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - error_a = 0.5*SQUARE(original_colors[x][y][R] - current_colors[0][R]) + - SQUARE(original_colors[x][y][G] - current_colors[0][G]) + - SQUARE(original_colors[x][y][B] - current_colors[0][B]); - error_b = 0.5*SQUARE(original_colors[x][y][R] - current_colors[1][R]) + - SQUARE(original_colors[x][y][G] - current_colors[1][G]) + - SQUARE(original_colors[x][y][B] - current_colors[1][B]); - if (error_a < error_b) - { - block_mask[x][y] = 0; - D += error_a; - ++n; - } - else - { - block_mask[x][y] = 1; - D += error_b; - } - } - } - - // compare with old distortion - if (D == 0) - { - // Perfect score -- we dont need to go further iterations. - continue_iterate = false; - continue_seeding = false; - } - if (D == oldD) - { - // Same score as last round -- no need to go for further iterations. - continue_iterate = false; - continue_seeding = false; - } - if (D < bestD) - { - bestD = D; - for(uint8 s = 0; s < 2; ++s) - { - for(uint8 c = 0; c < 3; ++c) - { - best_colors[s][c] = current_colors[s][c]; - } - } - } - if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) - { - // All colors end up in the same voroni region. We need to reseed. - continue_iterate = false; - continue_seeding = true; - } - else - { - // Calculate new reconstruction points using the centroids - - // Find new construction values from average - t_color[0][R] = 0; - t_color[0][G] = 0; - t_color[0][B] = 0; - t_color[1][R] = 0; - t_color[1][G] = 0; - t_color[1][B] = 0; - - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - // use dummy value for q-parameter - t_color[block_mask[x][y]][R] += original_colors[x][y][R]; - t_color[block_mask[x][y]][G] += original_colors[x][y][G]; - t_color[block_mask[x][y]][B] += original_colors[x][y][B]; - } - } - current_colors[0][R] = t_color[0][R] / n; - current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][G] = t_color[0][G] / n; - current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][B] = t_color[0][B] / n; - current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); - } - } - } - - for(x=0;x<2;x++) - { - double qq, rr, ss; - - qq = best_colors[x][0]; - rr = best_colors[x][1]; - ss = best_colors[x][2]; - - current_colors[x][0] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); - current_colors[x][1] = CLAMP(0, (1.0/sqrt(1.0*3))*qq - (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); - current_colors[x][2] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (0.0 )*rr - (2.0/sqrt(1.0*6))*ss, 255); - } - - for(x=0;x<2;x++) - for(y=0;y<3;y++) - LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); -} - -// Calculation of the two block colors using the LBG-algorithm -// The following method scales down the intensity, since this can be compensated for anyway by both the H and T mode. -// Faster version -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void computeColorLBGNotIntensityFast(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) -{ - uint8 block_mask[4][4]; - - // reset rand so that we get predictable output per block - srand(10000); - //LBG-algorithm - double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; - double error_a, error_b; - int number_of_iterations = 10; - double t_color[2][3]; - double original_colors[4][4][3]; - double current_colors[2][3]; - double best_colors[2][3]; - double max_v[3]; - double min_v[3]; - int x,y,i; - double red, green, blue; - bool continue_seeding; - int maximum_number_of_seedings = 10; - int seeding; - bool continue_iterate; - - max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; - min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; - - // resolve trainingdata - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (x = 0; x < BLOCKWIDTH; ++x) - { - red = img[3*((starty+y)*width+startx+x)+R]; - green = img[3*((starty+y)*width+startx+x)+G]; - blue = img[3*((starty+y)*width+startx+x)+B]; - - // Use qrs representation instead of rgb - // qrs = Q * rgb where Q = [a a a ; b -b 0 ; c c -2c]; a = 1/sqrt(1.0*3), b= 1/sqrt(1.0*2), c = 1/sqrt(1.0*6); - // rgb = inv(Q)*qrs = Q' * qrs where ' denotes transpose. - // The q variable holds intensity. r and s hold chrominance. - // q = [0, sqrt(1.0*3)*255], r = [-255/sqrt(1.0*2), 255/sqrt(1.0*2)], s = [-2*255/sqrt(1.0*6), 2*255/sqrt(1.0*6)]; - // - // The LGB algorithm will only act on the r and s variables and not on q. - // - original_colors[x][y][R] = (1.0/sqrt(1.0*3))*red + (1.0/sqrt(1.0*3))*green + (1.0/sqrt(1.0*3))*blue; - original_colors[x][y][G] = (1.0/sqrt(1.0*2))*red - (1.0/sqrt(1.0*2))*green; - original_colors[x][y][B] = (1.0/sqrt(1.0*6))*red + (1.0/sqrt(1.0*6))*green - (2.0/sqrt(1.0*6))*blue; - - // find max - if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; - // find min - if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; - } - } - - D = 512*512*3*16.0; - bestD = 512*512*3*16.0; - - continue_seeding = true; - - // loop seeds - for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) - { - // hopefully we will not need more seedings: - continue_seeding = false; - - // calculate seeds - for (uint8 s = 0; s < 2; ++s) - { - for (uint8 c = 0; c < 3; ++c) - { - current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; - } - } - // divide into two quantization sets and calculate distortion - - continue_iterate = true; - for(i = 0; (i < number_of_iterations) && continue_iterate; i++) - { - oldD = D; - D = 0; - int n = 0; - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - error_a = 0.0*SQUARE(original_colors[x][y][R] - current_colors[0][R]) + - SQUARE(original_colors[x][y][G] - current_colors[0][G]) + - SQUARE(original_colors[x][y][B] - current_colors[0][B]); - error_b = 0.0*SQUARE(original_colors[x][y][R] - current_colors[1][R]) + - SQUARE(original_colors[x][y][G] - current_colors[1][G]) + - SQUARE(original_colors[x][y][B] - current_colors[1][B]); - if (error_a < error_b) - { - block_mask[x][y] = 0; - D += error_a; - ++n; - } - else - { - block_mask[x][y] = 1; - D += error_b; - } - } - } - - // compare with old distortion - if (D == 0) - { - // Perfect score -- we dont need to go further iterations. - continue_iterate = false; - continue_seeding = false; - } - if (D == oldD) - { - // Same score as last round -- no need to go for further iterations. - continue_iterate = false; - continue_seeding = false; - } - if (D < bestD) - { - bestD = D; - for(uint8 s = 0; s < 2; ++s) - { - for(uint8 c = 0; c < 3; ++c) - { - best_colors[s][c] = current_colors[s][c]; - } - } - } - if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) - { - // All colors end up in the same voroni region. We need to reseed. - continue_iterate = false; - continue_seeding = true; - } - else - { - // Calculate new reconstruction points using the centroids - - // Find new construction values from average - t_color[0][R] = 0; - t_color[0][G] = 0; - t_color[0][B] = 0; - t_color[1][R] = 0; - t_color[1][G] = 0; - t_color[1][B] = 0; - - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - // use dummy value for q-parameter - t_color[block_mask[x][y]][R] += original_colors[x][y][R]; - t_color[block_mask[x][y]][G] += original_colors[x][y][G]; - t_color[block_mask[x][y]][B] += original_colors[x][y][B]; - } - } - current_colors[0][R] = t_color[0][R] / n; - current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][G] = t_color[0][G] / n; - current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][B] = t_color[0][B] / n; - current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); - } - } - } - - for(x=0;x<2;x++) - { - double qq, rr, ss; - - qq = best_colors[x][0]; - rr = best_colors[x][1]; - ss = best_colors[x][2]; - - current_colors[x][0] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); - current_colors[x][1] = CLAMP(0, (1.0/sqrt(1.0*3))*qq - (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); - current_colors[x][2] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (0.0 )*rr - (2.0/sqrt(1.0*6))*ss, 255); - } - - for(x=0;x<2;x++) - for(y=0;y<3;y++) - LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); -} - -// Calculation of the two block colors using the LBG-algorithm -// The following method completely ignores the intensity, since this can be compensated for anyway by both the H and T mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void computeColorLBGNotIntensity(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) -{ - uint8 block_mask[4][4]; - - // reset rand so that we get predictable output per block - srand(10000); - //LBG-algorithm - double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; - double error_a, error_b; - int number_of_iterations = 10; - double t_color[2][3]; - double original_colors[4][4][3]; - double current_colors[2][3]; - double best_colors[2][3]; - double max_v[3]; - double min_v[3]; - int x,y,i; - double red, green, blue; - bool continue_seeding; - int maximum_number_of_seedings = 10; - int seeding; - bool continue_iterate; - - max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; - min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; - - // resolve trainingdata - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (x = 0; x < BLOCKWIDTH; ++x) - { - red = img[3*((starty+y)*width+startx+x)+R]; - green = img[3*((starty+y)*width+startx+x)+G]; - blue = img[3*((starty+y)*width+startx+x)+B]; - - // Use qrs representation instead of rgb - // qrs = Q * rgb where Q = [a a a ; b -b 0 ; c c -2c]; a = 1/sqrt(1.0*3), b= 1/sqrt(1.0*2), c = 1/sqrt(1.0*6); - // rgb = inv(Q)*qrs = Q' * qrs where ' denotes transpose. - // The q variable holds intensity. r and s hold chrominance. - // q = [0, sqrt(1.0*3)*255], r = [-255/sqrt(1.0*2), 255/sqrt(1.0*2)], s = [-2*255/sqrt(1.0*6), 2*255/sqrt(1.0*6)]; - // - // The LGB algorithm will only act on the r and s variables and not on q. - // - original_colors[x][y][R] = (1.0/sqrt(1.0*3))*red + (1.0/sqrt(1.0*3))*green + (1.0/sqrt(1.0*3))*blue; - original_colors[x][y][G] = (1.0/sqrt(1.0*2))*red - (1.0/sqrt(1.0*2))*green; - original_colors[x][y][B] = (1.0/sqrt(1.0*6))*red + (1.0/sqrt(1.0*6))*green - (2.0/sqrt(1.0*6))*blue; - - // find max - if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; - // find min - if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; - } - } - - D = 512*512*3*16.0; - bestD = 512*512*3*16.0; - - continue_seeding = true; - - // loop seeds - for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) - { - // hopefully we will not need more seedings: - continue_seeding = false; - - // calculate seeds - for (uint8 s = 0; s < 2; ++s) - { - for (uint8 c = 0; c < 3; ++c) - { - current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; - } - } - - // divide into two quantization sets and calculate distortion - - continue_iterate = true; - for(i = 0; (i < number_of_iterations) && continue_iterate; i++) - { - oldD = D; - D = 0; - int n = 0; - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - error_a = 0.0*SQUARE(original_colors[x][y][R] - current_colors[0][R]) + - SQUARE(original_colors[x][y][G] - current_colors[0][G]) + - SQUARE(original_colors[x][y][B] - current_colors[0][B]); - error_b = 0.0*SQUARE(original_colors[x][y][R] - current_colors[1][R]) + - SQUARE(original_colors[x][y][G] - current_colors[1][G]) + - SQUARE(original_colors[x][y][B] - current_colors[1][B]); - if (error_a < error_b) - { - block_mask[x][y] = 0; - D += error_a; - ++n; - } - else - { - block_mask[x][y] = 1; - D += error_b; - } - } - } - - // compare with old distortion - if (D == 0) - { - // Perfect score -- we dont need to go further iterations. - continue_iterate = false; - continue_seeding = false; - } - if (D == oldD) - { - // Same score as last round -- no need to go for further iterations. - continue_iterate = false; - continue_seeding = true; - } - if (D < bestD) - { - bestD = D; - for(uint8 s = 0; s < 2; ++s) - { - for(uint8 c = 0; c < 3; ++c) - { - best_colors[s][c] = current_colors[s][c]; - } - } - } - if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) - { - // All colors end up in the same voroni region. We need to reseed. - continue_iterate = false; - continue_seeding = true; - } - else - { - // Calculate new reconstruction points using the centroids - - // Find new construction values from average - t_color[0][R] = 0; - t_color[0][G] = 0; - t_color[0][B] = 0; - t_color[1][R] = 0; - t_color[1][G] = 0; - t_color[1][B] = 0; - - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - // use dummy value for q-parameter - t_color[block_mask[x][y]][R] += original_colors[x][y][R]; - t_color[block_mask[x][y]][G] += original_colors[x][y][G]; - t_color[block_mask[x][y]][B] += original_colors[x][y][B]; - } - } - current_colors[0][R] = t_color[0][R] / n; - current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][G] = t_color[0][G] / n; - current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][B] = t_color[0][B] / n; - current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); - } - } - } - - for(x=0;x<2;x++) - { - double qq, rr, ss; - - qq = best_colors[x][0]; - rr = best_colors[x][1]; - ss = best_colors[x][2]; - - current_colors[x][0] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); - current_colors[x][1] = CLAMP(0, (1.0/sqrt(1.0*3))*qq - (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); - current_colors[x][2] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (0.0 )*rr - (2.0/sqrt(1.0*6))*ss, 255); - } - - for(x=0;x<2;x++) - for(y=0;y<3;y++) - LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); -} - -// Calculation of the two block colors using the LBG-algorithm -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void computeColorLBG(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) -{ - uint8 block_mask[4][4]; - - // reset rand so that we get predictable output per block - srand(10000); - //LBG-algorithm - double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; - double error_a, error_b; - int number_of_iterations = 10; - double t_color[2][3]; - double original_colors[4][4][3]; - double current_colors[2][3]; - double best_colors[2][3]; - double max_v[3]; - double min_v[3]; - int x,y,i; - double red, green, blue; - bool continue_seeding; - int maximum_number_of_seedings = 10; - int seeding; - bool continue_iterate; - - max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; - min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; - - // resolve trainingdata - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (x = 0; x < BLOCKWIDTH; ++x) - { - red = img[3*((starty+y)*width+startx+x)+R]; - green = img[3*((starty+y)*width+startx+x)+G]; - blue = img[3*((starty+y)*width+startx+x)+B]; - - original_colors[x][y][R] = red; - original_colors[x][y][G] = green; - original_colors[x][y][B] = blue; - - // find max - if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; - // find min - if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; - } - } - - D = 512*512*3*16.0; - bestD = 512*512*3*16.0; - - continue_seeding = true; - - // loop seeds - for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) - { - // hopefully we will not need more seedings: - continue_seeding = false; - - // calculate seeds - for (uint8 s = 0; s < 2; ++s) - { - for (uint8 c = 0; c < 3; ++c) - { - current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; - } - } - - // divide into two quantization sets and calculate distortion - - continue_iterate = true; - for(i = 0; (i < number_of_iterations) && continue_iterate; i++) - { - oldD = D; - D = 0; - int n = 0; - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - error_a = SQUARE(original_colors[x][y][R] - JAS_ROUND(current_colors[0][R])) + - SQUARE(original_colors[x][y][G] - JAS_ROUND(current_colors[0][G])) + - SQUARE(original_colors[x][y][B] - JAS_ROUND(current_colors[0][B])); - error_b = SQUARE(original_colors[x][y][R] - JAS_ROUND(current_colors[1][R])) + - SQUARE(original_colors[x][y][G] - JAS_ROUND(current_colors[1][G])) + - SQUARE(original_colors[x][y][B] - JAS_ROUND(current_colors[1][B])); - if (error_a < error_b) - { - block_mask[x][y] = 0; - D += error_a; - ++n; - } - else - { - block_mask[x][y] = 1; - D += error_b; - } - } - } - - // compare with old distortion - if (D == 0) - { - // Perfect score -- we dont need to go further iterations. - continue_iterate = false; - continue_seeding = false; - } - if (D == oldD) - { - // Same score as last round -- no need to go for further iterations. - continue_iterate = false; - continue_seeding = true; - } - if (D < bestD) - { - bestD = D; - for(uint8 s = 0; s < 2; ++s) - { - for(uint8 c = 0; c < 3; ++c) - { - best_colors[s][c] = current_colors[s][c]; - } - } - } - if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) - { - // All colors end up in the same voroni region. We need to reseed. - continue_iterate = false; - continue_seeding = true; - } - else - { - // Calculate new reconstruction points using the centroids - - // Find new construction values from average - t_color[0][R] = 0; - t_color[0][G] = 0; - t_color[0][B] = 0; - t_color[1][R] = 0; - t_color[1][G] = 0; - t_color[1][B] = 0; - - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - // use dummy value for q-parameter - t_color[block_mask[x][y]][R] += original_colors[x][y][R]; - t_color[block_mask[x][y]][G] += original_colors[x][y][G]; - t_color[block_mask[x][y]][B] += original_colors[x][y][B]; - } - } - current_colors[0][R] = t_color[0][R] / n; - current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][G] = t_color[0][G] / n; - current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][B] = t_color[0][B] / n; - current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); - } - } - } - - // Set the best colors as the final block colors - for(int s = 0; s < 2; ++s) - { - for(uint8 c = 0; c < 3; ++c) - { - current_colors[s][c] = best_colors[s][c]; - } - } - - for(x=0;x<2;x++) - for(y=0;y<3;y++) - LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); -} - -// Calculation of the two block colors using the LBG-algorithm -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void computeColorLBGfast(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) -{ - uint8 block_mask[4][4]; - - // reset rand so that we get predictable output per block - srand(10000); - //LBG-algorithm - double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; - double error_a, error_b; - int number_of_iterations = 10; - double t_color[2][3]; - uint8 original_colors[4][4][3]; - double current_colors[2][3]; - double best_colors[2][3]; - double max_v[3]; - double min_v[3]; - int x,y,i; - bool continue_seeding; - int maximum_number_of_seedings = 10; - int seeding; - bool continue_iterate; - - max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; - min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; - - // resolve trainingdata - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (x = 0; x < BLOCKWIDTH; ++x) - { - original_colors[x][y][R] = img[3*((starty+y)*width+startx+x)+R]; - original_colors[x][y][G] = img[3*((starty+y)*width+startx+x)+G]; - original_colors[x][y][B] = img[3*((starty+y)*width+startx+x)+B]; - - // find max - if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; - // find min - if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; - if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; - if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; - } - } - - D = 512*512*3*16.0; - bestD = 512*512*3*16.0; - - continue_seeding = true; - - // loop seeds - for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) - { - // hopefully we will not need more seedings: - continue_seeding = false; - - // calculate seeds - for (uint8 s = 0; s < 2; ++s) - { - for (uint8 c = 0; c < 3; ++c) - { - current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; - } - } - - // divide into two quantization sets and calculate distortion - continue_iterate = true; - for(i = 0; (i < number_of_iterations) && continue_iterate; i++) - { - oldD = D; - D = 0; - int n = 0; - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - error_a = SQUARE(original_colors[x][y][R] - JAS_ROUND(current_colors[0][R])) + - SQUARE(original_colors[x][y][G] - JAS_ROUND(current_colors[0][G])) + - SQUARE(original_colors[x][y][B] - JAS_ROUND(current_colors[0][B])); - error_b = SQUARE(original_colors[x][y][R] - JAS_ROUND(current_colors[1][R])) + - SQUARE(original_colors[x][y][G] - JAS_ROUND(current_colors[1][G])) + - SQUARE(original_colors[x][y][B] - JAS_ROUND(current_colors[1][B])); - if (error_a < error_b) - { - block_mask[x][y] = 0; - D += error_a; - ++n; - } - else - { - block_mask[x][y] = 1; - D += error_b; - } - } - } - - // compare with old distortion - if (D == 0) - { - // Perfect score -- we dont need to go further iterations. - continue_iterate = false; - continue_seeding = false; - } - if (D == oldD) - { - // Same score as last round -- no need to go for further iterations. - continue_iterate = false; - continue_seeding = false; - } - if (D < bestD) - { - bestD = D; - for(uint8 s = 0; s < 2; ++s) - { - for(uint8 c = 0; c < 3; ++c) - { - best_colors[s][c] = current_colors[s][c]; - } - } - } - if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) - { - // All colors end up in the same voroni region. We need to reseed. - continue_iterate = false; - continue_seeding = true; - } - else - { - // Calculate new reconstruction points using the centroids - - // Find new construction values from average - t_color[0][R] = 0; - t_color[0][G] = 0; - t_color[0][B] = 0; - t_color[1][R] = 0; - t_color[1][G] = 0; - t_color[1][B] = 0; - - for (y = 0; y < BLOCKHEIGHT; ++y) - { - for (int x = 0; x < BLOCKWIDTH; ++x) - { - // use dummy value for q-parameter - t_color[block_mask[x][y]][R] += original_colors[x][y][R]; - t_color[block_mask[x][y]][G] += original_colors[x][y][G]; - t_color[block_mask[x][y]][B] += original_colors[x][y][B]; - } - } - current_colors[0][R] = t_color[0][R] / n; - current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][G] = t_color[0][G] / n; - current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); - current_colors[0][B] = t_color[0][B] / n; - current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); - } - } - } - - // Set the best colors as the final block colors - for(int s = 0; s < 2; ++s) - { - for(uint8 c = 0; c < 3; ++c) - { - current_colors[s][c] = best_colors[s][c]; - } - } - - for(x=0;x<2;x++) - for(y=0;y<3;y++) - LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); -} - -// Each color component is compressed to fit in its specified number of bits -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressColor(int R_B, int G_B, int B_B, uint8 (current_color)[2][3], uint8 (quantized_color)[2][3]) -{ - // - // The color is calculated as: - // - // c = (c + (2^(8-b))/2) / (255 / (2^b - 1)) where b is the number of bits - // to code color c with - // For instance, if b = 3: - // - // c = (c + 16) / (255 / 7) = 7 * (c + 16) / 255 - // - - quantized_color[0][R] = CLAMP(0,(BINPOW(R_B)-1) * (current_color[0][R] + BINPOW(8-R_B-1)) / 255,255); - quantized_color[0][G] = CLAMP(0,(BINPOW(G_B)-1) * (current_color[0][G] + BINPOW(8-G_B-1)) / 255,255); - quantized_color[0][B] = CLAMP(0,(BINPOW(B_B)-1) * (current_color[0][B] + BINPOW(8-B_B-1)) / 255,255); - - quantized_color[1][R] = CLAMP(0,(BINPOW(R_B)-1) * (current_color[1][R] + BINPOW(8-R_B-1)) / 255,255); - quantized_color[1][G] = CLAMP(0,(BINPOW(G_B)-1) * (current_color[1][G] + BINPOW(8-G_B-1)) / 255,255); - quantized_color[1][B] = CLAMP(0,(BINPOW(B_B)-1) * (current_color[1][B] + BINPOW(8-B_B-1)) / 255,255); -} - -// Swapping two RGB-colors -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void swapColors(uint8 (colors)[2][3]) -{ - uint8 temp = colors[0][R]; - colors[0][R] = colors[1][R]; - colors[1][R] = temp; - - temp = colors[0][G]; - colors[0][G] = colors[1][G]; - colors[1][G] = temp; - - temp = colors[0][B]; - colors[0][B] = colors[1][B]; - colors[1][B] = temp; -} - - -// Calculate the paint colors from the block colors -// using a distance d and one of the H- or T-patterns. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. - -// Calculate the error for the block at position (startx,starty) -// The parameters needed for reconstruction are calculated as well -// -// Please note that the function can change the order between the two colors in colorsRGB444 -// -// In the 59T bit mode, we only have pattern T. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateError59Tperceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) -{ - - unsigned int block_error = 0, - best_block_error = MAXERR1000, - pixel_error, - best_pixel_error; - int diff[3]; - uint8 best_sw; - unsigned int pixel_colors; - uint8 colors[2][3]; - uint8 possible_colors[4][3]; - - // First use the colors as they are, then swap them - for (uint8 sw = 0; sw <2; ++sw) - { - if (sw == 1) - { - swapColors(colorsRGB444); - } - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) - { - calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXERR1000; - pixel_colors <<=2; // Make room for next value - - // Loop possible block colors - for (uint8 c = 0; c < 4; ++c) - { - - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); - - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + - PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]) + - PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*SQUARE(diff[B]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - pixel_colors ^= (pixel_colors & 3); // Reset the two first bits - pixel_colors |= c; - } - } - block_error += best_pixel_error; - } - } - if (block_error < best_block_error) - { - best_block_error = block_error; - distance = d; - pixel_indices = pixel_colors; - best_sw = sw; - } - } - - if (sw == 1 && best_sw == 0) - { - swapColors(colorsRGB444); - } - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - } - return best_block_error; -} - -// Calculate the error for the block at position (startx,starty) -// The parameters needed for reconstruction is calculated as well -// -// Please note that the function can change the order between the two colors in colorsRGB444 -// -// In the 59T bit mode, we only have pattern T. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calculateError59T(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) -{ - double block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - int diff[3]; - uint8 best_sw; - unsigned int pixel_colors; - uint8 colors[2][3]; - uint8 possible_colors[4][3]; - - // First use the colors as they are, then swap them - for (uint8 sw = 0; sw <2; ++sw) - { - if (sw == 1) - { - swapColors(colorsRGB444); - } - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) - { - calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXIMUM_ERROR; - pixel_colors <<=2; // Make room for next value - - // Loop possible block colors - for (uint8 c = 0; c < 4; ++c) - { - - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); - - pixel_error = weight[R]*SQUARE(diff[R]) + - weight[G]*SQUARE(diff[G]) + - weight[B]*SQUARE(diff[B]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - pixel_colors ^= (pixel_colors & 3); // Reset the two first bits - pixel_colors |= c; - } - } - block_error += best_pixel_error; - } - } - if (block_error < best_block_error) - { - best_block_error = block_error; - distance = d; - pixel_indices = pixel_colors; - best_sw = sw; - } - } - - if (sw == 1 && best_sw == 0) - { - swapColors(colorsRGB444); - } - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - } - return best_block_error; -} - -// Calculate the error for the block at position (startx,starty) -// The parameters needed for reconstruction is calculated as well -// -// In the 59T bit mode, we only have pattern T. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateError59TnoSwapPerceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) -{ - - unsigned int block_error = 0, - best_block_error = MAXERR1000, - pixel_error, - best_pixel_error; - int diff[3]; - unsigned int pixel_colors; - uint8 colors[2][3]; - uint8 possible_colors[4][3]; - int thebestintheworld; - - // First use the colors as they are, then swap them - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) - { - calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXERR1000; - pixel_colors <<=2; // Make room for next value - - // Loop possible block colors - for (uint8 c = 0; c < 4; ++c) - { - - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); - - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + - PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]) + - PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*SQUARE(diff[B]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - pixel_colors ^= (pixel_colors & 3); // Reset the two first bits - pixel_colors |= c; - thebestintheworld = c; - } - } - block_error += best_pixel_error; - } - } - if (block_error < best_block_error) - { - best_block_error = block_error; - distance = d; - pixel_indices = pixel_colors; - } - } - - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - return best_block_error; -} - -// Calculate the error for the block at position (startx,starty) -// The parameters needed for reconstruction is calculated as well -// -// In the 59T bit mode, we only have pattern T. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calculateError59TnoSwap(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) -{ - double block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - int diff[3]; - unsigned int pixel_colors; - uint8 colors[2][3]; - uint8 possible_colors[4][3]; - int thebestintheworld; - - // First use the colors as they are, then swap them - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) - { - calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXIMUM_ERROR; - pixel_colors <<=2; // Make room for next value - - // Loop possible block colors - for (uint8 c = 0; c < 4; ++c) - { - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); - - pixel_error = weight[R]*SQUARE(diff[R]) + - weight[G]*SQUARE(diff[G]) + - weight[B]*SQUARE(diff[B]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - pixel_colors ^= (pixel_colors & 3); // Reset the two first bits - pixel_colors |= c; - thebestintheworld = c; - } - } - block_error += best_pixel_error; - } - } - if (block_error < best_block_error) - { - best_block_error = block_error; - distance = d; - pixel_indices = pixel_colors; - } - } - - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - return best_block_error; -} - -// Put the compress params into the compression block -// -// -//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| -//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void packBlock59T(uint8 (colors)[2][3], uint8 d, unsigned int pixel_indices, unsigned int &compressed1, unsigned int &compressed2) -{ - - compressed1 = 0; - - PUTBITSHIGH( compressed1, colors[0][R], 4, 58); - PUTBITSHIGH( compressed1, colors[0][G], 4, 54); - PUTBITSHIGH( compressed1, colors[0][B], 4, 50); - PUTBITSHIGH( compressed1, colors[1][R], 4, 46); - PUTBITSHIGH( compressed1, colors[1][G], 4, 42); - PUTBITSHIGH( compressed1, colors[1][B], 4, 38); - PUTBITSHIGH( compressed1, d, TABLE_BITS_59T, 34); - pixel_indices=indexConversion(pixel_indices); - compressed2 = 0; - PUTBITS( compressed2, pixel_indices, 32, 31); -} - -// Copy colors from source to dest -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void copyColors(uint8 (source)[2][3], uint8 (dest)[2][3]) -{ - int x,y; - - for (x=0; x<2; x++) - for (y=0; y<3; y++) - dest[x][y] = source[x][y]; -} - -// The below code should compress the block to 59 bits. -// -//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| -//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockTHUMB59TFastestOnlyColorPerceptual1000(uint8 *img,int width,int height,int startx,int starty, int (best_colorsRGB444_packed)[2]) -{ - unsigned int best_error = MAXERR1000; - unsigned int best_pixel_indices; - uint8 best_distance; - - unsigned int error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm - computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); - compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); - - // Determine the parameters for the lowest error - error_no_i = calculateError59Tperceptual1000(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - - best_colorsRGB444_packed[0] = (colorsRGB444_no_i[0][0] << 8) + (colorsRGB444_no_i[0][1] << 4) + (colorsRGB444_no_i[0][2] << 0); - best_colorsRGB444_packed[1] = (colorsRGB444_no_i[1][0] << 8) + (colorsRGB444_no_i[1][1] << 4) + (colorsRGB444_no_i[1][2] << 0); - - return best_error; -} - - -// The below code should compress the block to 59 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// -//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| -//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double compressBlockTHUMB59TFastestOnlyColor(uint8 *img,int width,int height,int startx,int starty, int (best_colorsRGB444_packed)[2]) -{ - double best_error = MAXIMUM_ERROR; - unsigned int best_pixel_indices; - uint8 best_distance; - - double error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm - computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); - compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); - - // Determine the parameters for the lowest error - error_no_i = calculateError59T(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - - best_colorsRGB444_packed[0] = (colorsRGB444_no_i[0][0] << 8) + (colorsRGB444_no_i[0][1] << 4) + (colorsRGB444_no_i[0][2] << 0); - best_colorsRGB444_packed[1] = (colorsRGB444_no_i[1][0] << 8) + (colorsRGB444_no_i[1][1] << 4) + (colorsRGB444_no_i[1][2] << 0); - - return best_error; -} - -// The below code should compress the block to 59 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// -//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| -//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double compressBlockTHUMB59TFastestPerceptual1000(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - double best_error = MAXIMUM_ERROR; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - double error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm - computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); - compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); - - // Determine the parameters for the lowest error - error_no_i = calculateError59Tperceptual1000(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - copyColors(colorsRGB444_no_i, best_colorsRGB444); - - // Put the compress params into the compression block - packBlock59T(best_colorsRGB444, best_distance, best_pixel_indices, compressed1, compressed2); - - return best_error; -} - -// The below code should compress the block to 59 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// -//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| -//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double compressBlockTHUMB59TFastest(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - double best_error = MAXIMUM_ERROR; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - double error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm - computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); - compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); - - // Determine the parameters for the lowest error - error_no_i = calculateError59T(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - copyColors(colorsRGB444_no_i, best_colorsRGB444); - - // Put the compress params into the compression block - packBlock59T(best_colorsRGB444, best_distance, best_pixel_indices, compressed1, compressed2); - - return best_error; -} - -// The below code should compress the block to 59 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// -//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| -//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double compressBlockTHUMB59TFast(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - double best_error = MAXIMUM_ERROR; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - double error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - - double error_half_i; - uint8 colorsRGB444_half_i[2][3]; - unsigned int pixel_indices_half_i; - uint8 distance_half_i; - - double error; - uint8 colorsRGB444[2][3]; - unsigned int pixel_indices; - uint8 distance; - - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm - computeColorLBGNotIntensityFast(img,width,startx,starty, colors); - compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); - // Determine the parameters for the lowest error - error_no_i = calculateError59T(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - // Calculate average color using the LBG-algorithm - computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); - compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_half_i); - // Determine the parameters for the lowest error - error_half_i = calculateError59T(img, width, startx, starty, colorsRGB444_half_i, distance_half_i, pixel_indices_half_i); - - // Calculate average color using the LBG-algorithm - computeColorLBGfast(img,width,startx,starty, colors); - compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444); - // Determine the parameters for the lowest error - error = calculateError59T(img, width, startx, starty, colorsRGB444, distance, pixel_indices); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - copyColors(colorsRGB444_no_i, best_colorsRGB444); - - if(error_half_i < best_error) - { - best_error = error_half_i; - best_distance = distance_half_i; - best_pixel_indices = pixel_indices_half_i; - copyColors (colorsRGB444_half_i, best_colorsRGB444); - } - if(error < best_error) - { - best_error = error; - best_distance = distance; - best_pixel_indices = pixel_indices; - copyColors (colorsRGB444, best_colorsRGB444); - } - - // Put the compress params into the compression block - packBlock59T(best_colorsRGB444, best_distance, best_pixel_indices, compressed1, compressed2); - - return best_error; -} - -// Calculate the error for the block at position (startx,starty) -// The parameters needed for reconstruction is calculated as well -// -// In the 58H bit mode, we only have pattern H. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateErrorAndCompress58Hperceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) -{ - unsigned int block_error = 0, - best_block_error = MAXERR1000, - pixel_error, - best_pixel_error; - int diff[3]; - unsigned int pixel_colors; - uint8 possible_colors[4][3]; - uint8 colors[2][3]; - - decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) - { - calculatePaintColors58H(d, PATTERN_H, colors, possible_colors); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXERR1000; - pixel_colors <<=2; // Make room for next value - - // Loop possible block colors - for (uint8 c = 0; c < 4; ++c) - { - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); - - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + - PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]) + - PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*SQUARE(diff[B]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - pixel_colors ^= (pixel_colors & 3); // Reset the two first bits - pixel_colors |= c; - } - } - block_error += best_pixel_error; - } - } - - if (block_error < best_block_error) - { - best_block_error = block_error; - distance = d; - pixel_indices = pixel_colors; - } - } - return best_block_error; -} - -// The H-mode but with punchthrough alpha -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calculateErrorAndCompress58HAlpha(uint8* srcimg, uint8* alphaimg,int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) -{ - double block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - int diff[3]; - unsigned int pixel_colors; - uint8 possible_colors[4][3]; - uint8 colors[2][3]; - int alphaindex; - int colorsRGB444_packed[2]; - colorsRGB444_packed[0] = (colorsRGB444[0][R] << 8) + (colorsRGB444[0][G] << 4) + colorsRGB444[0][B]; - colorsRGB444_packed[1] = (colorsRGB444[1][R] << 8) + (colorsRGB444[1][G] << 4) + colorsRGB444[1][B]; - - decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) - { - alphaindex=2; - if( (colorsRGB444_packed[0] >= colorsRGB444_packed[1]) ^ ((d & 1)==1) ) - { - //we're going to have to swap the colors to be able to choose this distance.. that means - //that the indices will be swapped as well, so C1 will be the one with alpha instead of C3.. - alphaindex=0; - } - - calculatePaintColors58H(d, PATTERN_H, colors, possible_colors); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - int alpha=0; - if(alphaimg[((starty+y)*width+startx+x)]>0) - alpha=1; - if(alphaimg[((starty+y)*width+startx+x)]>0&&alphaimg[((starty+y)*width+startx+x)]<255) - printf("INVALID ALPHA DATA!!\n"); - best_pixel_error = MAXIMUM_ERROR; - pixel_colors <<=2; // Make room for next value - - // Loop possible block colors - for (uint8 c = 0; c < 4; ++c) - { - if(c==alphaindex&&alpha) - { - pixel_error=0; - } - else if(c==alphaindex||alpha) - { - pixel_error=MAXIMUM_ERROR; - } - else - { - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); - - pixel_error = weight[R]*SQUARE(diff[R]) + - weight[G]*SQUARE(diff[G]) + - weight[B]*SQUARE(diff[B]); - } - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - pixel_colors ^= (pixel_colors & 3); // Reset the two first bits - pixel_colors |= c; - } - } - block_error += best_pixel_error; - } - } - if (block_error < best_block_error) - { - best_block_error = block_error; - distance = d; - pixel_indices = pixel_colors; - } - } - return best_block_error; -} - -// Calculate the error for the block at position (startx,starty) -// The parameters needed for reconstruction is calculated as well -// -// In the 58H bit mode, we only have pattern H. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calculateErrorAndCompress58H(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) -{ - double block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - int diff[3]; - unsigned int pixel_colors; - uint8 possible_colors[4][3]; - uint8 colors[2][3]; - - - decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) - { - calculatePaintColors58H(d, PATTERN_H, colors, possible_colors); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXIMUM_ERROR; - pixel_colors <<=2; // Make room for next value - - // Loop possible block colors - for (uint8 c = 0; c < 4; ++c) - { - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); - - pixel_error = weight[R]*SQUARE(diff[R]) + - weight[G]*SQUARE(diff[G]) + - weight[B]*SQUARE(diff[B]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - pixel_colors ^= (pixel_colors & 3); // Reset the two first bits - pixel_colors |= c; - } - } - block_error += best_pixel_error; - } - } - - if (block_error < best_block_error) - { - best_block_error = block_error; - distance = d; - pixel_indices = pixel_colors; - } - } - - return best_block_error; -} - -// Makes sure that col0 < col1; -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void sortColorsRGB444(uint8 (colorsRGB444)[2][3]) -{ - unsigned int col0, col1, tcol; - - // sort colors - col0 = 16*16*colorsRGB444[0][R] + 16*colorsRGB444[0][G] + colorsRGB444[0][B]; - col1 = 16*16*colorsRGB444[1][R] + 16*colorsRGB444[1][G] + colorsRGB444[1][B]; - - // After this, col0 should be smaller than col1 (col0 < col1) - if( col0 > col1) - { - tcol = col0; - col0 = col1; - col1 = tcol; - } - else - { - if(col0 == col1) - { - // Both colors are the same. That is useless. If they are both black, - // col1 can just as well be (0,0,1). Else, col0 can be col1 - 1. - if(col0 == 0) - col1 = col0+1; - else - col0 = col1-1; - } - } - - colorsRGB444[0][R] = GETBITS(col0, 4, 11); - colorsRGB444[0][G] = GETBITS(col0, 4, 7); - colorsRGB444[0][B] = GETBITS(col0, 4, 3); - colorsRGB444[1][R] = GETBITS(col1, 4, 11); - colorsRGB444[1][G] = GETBITS(col1, 4, 7); - colorsRGB444[1][B] = GETBITS(col1, 4, 3); -} - -// The below code should compress the block to 58 bits. -// The bit layout is thought to be: -// -//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| -//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. -// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. -// Else, it is assumed to be 1. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockTHUMB58HFastestPerceptual1000(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - unsigned int best_error = MAXERR1000; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - unsigned int error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm but discarding the intensity in the error function - computeColorLBGHalfIntensityFast(img, width, startx, starty, colors); - compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_no_i); - sortColorsRGB444(colorsRGB444_no_i); - - error_no_i = calculateErrorAndCompress58Hperceptual1000(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - copyColors(colorsRGB444_no_i, best_colorsRGB444); - - // | col0 >= col1 col0 < col1 - //------------------------------------------------------ - // (dist & 1) = 1 | no need to swap | need to swap - // |-----------------+---------------- - // (dist & 1) = 0 | need to swap | no need to swap - // - // This can be done with an xor test. - - int best_colorsRGB444_packed[2]; - best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; - best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; - if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) - { - swapColors(best_colorsRGB444); - - // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 - best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); - } - - // Put the compress params into the compression block - - compressed1 = 0; - - PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); - PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); - - compressed2 = 0; - best_pixel_indices=indexConversion(best_pixel_indices); - PUTBITS( compressed2, best_pixel_indices, 32, 31); - - return best_error; -} - -// The below code should compress the block to 58 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// The bit layout is thought to be: -// -//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| -//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. -// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. -// Else, it is assumed to be 1. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double compressBlockTHUMB58HFastest(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - double best_error = MAXIMUM_ERROR; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - double error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm but discarding the intensity in the error function - computeColorLBGHalfIntensityFast(img, width, startx, starty, colors); - compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_no_i); - sortColorsRGB444(colorsRGB444_no_i); - - error_no_i = calculateErrorAndCompress58H(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - copyColors(colorsRGB444_no_i, best_colorsRGB444); - - // | col0 >= col1 col0 < col1 - //------------------------------------------------------ - // (dist & 1) = 1 | no need to swap | need to swap - // |-----------------+---------------- - // (dist & 1) = 0 | need to swap | no need to swap - // - // This can be done with an xor test. - - int best_colorsRGB444_packed[2]; - best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; - best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; - if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) - { - swapColors(best_colorsRGB444); - - // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 - best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); - } - - // Put the compress params into the compression block - - compressed1 = 0; - - PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); - PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); - best_pixel_indices=indexConversion(best_pixel_indices); - compressed2 = 0; - PUTBITS( compressed2, best_pixel_indices, 32, 31); - - return best_error; -} - -//same as above, but with 1-bit alpha -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double compressBlockTHUMB58HAlpha(uint8 *img, uint8* alphaimg, int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - double best_error = MAXIMUM_ERROR; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - double error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm but discarding the intensity in the error function - computeColorLBGHalfIntensityFast(img, width, startx, starty, colors); - compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_no_i); - sortColorsRGB444(colorsRGB444_no_i); - - error_no_i = calculateErrorAndCompress58HAlpha(img, alphaimg,width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - copyColors(colorsRGB444_no_i, best_colorsRGB444); - - // | col0 >= col1 col0 < col1 - //------------------------------------------------------ - // (dist & 1) = 1 | no need to swap | need to swap - // |-----------------+---------------- - // (dist & 1) = 0 | need to swap | no need to swap - // - // This can be done with an xor test. - - int best_colorsRGB444_packed[2]; - best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; - best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; - if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) - { - swapColors(best_colorsRGB444); - - // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 - best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); - } - - // Put the compress params into the compression block - - compressed1 = 0; - - PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); - PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); - best_pixel_indices=indexConversion(best_pixel_indices); - compressed2 = 0; - PUTBITS( compressed2, best_pixel_indices, 32, 31); - - return best_error; -} - -// The below code should compress the block to 58 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// The bit layout is thought to be: -// -//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| -//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. -// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. -// Else, it is assumed to be 1. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double compressBlockTHUMB58HFast(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - double best_error = MAXIMUM_ERROR; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - double error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - - double error_half_i; - uint8 colorsRGB444_half_i[2][3]; - unsigned int pixel_indices_half_i; - uint8 distance_half_i; - - double error; - uint8 colorsRGB444[2][3]; - unsigned int pixel_indices; - uint8 distance; - - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm but discarding the intensity in the error function - computeColorLBGNotIntensity(img, width, startx, starty, colors); - compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_no_i); - sortColorsRGB444(colorsRGB444_no_i); - error_no_i = calculateErrorAndCompress58H(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - // Calculate average color using the LBG-algorithm but halfing the influence of the intensity in the error function - computeColorLBGNotIntensity(img, width, startx, starty, colors); - compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_half_i); - sortColorsRGB444(colorsRGB444_half_i); - error_half_i = calculateErrorAndCompress58H(img, width, startx, starty, colorsRGB444_half_i, distance_half_i, pixel_indices_half_i); - - // Calculate average color using the LBG-algorithm - computeColorLBG(img, width, startx, starty, colors); - compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444); - sortColorsRGB444(colorsRGB444); - error = calculateErrorAndCompress58H(img, width, startx, starty, colorsRGB444, distance, pixel_indices); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - copyColors(colorsRGB444_no_i, best_colorsRGB444); - - if(error_half_i < best_error) - { - best_error = error_half_i; - best_distance = distance_half_i; - best_pixel_indices = pixel_indices_half_i; - copyColors(colorsRGB444_half_i, best_colorsRGB444); - } - - if(error < best_error) - { - best_error = error; - best_distance = distance; - best_pixel_indices = pixel_indices; - copyColors(colorsRGB444, best_colorsRGB444); - } - - // | col0 >= col1 col0 < col1 - //------------------------------------------------------ - // (dist & 1) = 1 | no need to swap | need to swap - // |-----------------+---------------- - // (dist & 1) = 0 | need to swap | no need to swap - // - // This can be done with an xor test. - - int best_colorsRGB444_packed[2]; - best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; - best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; - if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) - { - swapColors(best_colorsRGB444); - - // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 - best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); - } - - // Put the compress params into the compression block - compressed1 = 0; - - PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); - PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); - best_pixel_indices=indexConversion(best_pixel_indices); - compressed2 = 0; - PUTBITS( compressed2, best_pixel_indices, 32, 31); - - return best_error; -} - -// Compress block testing both individual and differential mode. -// Perceptual error metric. -// Combined quantization for colors. -// Both flipped and unflipped tested. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockDiffFlipCombinedPerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - - unsigned int compressed1_norm, compressed2_norm; - unsigned int compressed1_flip, compressed2_flip; - uint8 avg_color_quant1[3], avg_color_quant2[3]; - - float avg_color_float1[3],avg_color_float2[3]; - int enc_color1[3], enc_color2[3], diff[3]; - int min_error=255*255*8*3; - unsigned int best_table_indices1=0, best_table_indices2=0; - unsigned int best_table1=0, best_table2=0; - int diffbit; - - int norm_err=0; - int flip_err=0; - - // First try normal blocks 2x4: - - computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - - float eps; - - uint8 dummy[3]; - - quantize555ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); - quantize555ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) - { - diffbit = 1; - - // The difference to be coded: - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); - PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); - PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - norm_err = 0; - - // left part of block - norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - } - else - { - diffbit = 0; - // The difference is bigger than what fits in 555 plus delta-333, so we will have - // to deal with 444 444. - - eps = (float) 0.0001; - - quantize444ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); - quantize444ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); - - avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; - avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; - avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; - avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; - avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; - avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; - - // Pack bits into the first word. - - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - compressed1_norm = 0; - PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); - PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // left part of block - norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - // right part of block - norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); - PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); - PUTBITSHIGH( compressed1_norm, 0, 1, 32); - - compressed2_norm = 0; - PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); - } - - // Now try flipped blocks 4x2: - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); - computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); - - // First test if avg_color1 is similar enough to avg_color2 so that - // we can use differential coding of colors. - quantize555ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); - quantize555ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); - - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) - { - diffbit = 1; - - // The difference to be coded: - diff[0] = enc_color2[0]-enc_color1[0]; - diff[1] = enc_color2[1]-enc_color1[1]; - diff[2] = enc_color2[2]-enc_color1[2]; - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); - PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); - PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // upper part of block - flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - else - { - diffbit = 0; - // The difference is bigger than what fits in 555 plus delta-333, so we will have - // to deal with 444 444. - eps = (float) 0.0001; - - quantize444ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); - quantize444ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); - - avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; - avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; - avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; - avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; - avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; - avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; - - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - - // Pack bits into the first word. - compressed1_flip = 0; - PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); - PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); - PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); - PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); - PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); - PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); - PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); - - unsigned int best_pixel_indices1_MSB; - unsigned int best_pixel_indices1_LSB; - unsigned int best_pixel_indices2_MSB; - unsigned int best_pixel_indices2_LSB; - - // upper part of block - flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - // lower part of block - flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); - PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); - PUTBITSHIGH( compressed1_flip, 1, 1, 32); - - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - - compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - - // Now lets see which is the best table to use. Only 8 tables are possible. - if(norm_err <= flip_err) - { - compressed1 = compressed1_norm | 0; - compressed2 = compressed2_norm; - } - else - { - compressed1 = compressed1_flip | 1; - compressed2 = compressed2_flip; - } -} - -// Calculate the error of a block -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calcBlockErrorRGB(uint8 *img, uint8 *imgdec, int width, int height, int startx, int starty) -{ - int xx,yy; - double err; - - err = 0; - - for(xx = startx; xx< startx+4; xx++) - { - for(yy = starty; yy3) - diff[c]=3; - enc_color2[c]=enc_color1[c]+diff[c]; - } - - avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); - avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); - avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); - - // Pack bits into the first word. - // see regular compressblockdiffflipfast for details - - compressed1_temp = 0; - PUTBITSHIGH( compressed1_temp, !isTransparent, 1, 33); - PUTBITSHIGH( compressed1_temp, enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1_temp, enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1_temp, enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1_temp, diff[0], 3, 58); - PUTBITSHIGH( compressed1_temp, diff[1], 3, 50); - PUTBITSHIGH( compressed1_temp, diff[2], 3, 42); - - temp_err = 0; - - int besterror[2]; - besterror[0]=255*255*3*16; - besterror[1]=255*255*3*16; - int besttable[2]; - int best_indices_LSB[16]; - int best_indices_MSB[16]; - //for each table, we're going to compute the indices required to get minimum error in each half. - //then we'll check if this was the best table for either half, and set besterror/besttable accordingly. - for(int table=0; table<8; table++) - { - int taberror[2];//count will be sort of an index of each pixel within a half, determining where the index will be placed in the bitstream. - - int pixel_indices_LSB[16],pixel_indices_MSB[16]; - - for(int i=0; i<2; i++) - { - taberror[i]=0; - } - for(int x=0; x<4; x++) - { - for(int y=0; y<4; y++) - { - int index = x+startx+(y+starty)*width; - uint8 basecol[3]; - bool transparentPixel=alphaimg[index]<128; - //determine which half of the block this pixel is in, based on the flipbit. - int half=0; - if( (flipbit==0&&x<2) || (flipbit&&y<2) ) - { - basecol[0]=avg_color_quant1[0]; - basecol[1]=avg_color_quant1[1]; - basecol[2]=avg_color_quant1[2]; - } - else - { - half=1; - basecol[0]=avg_color_quant2[0]; - basecol[1]=avg_color_quant2[1]; - basecol[2]=avg_color_quant2[2]; - } - int besterri=255*255*3*2; - int besti=0; - int erri; - for(int i=0; i<4; i++) - { - if(i==1&&isTransparent) - continue; - erri=0; - for(int c=0; c<3; c++) - { - int col=CLAMP(0,((int)basecol[c])+compressParams[table*2][i],255); - if(i==2&&isTransparent) - { - col=(int)basecol[c]; - } - int errcol=col-((int)(img[index*3+c])); - erri=erri+(errcol*errcol); - } - if(erri> 1); - pixel_indices_LSB[x*4+y]=(pixel_index & 1); - } - } - for(int half=0; half<2; half++) - { - if(taberror[half] 128) -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calcBlockErrorRGBA(uint8 *img, uint8 *imgdec, uint8* alpha, int width, int height, int startx, int starty) -{ - int xx,yy; - double err; - - err = 0; - - for(xx = startx; xx< startx+4; xx++) - { - for(yy = starty; yy128) - { - err += SQUARE(1.0*RED(img,width,xx,yy) - 1.0*RED(imgdec, width, xx,yy)); - err += SQUARE(1.0*GREEN(img,width,xx,yy)- 1.0*GREEN(imgdec, width, xx,yy)); - err += SQUARE(1.0*BLUE(img,width,xx,yy) - 1.0*BLUE(imgdec, width, xx,yy)); - } - } - } - return err; -} - -//calculates the error for a block using the given colors, and the paremeters required to obtain the error. This version uses 1-bit punch-through alpha. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calculateError59TAlpha(uint8* srcimg, uint8* alpha,int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) -{ - - double block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - int diff[3]; - uint8 best_sw; - unsigned int pixel_colors; - uint8 colors[2][3]; - uint8 possible_colors[4][3]; - - // First use the colors as they are, then swap them - for (uint8 sw = 0; sw <2; ++sw) - { - if (sw == 1) - { - swapColors(colorsRGB444); - } - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) - { - calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXIMUM_ERROR; - pixel_colors <<=2; // Make room for next value - - // Loop possible block colors - if(alpha[x+startx+(y+starty)*width]==0) - { - best_pixel_error=0; - pixel_colors ^= (pixel_colors & 3); // Reset the two first bits - pixel_colors |= 2; //insert the index for this pixel, two meaning transparent. - } - else - { - for (uint8 c = 0; c < 4; ++c) - { - - if(c==2) - continue; //don't use this, because we don't have alpha here and index 2 means transparent. - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); - - pixel_error = weight[R]*SQUARE(diff[R]) + - weight[G]*SQUARE(diff[G]) + - weight[B]*SQUARE(diff[B]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - pixel_colors ^= (pixel_colors & 3); // Reset the two first bits - pixel_colors |= c; //insert the index for this pixel - } - } - } - block_error += best_pixel_error; - } - } - if (block_error < best_block_error) - { - best_block_error = block_error; - distance = d; - pixel_indices = pixel_colors; - best_sw = sw; - } - } - - if (sw == 1 && best_sw == 0) - { - swapColors(colorsRGB444); - } - decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); - } - return best_block_error; -} - -// same as fastest t-mode compressor above, but here one of the colors (the central one in the T) is used to also signal that the pixel is transparent. -// the only difference is that calculateError has been swapped out to one that considers alpha. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double compressBlockTHUMB59TAlpha(uint8 *img, uint8* alpha, int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - double best_error = MAXIMUM_ERROR; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - double error_no_i; - uint8 colorsRGB444_no_i[2][3]; - unsigned int pixel_indices_no_i; - uint8 distance_no_i; - - uint8 colors[2][3]; - - // Calculate average color using the LBG-algorithm - computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); - compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); - - // Determine the parameters for the lowest error - error_no_i = calculateError59TAlpha(img, alpha, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); - - best_error = error_no_i; - best_distance = distance_no_i; - best_pixel_indices = pixel_indices_no_i; - copyColors(colorsRGB444_no_i, best_colorsRGB444); - - // Put the compress params into the compression block - packBlock59T(best_colorsRGB444, best_distance, best_pixel_indices, compressed1, compressed2); - - return best_error; -} - -// Put bits in order for the format. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void stuff59bitsDiffFalse(unsigned int thumbT59_word1, unsigned int thumbT59_word2, unsigned int &thumbT_word1, unsigned int &thumbT_word2) -{ - // Put bits in twotimer configuration for 59 (red overflows) - // - // Go from this bit layout: - // - // |63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| - // |----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| - // - // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| - // |----------------------------------------index bits---------------------------------------------| - // - // - // To this: - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // |// // //|R0a |//|R0b |G0 |B0 |R1 |G1 |B1 |da |df|db| - // ----------------------------------------------------------------------------------------------- - // - // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| - // |----------------------------------------index bits---------------------------------------------| - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // ----------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt| - // ------------------------------------------------------------------------------------------------ - - uint8 R0a; - uint8 bit, a, b, c, d, bits; - - R0a = GETBITSHIGH( thumbT59_word1, 2, 58); - - // Fix middle part - thumbT_word1 = thumbT59_word1 << 1; - // Fix R0a (top two bits of R0) - PUTBITSHIGH( thumbT_word1, R0a, 2, 60); - // Fix db (lowest bit of d) - PUTBITSHIGH( thumbT_word1, thumbT59_word1, 1, 32); - // - // Make sure that red overflows: - a = GETBITSHIGH( thumbT_word1, 1, 60); - b = GETBITSHIGH( thumbT_word1, 1, 59); - c = GETBITSHIGH( thumbT_word1, 1, 57); - d = GETBITSHIGH( thumbT_word1, 1, 56); - // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 - // The following logical expression checks for the presence of any of those: - bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); - bits = 0xf*bit; - PUTBITSHIGH( thumbT_word1, bits, 3, 63); - PUTBITSHIGH( thumbT_word1, !bit, 1, 58); - - // Set diffbit - PUTBITSHIGH( thumbT_word1, 0, 1, 33); - thumbT_word2 = thumbT59_word2; -} - -// Tests if there is at least one pixel in the image which would get alpha = 0 in punchtrough mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -bool hasAlpha(uint8* alphaimg, int ix, int iy, int width) -{ - for(int x=ix; x> 8) & 0xff; - bytes[1] = (block >> 0) & 0xff; - - fwrite(&bytes[0],1,1,f); - fwrite(&bytes[1],1,1,f); -} - - -// Write a word in big endian style -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void write_big_endian_4byte_word(unsigned int *blockadr, FILE *f) -{ - uint8 bytes[4]; - unsigned int block; - - block = blockadr[0]; - - bytes[0] = (block >> 24) & 0xff; - bytes[1] = (block >> 16) & 0xff; - bytes[2] = (block >> 8) & 0xff; - bytes[3] = (block >> 0) & 0xff; - - fwrite(&bytes[0],1,1,f); - fwrite(&bytes[1],1,1,f); - fwrite(&bytes[2],1,1,f); - fwrite(&bytes[3],1,1,f); -} - -extern int alphaTable[256][8]; -extern int alphaBase[16][4]; - -// valtab holds precalculated data used for compressing using EAC2. -// Note that valtab is constructed using get16bits11bits, which means -// that it already is expanded to 16 bits. -// Note also that it its contents will depend on the value of formatSigned. -int *valtab; - -void setupAlphaTableAndValtab() -{ - setupAlphaTable(); - - //fix precomputation table..! - valtab = new int[1024*512]; - int16 val16; - int count=0; - for(int base=0; base<256; base++) - { - for(int tab=0; tab<16; tab++) - { - for(int mul=0; mul<16; mul++) - { - for(int index=0; index<8; index++) - { - if(formatSigned) - { - val16=get16bits11signed(base,tab,mul,index); - valtab[count] = val16 + 256*128; - } - else - valtab[count]=get16bits11bits(base,tab,mul,index); - count++; - } - } - } - } -} - -// Reads alpha data -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void readAlpha(uint8* &data, int &width, int &height, int &extendedwidth, int &extendedheight) -{ - //width and height are already known..? - uint8* tempdata; - int wantedBitDepth; - if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS||format==ETC2PACKAGE_RGBA1_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) - { - wantedBitDepth=8; - } - else if(format==ETC2PACKAGE_R_NO_MIPMAPS) - { - wantedBitDepth=16; - } - else - { - printf("invalid format for alpha reading!\n"); - exit(1); - } - fReadPGM("alpha.pgm",width,height,tempdata,wantedBitDepth); - extendedwidth=4*((width+3)/4); - extendedheight=4*((height+3)/4); - - if(width==extendedwidth&&height==extendedheight) - { - data=tempdata; - } - else - { - data = (uint8*)malloc(extendedwidth*extendedheight*wantedBitDepth/8); - uint8 last=0; - uint8 lastlast=0; - for(int x=0; xmaxdist) - maxdist=abs(alpha-data[ix+x+(iy+y)*width]); //maximum distance from average - } - } - int approxPos = (maxdist*255)/160-4; //experimentally derived formula for calculating approximate table position given a max distance from average - if(approxPos>255) - approxPos=255; - int startTable=approxPos-15; //first table to be tested - if(startTable<0) - startTable=0; - int endTable=clamp(approxPos+15); //last table to be tested - - int bestsum=1000000000; - int besttable=-3; - int bestalpha=128; - int prevalpha=alpha; - - //main loop: determine best base alpha value and offset table to use for compression - //try some different alpha tables. - for(int table = startTable; table0; table++) - { - int tablealpha=prevalpha; - int tablebestsum=1000000000; - //test some different alpha values, trying to find the best one for the given table. - for(int alphascale=16; alphascale>0; alphascale/=4) - { - int startalpha; - int endalpha; - if(alphascale==16) - { - startalpha = clamp(tablealpha-alphascale*4); - endalpha = clamp(tablealpha+alphascale*4); - } - else - { - startalpha = clamp(tablealpha-alphascale*2); - endalpha = clamp(tablealpha+alphascale*2); - } - for(alpha=startalpha; alpha<=endalpha; alpha+=alphascale) - { - int sum=0; - int val,diff,bestdiff=10000000,index; - for(int x=0; x<4; x++) - { - for(int y=0; y<4; y++) - { - //compute best offset here, add square difference to sum.. - val=data[ix+x+(iy+y)*width]; - bestdiff=1000000000; - //the values are always ordered from small to large, with the first 4 being negative and the last 4 positive - //search is therefore made in the order 0-1-2-3 or 7-6-5-4, stopping when error increases compared to the previous entry tested. - if(val>alpha) - { - for(index=7; index>3; index--) - { - diff=clamp_table[alpha+(int)(alphaTable[table][index])+255]-val; - diff*=diff; - if(diff<=bestdiff) - { - bestdiff=diff; - } - else - break; - } - } - else - { - for(index=0; index<4; index++) - { - diff=clamp_table[alpha+(int)(alphaTable[table][index])+255]-val; - diff*=diff; - if(diffbestsum) - { - x=9999; //just to make it large and get out of the x<4 loop - break; - } - } - } - if(sum7) - { - bit=0; - byte++; - } - } - } - } -} - -// Helper function for the below function -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int getPremulIndex(int base, int tab, int mul, int index) -{ - return (base<<11)+(tab<<7)+(mul<<3)+index; -} - -// Calculates the error used in compressBlockAlpha16() -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calcError(uint8* data, int ix, int iy, int width, int height, int base, int tab, int mul, double prevbest) -{ - int offset = getPremulIndex(base,tab,mul,0); - double error=0; - for (int y=0; y<4; y++) - { - for(int x=0; x<4; x++) - { - double besthere = (1<<20); - besthere*=besthere; - uint8 byte1 = data[2*(x+ix+(y+iy)*width)]; - uint8 byte2 = data[2*(x+ix+(y+iy)*width)+1]; - int alpha = (byte1<<8)+byte2; - for(int index=0; index<8; index++) - { - double indexError; - indexError = alpha-valtab[offset+index]; - indexError*=indexError; - if(indexError=prevbest) - return prevbest+(1<<30); - } - } - return error; -} - -// compressBlockAlpha16 -// -// Compresses a block using the 11-bit EAC formats. -// Depends on the global variable formatSigned. -// -// COMPRESSED_R11_EAC (if formatSigned = 0) -// This is an 11-bit unsigned format. Since we do not have a good 11-bit file format, we use 16-bit pgm instead. -// Here we assume that, in the input 16-bit pgm file, 0 represents 0.0 and 65535 represents 1.0. The function compressBlockAlpha16 -// will find the compressed block which best matches the data. In detail, it will find the compressed block, which -// if decompressed, will generate an 11-bit block that after bit replication to 16-bits will generate the closest -// block to the original 16-bit pgm block. -// -// COMPRESSED_SIGNED_R11_EAC (if formatSigned = 1) -// This is an 11-bit signed format. Since we do not have any signed file formats, we use unsigned 16-bit pgm instead. -// Hence we assume that, in the input 16-bit pgm file, 1 represents -1.0, 32768 represents 0.0 and 65535 represents 1.0. -// The function compresseBlockAlpha16 will find the compressed block, which if decompressed, will generate a signed -// 11-bit block that after bit replication to 16-bits and conversion to unsigned (1 equals -1.0, 32768 equals 0.0 and -// 65535 equals 1.0) will generate the closest block to the original 16-bit pgm block. -// -// COMPRESSED_RG11_EAC is compressed by calling the function twice, dito for COMPRESSED_SIGNED_RG11_EAC. -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockAlpha16(uint8* data, int ix, int iy, int width, int height, uint8* returnData) -{ - unsigned int bestbase, besttable, bestmul; - double besterror; - besterror=1<<20; - besterror*=besterror; - for(int base=0; base<256; base++) - { - for(int table=0; table<16; table++) - { - for(int mul=0; mul<16; mul++) - { - double e = calcError(data, ix, iy, width, height,base,table,mul,besterror); - if(e7) - { - bit=0; - byte++; - } - } - } - } -} - -// Exhaustive compression of alpha compression in a GL_COMPRESSED_RGB8_ETC2 block -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockAlphaSlow(uint8* data, int ix, int iy, int width, int height, uint8* returnData) -{ - //determine the best table and base alpha value for this block using MSE - int alphasum=0; - int maxdist=-2; - for(int x=0; x<4; x++) - { - for(int y=0; y<4; y++) - { - alphasum+=data[ix+x+(iy+y)*width]; - } - } - int alpha = (int)( ((float)alphasum)/16.0f+0.5f); //average pixel value, used as guess for base value. - - int bestsum=1000000000; - int besttable=-3; - int bestalpha=128; - int prevalpha=alpha; - - //main loop: determine best base alpha value and offset table to use for compression - //try some different alpha tables. - for(int table = 0; table<256&&bestsum>0; table++) - { - int tablealpha=prevalpha; - int tablebestsum=1000000000; - //test some different alpha values, trying to find the best one for the given table. - for(int alphascale=32; alphascale>0; alphascale/=8) - { - - int startalpha = clamp(tablealpha-alphascale*4); - int endalpha = clamp(tablealpha+alphascale*4); - - for(alpha=startalpha; alpha<=endalpha; alpha+=alphascale) { - int sum=0; - int val,diff,bestdiff=10000000,index; - for(int x=0; x<4; x++) - { - for(int y=0; y<4; y++) - { - //compute best offset here, add square difference to sum.. - val=data[ix+x+(iy+y)*width]; - bestdiff=1000000000; - //the values are always ordered from small to large, with the first 4 being negative and the last 4 positive - //search is therefore made in the order 0-1-2-3 or 7-6-5-4, stopping when error increases compared to the previous entry tested. - if(val>alpha) - { - for(index=7; index>3; index--) - { - diff=clamp_table[alpha+(alphaTable[table][index])+255]-val; - diff*=diff; - if(diff<=bestdiff) - { - bestdiff=diff; - } - else - break; - } - } - else - { - for(index=0; index<5; index++) - { - diff=clamp_table[alpha+(alphaTable[table][index])+255]-val; - diff*=diff; - if(difftablebestsum) - { - x=9999; //just to make it large and get out of the x<4 loop - break; - } - } - } - if(sum7) - { - bit=0; - byte++; - } - } - } - } -} - -// Calculate weighted PSNR -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calculateWeightedPSNR(uint8 *lossyimg, uint8 *origimg, int width, int height, double w1, double w2, double w3) -{ - // Note: This calculation of PSNR uses the formula - // - // PSNR = 10 * log_10 ( 255^2 / wMSE ) - // - // where the wMSE is calculated as - // - // 1/(N*M) * sum ( ( w1*(R' - R)^2 + w2*(G' - G)^2 + w3*(B' - B)^2) ) - // - // typical weights are 0.299, 0.587, 0.114 for perceptually weighted PSNR and - // 1.0/3.0, 1.0/3.0, 1.0/3.0 for nonweighted PSNR - - int x,y; - double wMSE; - double PSNR; - double err; - wMSE = 0; - - for(y=0;y.\n",srcfile); - exit(1); - } - height=active_height; - width=active_width; - fclose(f); -} - -// Writes output file -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void writeOutputFile(char *dstfile, uint8* img, uint8* alphaimg, int width, int height) -{ - char str[300]; - - if(format!=ETC2PACKAGE_R_NO_MIPMAPS&&format!=ETC2PACKAGE_RG_NO_MIPMAPS) - { - fWritePPM("tmp.ppm",width,height,img,8,false); - printf("Saved file tmp.ppm \n\n"); - } - else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) - { - fWritePPM("tmp.ppm",width,height,img,16,false); - } - if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS||format==ETC2PACKAGE_RGBA1_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) - fWritePGM("alphaout.pgm",width,height,alphaimg,false,8); - if(format==ETC2PACKAGE_R_NO_MIPMAPS) - fWritePGM("alphaout.pgm",width,height,alphaimg,false,16); - - // Delete destination file if it exists - if(fileExist(dstfile)) - { - sprintf(str, "del %s\n",dstfile); - system(str); - } - - int q = find_pos_of_extension(dstfile); - if(!strcmp(&dstfile[q],".ppm")&&format!=ETC2PACKAGE_R_NO_MIPMAPS) - { - // Already a .ppm file. Just rename. - sprintf(str,"move tmp.ppm %s\n",dstfile); - printf("Renaming destination file to %s\n",dstfile); - } - else - { - // Converting from .ppm to other file format - // - // Use your favorite command line image converter program, - // for instance Image Magick. Just make sure the syntax can - // be written as below: - // - // C:\magick convert source.ppm dest.jpg - // - if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS||format==ETC2PACKAGE_RGBA1_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) - { - // Somewhere after version 6.7.1-2 of ImageMagick the following command gives the wrong result due to a bug. - // sprintf(str,"composite -compose CopyOpacity alphaout.pgm tmp.ppm %s\n",dstfile); - // Instead we read the file and write a tga. - - printf("Converting destination file from .ppm/.pgm to %s with alpha\n",dstfile); - int rw, rh; - unsigned char *pixelsRGB; - unsigned char *pixelsA; - fReadPPM("tmp.ppm", rw, rh, pixelsRGB, 8); - fReadPGM("alphaout.pgm", rw, rh, pixelsA, 8); - fWriteTGAfromRGBandA(dstfile, rw, rh, pixelsRGB, pixelsA, true); - free(pixelsRGB); - free(pixelsA); - sprintf(str,""); // Nothing to execute. - } - else if(format==ETC2PACKAGE_R_NO_MIPMAPS) - { - sprintf(str,"magick convert alphaout.pgm %s\n",dstfile); - printf("Converting destination file from .pgm to %s\n",dstfile); - } - else - { - sprintf(str,"magick convert tmp.ppm %s\n",dstfile); - printf("Converting destination file from .ppm to %s\n",dstfile); - } - } - // Execute system call - system(str); - - free(img); - if(alphaimg!=NULL) - free(alphaimg); -} - -// Calculates the PSNR between two files -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calculatePSNRfile(char *srcfile, uint8 *origimg, uint8* origalpha) -{ - uint8 *alphaimg, *img; - int active_width, active_height; - uncompressFile(srcfile,img,alphaimg,active_width,active_height); - - // calculate Mean Square Error (MSE) - double MSER=0,MSEG=0,MSEB=0,MSEA, PSNRR,PSNRG,PSNRA; - double MSE; - double wMSE; - double PSNR=0; - double wPSNR; - double err; - MSE = 0; - MSEA=0; - wMSE = 0; - int width=((active_width+3)/4)*4; - int height=((active_height+3)/4)*4; - int numpixels = 0; - for(int y=0;y 0) - { - err = img[y*active_width*3+x*3+0] - origimg[y*width*3+x*3+0]; - MSE += ((err * err)/3.0); - wMSE += PERCEPTUAL_WEIGHT_R_SQUARED * (err*err); - err = img[y*active_width*3+x*3+1] - origimg[y*width*3+x*3+1]; - MSE += ((err * err)/3.0); - wMSE += PERCEPTUAL_WEIGHT_G_SQUARED * (err*err); - err = img[y*active_width*3+x*3+2] - origimg[y*width*3+x*3+2]; - MSE += ((err * err)/3.0); - wMSE += PERCEPTUAL_WEIGHT_B_SQUARED * (err*err); - numpixels++; - } - } - else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) - { - int rorig = (origimg[6*(y*width+x)+0]<<8)+origimg[6*(y*width+x)+1]; - int rnew = ( img[6*(y*active_width+x)+0]<<8)+ img[6*(y*active_width+x)+1]; - int gorig = (origimg[6*(y*width+x)+2]<<8)+origimg[6*(y*width+x)+3]; - int gnew = ( img[6*(y*active_width+x)+2]<<8)+ img[6*(y*active_width+x)+3]; - err=rorig-rnew; - MSER+=(err*err); - err=gorig-gnew; - MSEG+=(err*err); - } - else if(format==ETC2PACKAGE_R_NO_MIPMAPS) - { - int aorig = (((int)origalpha[2*(y*width+x)+0])<<8)+origalpha[2*(y*width+x)+1]; - int anew = (((int)alphaimg[2*(y*active_width+x)+0])<<8)+alphaimg[2*(y*active_width+x)+1]; - err=aorig-anew; - MSEA+=(err*err); - } - } - } - if(format == ETC2PACKAGE_RGBA1_NO_MIPMAPS || format == ETC2PACKAGE_sRGBA1_NO_MIPMAPS) - { - MSE = MSE / (1.0 * numpixels); - wMSE = wMSE / (1.0 * numpixels); - PSNR = 10*log((1.0*255*255)/MSE)/log(10.0); - wPSNR = 10*log((1.0*255*255)/wMSE)/log(10.0); - printf("PSNR only calculated on pixels where compressed alpha > 0\n"); - printf("color PSNR: %lf\nweighted PSNR: %lf\n",PSNR,wPSNR); - } - else if(format!=ETC2PACKAGE_R_NO_MIPMAPS&&format!=ETC2PACKAGE_RG_NO_MIPMAPS) - { - MSE = MSE / (active_width * active_height); - wMSE = wMSE / (active_width * active_height); - PSNR = 10*log((1.0*255*255)/MSE)/log(10.0); - wPSNR = 10*log((1.0*255*255)/wMSE)/log(10.0); - if(format == ETC2PACKAGE_RGBA_NO_MIPMAPS || format == ETC2PACKAGE_sRGBA_NO_MIPMAPS) - printf("PSNR only calculated on RGB, not on alpha\n"); - printf("color PSNR: %lf\nweighted PSNR: %lf\n",PSNR,wPSNR); - } - else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) - { - MSER = MSER / (active_width * active_height); - MSEG = MSEG / (active_width * active_height); - PSNRR = 10*log((1.0*65535*65535)/MSER)/log(10.0); - PSNRG = 10*log((1.0*65535*65535)/MSEG)/log(10.0); - printf("red PSNR: %lf\ngreen PSNR: %lf\n",PSNRR,PSNRG); - } - else if(format==ETC2PACKAGE_R_NO_MIPMAPS) - { - MSEA = MSEA / (active_width * active_height); - PSNRA = 10*log((1.0*65535.0*65535.0)/MSEA)/log(10.0); - printf("PSNR: %lf\n",PSNRA); - } - free(img); - return PSNR; -} - -//// Exhaustive code starts here. - -#if EXHAUSTIVE_CODE_ACTIVE -// Precomutes a table that is used when compressing a block exhaustively -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -inline unsigned int precompute_3bittable_all_subblocksRG_withtest_perceptual1000(uint8 *block,uint8 *avg_color, unsigned int *precalc_err_UL_R, unsigned int *precalc_err_UR_R, unsigned int *precalc_err_LL_R, unsigned int *precalc_err_LR_R,unsigned int *precalc_err_UL_RG, unsigned int *precalc_err_UR_RG, unsigned int *precalc_err_LL_RG, unsigned int *precalc_err_LR_RG, unsigned int best_err) -{ - int table; - int index; - int orig[3],approx[3][4]; - int x; - int intensity_modifier; - const int *table_indices; - - int good_enough_to_test; - unsigned int err[4]; - unsigned int err_this_table_upper; - unsigned int err_this_table_lower; - unsigned int err_this_table_left; - unsigned int err_this_table_right; - - // If the error in the red and green component is already larger than best_err for all 8 tables in - // all of upper, lower, left and right, this combination of red and green will never be used in - // the optimal color configuration. Therefore we can avoid testing all the blue colors for this - // combination. - good_enough_to_test = false; - - for(table=0;table<8;table++) // try all the 8 tables. - { - table_indices = &compressParamsFast[table*4]; - - intensity_modifier = table_indices[0]; - approx[1][0]=CLAMP(0, avg_color[1]+intensity_modifier,255); - intensity_modifier = table_indices[1]; - approx[1][1]=CLAMP(0, avg_color[1]+intensity_modifier,255); - intensity_modifier = table_indices[2]; - approx[1][2]=CLAMP(0, avg_color[1]+intensity_modifier,255); - intensity_modifier = table_indices[3]; - approx[1][3]=CLAMP(0, avg_color[1]+intensity_modifier,255); - - err_this_table_upper = 0; - err_this_table_lower = 0; - err_this_table_left = 0; - err_this_table_right = 0; - for(x=0; x<4; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - for(index=0;index<4;index++) - { - err[index] = precalc_err_UL_R[table*4*4+x*4+index] - + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 * SQUARE(approx[1][index]-orig[1]); - precalc_err_UL_RG[table*4*4+x*4+index] = err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_upper+=err[0]; - err_this_table_left+=err[0]; - } - for(x=4; x<8; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - for(index=0;index<4;index++) - { - err[index] = precalc_err_UR_R[table*4*4+(x-4)*4+index] - + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 * SQUARE(approx[1][index]-orig[1]); - precalc_err_UR_RG[table*4*4+(x-4)*4+index] = err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_upper+=err[0]; - err_this_table_right+=err[0]; - } - for(x=8; x<12; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - - for(index=0;index<4;index++) - { - err[index] = precalc_err_LL_R[table*4*4+(x-8)*4+index] - + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 * SQUARE(approx[1][index]-orig[1]); - precalc_err_LL_RG[table*4*4+(x-8)*4+index] = err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_lower+=err[0]; - err_this_table_left+=err[0]; - } - for(x=12; x<16; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - - for(index=0;index<4;index++) - { - err[index] = precalc_err_LR_R[table*4*4+(x-12)*4+index] - + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 * SQUARE(approx[1][index]-orig[1]); - precalc_err_LR_RG[table*4*4+(x-12)*4+index] = err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_lower+=err[0]; - err_this_table_right+=err[0]; - } - if(err_this_table_upper < best_err) - good_enough_to_test = true; - if(err_this_table_lower < best_err) - good_enough_to_test = true; - if(err_this_table_left < best_err) - good_enough_to_test = true; - if(err_this_table_right < best_err) - good_enough_to_test = true; - } - return good_enough_to_test; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precomutes a table that is used when compressing a block exhaustively -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -inline int precompute_3bittable_all_subblocksRG_withtest(uint8 *block,uint8 *avg_color, unsigned int *precalc_err_UL_R, unsigned int *precalc_err_UR_R, unsigned int *precalc_err_LL_R, unsigned int *precalc_err_LR_R,unsigned int *precalc_err_UL_RG, unsigned int *precalc_err_UR_RG, unsigned int *precalc_err_LL_RG, unsigned int *precalc_err_LR_RG, unsigned int best_err) -{ - int table; - int index; - int orig[3],approx[3][4]; - int x; - int intensity_modifier; - const int *table_indices; - - int good_enough_to_test; - unsigned int err[4]; - unsigned int err_this_table_upper; - unsigned int err_this_table_lower; - unsigned int err_this_table_left; - unsigned int err_this_table_right; - - // If the error in the red and green component is already larger than best_err for all 8 tables in - // all of upper, lower, left and right, this combination of red and green will never be used in - // the optimal color configuration. Therefore we can avoid testing all the blue colors for this - // combination. - good_enough_to_test = false; - - for(table=0;table<8;table++) // try all the 8 tables. - { - table_indices = &compressParamsFast[table*4]; - - intensity_modifier = table_indices[0]; - approx[1][0]=CLAMP(0, avg_color[1]+intensity_modifier,255); - intensity_modifier = table_indices[1]; - approx[1][1]=CLAMP(0, avg_color[1]+intensity_modifier,255); - intensity_modifier = table_indices[2]; - approx[1][2]=CLAMP(0, avg_color[1]+intensity_modifier,255); - intensity_modifier = table_indices[3]; - approx[1][3]=CLAMP(0, avg_color[1]+intensity_modifier,255); - - err_this_table_upper = 0; - err_this_table_lower = 0; - err_this_table_left = 0; - err_this_table_right = 0; - for(x=0; x<4; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - for(index=0;index<4;index++) - { - err[index] = precalc_err_UL_R[table*4*4+x*4+index]+SQUARE(approx[1][index]-orig[1]); - precalc_err_UL_RG[table*4*4+x*4+index] = err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_upper+=err[0]; - err_this_table_left+=err[0]; - } - for(x=4; x<8; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - for(index=0;index<4;index++) - { - err[index] = precalc_err_UR_R[table*4*4+(x-4)*4+index]+SQUARE(approx[1][index]-orig[1]); - precalc_err_UR_RG[table*4*4+(x-4)*4+index] = err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_upper+=err[0]; - err_this_table_right+=err[0]; - } - for(x=8; x<12; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - - for(index=0;index<4;index++) - { - err[index] = precalc_err_LL_R[table*4*4+(x-8)*4+index]+SQUARE(approx[1][index]-orig[1]); - precalc_err_LL_RG[table*4*4+(x-8)*4+index] = err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_lower+=err[0]; - err_this_table_left+=err[0]; - } - for(x=12; x<16; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - - for(index=0;index<4;index++) - { - err[index] = precalc_err_LR_R[table*4*4+(x-12)*4+index]+SQUARE(approx[1][index]-orig[1]); - precalc_err_LR_RG[table*4*4+(x-12)*4+index] = err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_lower+=err[0]; - err_this_table_right+=err[0]; - } - if(err_this_table_upper < best_err) - good_enough_to_test = true; - if(err_this_table_lower < best_err) - good_enough_to_test = true; - if(err_this_table_left < best_err) - good_enough_to_test = true; - if(err_this_table_right < best_err) - good_enough_to_test = true; - } - return good_enough_to_test; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precomutes a table that is used when compressing a block exhaustively -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -inline unsigned int precompute_3bittable_all_subblocksR_with_test_perceptual1000(uint8 *block,uint8 *avg_color, unsigned int *precalc_err_UL_R, unsigned int *precalc_err_UR_R, unsigned int *precalc_err_LL_R, unsigned int *precalc_err_LR_R, unsigned int best_err) -{ - int table; - int index; - int orig[3],approx[3][4]; - int x; - int intensity_modifier; - const int *table_indices; - - unsigned int err[4]; - unsigned int err_this_table_upper; - unsigned int err_this_table_lower; - unsigned int err_this_table_left; - unsigned int err_this_table_right; - - int good_enough_to_test; - - good_enough_to_test = false; - - for(table=0;table<8;table++) // try all the 8 tables. - { - err_this_table_upper = 0; - err_this_table_lower = 0; - err_this_table_left = 0; - err_this_table_right = 0; - - table_indices = &compressParamsFast[table*4]; - - intensity_modifier = table_indices[0]; - approx[0][0]=CLAMP(0, avg_color[0]+intensity_modifier,255); - intensity_modifier = table_indices[1]; - approx[0][1]=CLAMP(0, avg_color[0]+intensity_modifier,255); - intensity_modifier = table_indices[2]; - approx[0][2]=CLAMP(0, avg_color[0]+intensity_modifier,255); - intensity_modifier = table_indices[3]; - approx[0][3]=CLAMP(0, avg_color[0]+intensity_modifier,255); - - for(x=0; x<4; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - for(index=0;index<4;index++) - { - err[index]=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(approx[0][index]-orig[0]); - precalc_err_UL_R[table*4*4+x*4+index]=err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_upper+=err[0]; - err_this_table_left+=err[0]; - } - for(x=4; x<8; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - for(index=0;index<4;index++) - { - err[index]=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(approx[0][index]-orig[0]); - precalc_err_UR_R[table*4*4+(x-4)*4+index]=err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_upper+=err[0]; - err_this_table_right+=err[0]; - } - for(x=8; x<12; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - - for(index=0;index<4;index++) - { - err[index]=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(approx[0][index]-orig[0]); - precalc_err_LL_R[table*4*4+(x-8)*4+index]=err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_lower+=err[0]; - err_this_table_left+=err[0]; - - } - for(x=12; x<16; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - - for(index=0;index<4;index++) - { - err[index]=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(approx[0][index]-orig[0]); - precalc_err_LR_R[table*4*4+(x-12)*4+index]=err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_lower+=err[0]; - err_this_table_right+=err[0]; - } - if(err_this_table_upper < best_err) - good_enough_to_test = true; - if(err_this_table_lower < best_err) - good_enough_to_test = true; - if(err_this_table_left < best_err) - good_enough_to_test = true; - if(err_this_table_right < best_err) - good_enough_to_test = true; - } - return good_enough_to_test; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precomutes a table that is used when compressing a block exhaustively -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -inline int precompute_3bittable_all_subblocksR_with_test(uint8 *block,uint8 *avg_color, unsigned int *precalc_err_UL_R, unsigned int *precalc_err_UR_R, unsigned int *precalc_err_LL_R, unsigned int *precalc_err_LR_R, unsigned int best_err) -{ - int table; - int index; - int orig[3],approx[3][4]; - int x; - int intensity_modifier; - const int *table_indices; - - unsigned int err[4]; - unsigned int err_this_table_upper; - unsigned int err_this_table_lower; - unsigned int err_this_table_left; - unsigned int err_this_table_right; - - int good_enough_to_test; - - good_enough_to_test = false; - - for(table=0;table<8;table++) // try all the 8 tables. - { - err_this_table_upper = 0; - err_this_table_lower = 0; - err_this_table_left = 0; - err_this_table_right = 0; - - table_indices = &compressParamsFast[table*4]; - - intensity_modifier = table_indices[0]; - approx[0][0]=CLAMP(0, avg_color[0]+intensity_modifier,255); - intensity_modifier = table_indices[1]; - approx[0][1]=CLAMP(0, avg_color[0]+intensity_modifier,255); - intensity_modifier = table_indices[2]; - approx[0][2]=CLAMP(0, avg_color[0]+intensity_modifier,255); - intensity_modifier = table_indices[3]; - approx[0][3]=CLAMP(0, avg_color[0]+intensity_modifier,255); - - for(x=0; x<4; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - for(index=0;index<4;index++) - { - err[index]=SQUARE(approx[0][index]-orig[0]); - precalc_err_UL_R[table*4*4+x*4+index]=err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_upper+=err[0]; - err_this_table_left+=err[0]; - } - for(x=4; x<8; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - for(index=0;index<4;index++) - { - err[index]=SQUARE(approx[0][index]-orig[0]); - precalc_err_UR_R[table*4*4+(x-4)*4+index]=err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_upper+=err[0]; - err_this_table_right+=err[0]; - } - for(x=8; x<12; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - - for(index=0;index<4;index++) - { - err[index]=SQUARE(approx[0][index]-orig[0]); - precalc_err_LL_R[table*4*4+(x-8)*4+index]=err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_lower+=err[0]; - err_this_table_left+=err[0]; - - } - for(x=12; x<16; x++) - { - orig[0]=block[x*4]; - orig[1]=block[x*4+1]; - orig[2]=block[x*4+2]; - - for(index=0;index<4;index++) - { - err[index]=SQUARE(approx[0][index]-orig[0]); - precalc_err_LR_R[table*4*4+(x-12)*4+index]=err[index]; - } - if(err[0] > err[1]) - err[0] = err[1]; - if(err[2] > err[3]) - err[2] = err[3]; - if(err[0] > err[2]) - err[0] = err[2]; - err_this_table_lower+=err[0]; - err_this_table_right+=err[0]; - } - if(err_this_table_upper < best_err) - good_enough_to_test = true; - if(err_this_table_lower < best_err) - good_enough_to_test = true; - if(err_this_table_left < best_err) - good_enough_to_test = true; - if(err_this_table_right < best_err) - good_enough_to_test = true; - } - return good_enough_to_test; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Tries all index-tables, used when compressing a block exhaustively -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -inline void tryalltables_3bittable_all_subblocks_using_precalc(uint8 *block_2x2,uint8 *color_quant1, unsigned int *precalc_err_UL_RG, unsigned int *precalc_err_UR_RG, unsigned int *precalc_err_LL_RG, unsigned int *precalc_err_LR_RG, unsigned int &err_upper, unsigned int &err_lower, unsigned int &err_left, unsigned int &err_right, unsigned int best_err) -{ - unsigned int err_this_table_upper; - unsigned int err_this_table_lower; - unsigned int err_this_table_left; - unsigned int err_this_table_right; - int orig[3],approx[4]; - int err[4]; - err_upper = 3*255*255*16; - err_lower = 3*255*255*16; - err_left = 3*255*255*16; - err_right = 3*255*255*16; - -#define ONE_PIXEL_UL(table_nbr,xx)\ - orig[0]=block_2x2[xx*4];\ - orig[1]=block_2x2[xx*4+1];\ - orig[2]=block_2x2[xx*4+2];\ - /* unrolled loop for(index=0;index<4;index++)*/\ - err[0]=precalc_err_UL_RG[table_nbr*4*4+xx*4+0] + square_table[approx[0]-orig[2]];\ - err[1]=precalc_err_UL_RG[table_nbr*4*4+xx*4+1] + square_table[approx[1]-orig[2]];\ - err[2]=precalc_err_UL_RG[table_nbr*4*4+xx*4+2] + square_table[approx[2]-orig[2]];\ - err[3]=precalc_err_UL_RG[table_nbr*4*4+xx*4+3] + square_table[approx[3]-orig[2]];\ - /* end unrolled loop*/\ - if(err[0] > err[1])\ - err[0] = err[1];\ - if(err[2] > err[3])\ - err[2] = err[3];\ - if(err[0] > err[2])\ - err[0] = err[2];\ - err_this_table_upper+=err[0];\ - err_this_table_left+=err[0];\ - -#define ONE_PIXEL_UR(table_nbr,xx)\ - orig[0]=block_2x2[xx*4];\ - orig[1]=block_2x2[xx*4+1];\ - orig[2]=block_2x2[xx*4+2];\ - /* unrolled loop for(index=0;index<4;index++)*/\ - err[0]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+0] + square_table[approx[0]-orig[2]];\ - err[1]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+1] + square_table[approx[1]-orig[2]];\ - err[2]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+2] + square_table[approx[2]-orig[2]];\ - err[3]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+3] + square_table[approx[3]-orig[2]];\ - /* end unrolled loop */\ - if(err[0] > err[1])\ - err[0] = err[1];\ - if(err[2] > err[3])\ - err[2] = err[3];\ - if(err[0] > err[2])\ - err[0] = err[2];\ - err_this_table_upper+=err[0];\ - err_this_table_right+=err[0]; - -#define ONE_PIXEL_LL(table_nbr,xx)\ - orig[0]=block_2x2[xx*4];\ - orig[1]=block_2x2[xx*4+1];\ - orig[2]=block_2x2[xx*4+2];\ - /* unrolled loop for(index=0;index<4;index++)*/\ - err[0]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+0] + square_table[approx[0]-orig[2]];\ - err[1]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+1] + square_table[approx[1]-orig[2]];\ - err[2]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+2] + square_table[approx[2]-orig[2]];\ - err[3]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+3] + square_table[approx[3]-orig[2]];\ - /* end unrolled loop*/\ - if(err[0] > err[1])\ - err[0] = err[1];\ - if(err[2] > err[3])\ - err[2] = err[3];\ - if(err[0] > err[2])\ - err[0] = err[2];\ - err_this_table_lower+=err[0];\ - err_this_table_left+=err[0];\ - -#define ONE_PIXEL_LR(table_nbr,xx)\ - orig[0]=block_2x2[xx*4];\ - orig[1]=block_2x2[xx*4+1];\ - orig[2]=block_2x2[xx*4+2];\ - /* unrolled loop for(index=0;index<4;index++)*/\ - err[0]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+0] + square_table[approx[0]-orig[2]];\ - err[1]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+1] + square_table[approx[1]-orig[2]];\ - err[2]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+2] + square_table[approx[2]-orig[2]];\ - err[3]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+3] + square_table[approx[3]-orig[2]];\ - /* end unrolled loop*/\ - if(err[0] > err[1])\ - err[0] = err[1];\ - if(err[2] > err[3])\ - err[2] = err[3];\ - if(err[0] > err[2])\ - err[0] = err[2];\ - err_this_table_lower+=err[0];\ - err_this_table_right+=err[0];\ - -#define ONE_TABLE_3(table_nbr)\ - err_this_table_upper = 0;\ - err_this_table_lower = 0;\ - err_this_table_left = 0;\ - err_this_table_right = 0;\ - approx[0]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+0]+255];\ - approx[1]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+1]+255];\ - approx[2]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+2]+255];\ - approx[3]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+3]+255];\ - /* unroll loop for(xx=0; xx<4; xx++) */\ - ONE_PIXEL_UL(table_nbr,0)\ - ONE_PIXEL_UL(table_nbr,1)\ - ONE_PIXEL_UL(table_nbr,2)\ - ONE_PIXEL_UL(table_nbr,3)\ - /* end unroll loop */\ - /* unroll loop for(xx=4; xx<8; xx++) */\ - ONE_PIXEL_LR(table_nbr,12)\ - ONE_PIXEL_LR(table_nbr,13)\ - ONE_PIXEL_LR(table_nbr,14)\ - ONE_PIXEL_LR(table_nbr,15)\ - /* end unroll loop */\ - /* If error in the top left 2x2 pixel area is already larger than the best error, and */\ - /* The same is true for the bottom right 2x2 pixel area, this combination of table and color */\ - /* can never be part of an optimal solution and therefore we do not need to test the other */\ - /* two 2x2 pixel areas */\ - if((err_this_table_upper err[1])\ - err[0] = err[1];\ - if(err[2] > err[3])\ - err[2] = err[3];\ - if(err[0] > err[2])\ - err[0] = err[2];\ - err_this_table_upper+=err[0];\ - err_this_table_left+=err[0];\ - -#define ONE_PIXEL_UR_PERCEP(table_nbr,xx)\ - orig[0]=block_2x2[xx*4];\ - orig[1]=block_2x2[xx*4+1];\ - orig[2]=block_2x2[xx*4+2];\ - /* unrolled loop for(index=0;index<4;index++)*/\ - err[0]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+0] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[0]-orig[2]];\ - err[1]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+1] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[1]-orig[2]];\ - err[2]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+2] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[2]-orig[2]];\ - err[3]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+3] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[3]-orig[2]];\ - /* end unrolled loop */\ - if(err[0] > err[1])\ - err[0] = err[1];\ - if(err[2] > err[3])\ - err[2] = err[3];\ - if(err[0] > err[2])\ - err[0] = err[2];\ - err_this_table_upper+=err[0];\ - err_this_table_right+=err[0]; - -#define ONE_PIXEL_LL_PERCEP(table_nbr,xx)\ - orig[0]=block_2x2[xx*4];\ - orig[1]=block_2x2[xx*4+1];\ - orig[2]=block_2x2[xx*4+2];\ - /* unrolled loop for(index=0;index<4;index++)*/\ - err[0]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+0] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[0]-orig[2]];\ - err[1]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+1] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[1]-orig[2]];\ - err[2]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+2] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[2]-orig[2]];\ - err[3]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+3] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[3]-orig[2]];\ - /* end unrolled loop*/\ - if(err[0] > err[1])\ - err[0] = err[1];\ - if(err[2] > err[3])\ - err[2] = err[3];\ - if(err[0] > err[2])\ - err[0] = err[2];\ - err_this_table_lower+=err[0];\ - err_this_table_left+=err[0];\ - -#define ONE_PIXEL_LR_PERCEP(table_nbr,xx)\ - orig[0]=block_2x2[xx*4];\ - orig[1]=block_2x2[xx*4+1];\ - orig[2]=block_2x2[xx*4+2];\ - /* unrolled loop for(index=0;index<4;index++)*/\ - err[0]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+0] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[0]-orig[2]];\ - err[1]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+1] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[1]-orig[2]];\ - err[2]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+2] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[2]-orig[2]];\ - err[3]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+3] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[3]-orig[2]];\ - /* end unrolled loop*/\ - if(err[0] > err[1])\ - err[0] = err[1];\ - if(err[2] > err[3])\ - err[2] = err[3];\ - if(err[0] > err[2])\ - err[0] = err[2];\ - err_this_table_lower+=err[0];\ - err_this_table_right+=err[0];\ - -#define ONE_TABLE_3_PERCEP(table_nbr)\ - err_this_table_upper = 0;\ - err_this_table_lower = 0;\ - err_this_table_left = 0;\ - err_this_table_right = 0;\ - approx[0]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+0]+255];\ - approx[1]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+1]+255];\ - approx[2]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+2]+255];\ - approx[3]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+3]+255];\ - /* unroll loop for(xx=0; xx<4; xx++) */\ - ONE_PIXEL_UL_PERCEP(table_nbr,0)\ - ONE_PIXEL_UL_PERCEP(table_nbr,1)\ - ONE_PIXEL_UL_PERCEP(table_nbr,2)\ - ONE_PIXEL_UL_PERCEP(table_nbr,3)\ - /* end unroll loop */\ - /* unroll loop for(xx=4; xx<8; xx++) */\ - ONE_PIXEL_LR_PERCEP(table_nbr,12)\ - ONE_PIXEL_LR_PERCEP(table_nbr,13)\ - ONE_PIXEL_LR_PERCEP(table_nbr,14)\ - ONE_PIXEL_LR_PERCEP(table_nbr,15)\ - /* end unroll loop */\ - /* If error in the top left 2x2 pixel area is already larger than the best error, and */\ - /* The same is true for the bottom right 2x2 pixel area, this combination of table and color */\ - /* can never be part of an optimal solution and therefore we do not need to test the other */\ - /* two 2x2 pixel areas */\ - if((err_this_table_upper> 5; - bytediff[1] = bytediff[1] >> 5; - bytediff[2] = bytediff[2] >> 5; - best_enc_color2[0]= best_enc_color1[0] + bytediff[0]; - best_enc_color2[1]= best_enc_color1[1] + bytediff[1]; - best_enc_color2[2]= best_enc_color1[2] + bytediff[2]; - - // allocate memory for errors: - err_upper = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); - if(!err_upper){printf("Out of memory allocating \n");exit(1);} - err_lower = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); - if(!err_lower){printf("Out of memory allocating \n");exit(1);} - err_left = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); - if(!err_left){printf("Out of memory allocating \n");exit(1);} - err_right = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); - if(!err_right){printf("Out of memory allocating \n");exit(1);} - - int q; - // Calculate all errors - for(enc_color1[0]=0; enc_color1[0]<32; enc_color1[0]++) - { - color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - if(precompute_3bittable_all_subblocksR_with_test_perceptual1000(block_2x2, color_quant1, precalc_err_UL_R, precalc_err_UR_R, precalc_err_LL_R, precalc_err_LR_R, best_error_so_far)) - { - for(enc_color1[1]=0; enc_color1[1]<32; enc_color1[1]++) - { - color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - if(precompute_3bittable_all_subblocksRG_withtest_perceptual1000(block_2x2, color_quant1, precalc_err_UL_R, precalc_err_UR_R, precalc_err_LL_R, precalc_err_LR_R, precalc_err_UL_RG, precalc_err_UR_RG, precalc_err_LL_RG, precalc_err_LR_RG, best_error_so_far)) - { - for(enc_color1[2]=0; enc_color1[2]<32; enc_color1[2]++) - { - color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - tryalltables_3bittable_all_subblocks_using_precalc_perceptual1000(block_2x2, color_quant1, precalc_err_UL_RG, precalc_err_UR_RG, precalc_err_LL_RG, precalc_err_LR_RG, err_upper[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_lower[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_left[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_right[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], best_error_so_far); - } - } - else - { - for(q=0;q<32;q++) - { - err_upper[32*32*enc_color1[0]+32*enc_color1[1]+q] = MAXERR1000; - err_lower[32*32*enc_color1[0]+32*enc_color1[1]+q] = MAXERR1000; - err_left[32*32*enc_color1[0]+32*enc_color1[1]+q] = MAXERR1000; - err_right[32*32*enc_color1[0]+32*enc_color1[1]+q] = MAXERR1000; - } - } - } - } - else - { - for(q=0;q<32*32;q++) - { - err_upper[32*32*enc_color1[0]+q] = MAXERR1000; - err_lower[32*32*enc_color1[0]+q] = MAXERR1000; - err_left[32*32*enc_color1[0]+q] = MAXERR1000; - err_right[32*32*enc_color1[0]+q] = MAXERR1000; - } - } - } - for(enc_color1[0]=0; enc_color1[0]<32; enc_color1[0]++) - { - for(enc_color1[1]=0; enc_color1[1]<32; enc_color1[1]++) - { - for(enc_color1[2]=0; enc_color1[2]<4; enc_color1[2]++) - { - error_lying = err_upper[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]]; - error_standing = err_left[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]]; - if(error_lying < best_error_so_far || error_standing < best_error_so_far) - { - for(enc_color2[0]=JAS_MAX(0,enc_color1[0]-4); enc_color2[0]> 2); - color_quant1[1] = best_enc_color1[1] << 3 | (best_enc_color1[1] >> 2); - color_quant1[2] = best_enc_color1[2] << 3 | (best_enc_color1[2] >> 2); - if(best_flip == 0) - tryalltables_3bittable2x4percep1000(img,width,height,startx,starty,color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - else - tryalltables_3bittable4x2percep1000(img,width,height,startx,starty,color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - color_quant2[0] = best_enc_color2[0] << 3 | (best_enc_color2[0] >> 2); - color_quant2[1] = best_enc_color2[1] << 3 | (best_enc_color2[1] >> 2); - color_quant2[2] = best_enc_color2[2] << 3 | (best_enc_color2[2] >> 2); - if(best_flip == 0) - tryalltables_3bittable2x4percep1000(img,width,height,startx+2,starty,color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - else - tryalltables_3bittable4x2percep1000(img,width,height,startx,starty+2,color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - diff[0] = best_enc_color2[0]-best_enc_color1[0]; - diff[1] = best_enc_color2[1]-best_enc_color1[1]; - diff[2] = best_enc_color2[2]-best_enc_color1[2]; - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - diffbit = 1; - compressed1 = 0; - PUTBITSHIGH( compressed1, diffbit, 1, 33); - PUTBITSHIGH( compressed1, best_enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1, best_enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1, best_enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1, diff[0], 3, 58); - PUTBITSHIGH( compressed1, diff[1], 3, 50); - PUTBITSHIGH( compressed1, diff[2], 3, 42); - PUTBITSHIGH( compressed1, best_table1, 3, 39); - PUTBITSHIGH( compressed1, best_table2, 3, 36); - PUTBITSHIGH( compressed1, best_flip, 1, 32); - - if(best_flip == 0) - { - compressed2 = 0; - PUTBITS( compressed2, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2, (best_pixel_indices2_LSB ), 8, 15); - } - else - { - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - compressed2 = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - return best_error_using_diff_mode; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Compresses the differential mode exhaustively. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockDifferentialExhaustive(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int previous_best_err) -{ - unsigned int best_err_norm_diff = 255*255*16*3; - unsigned int best_err_norm_444 = 255*255*16*3; - unsigned int best_err_flip_diff = 255*255*16*3; - unsigned int best_err_flip_444 = 255*255*16*3; - uint8 color_quant1[3], color_quant2[3]; - - int enc_color1[3], enc_color2[3], diff[3]; - int best_enc_color1[3], best_enc_color2[3]; - - int min_error=255*255*8*3; - unsigned int best_pixel_indices1_MSB=0; - unsigned int best_pixel_indices1_LSB=0; - unsigned int best_pixel_indices2_MSB=0; - unsigned int best_pixel_indices2_LSB=0; - unsigned int pixel_indices1_MSB=0; - unsigned int pixel_indices1_LSB=0; - unsigned int pixel_indices2_MSB=0; - - unsigned int *err_upper, *err_lower; - unsigned int *err_left, *err_right; - - unsigned int pixel_indices2_LSB=0; - - unsigned int table1=0, table2=0; - unsigned int best_table1=0, best_table2=0; - - unsigned int precalc_err_UL_R[8*4*4]; - unsigned int precalc_err_UR_R[8*4*4]; - unsigned int precalc_err_LL_R[8*4*4]; - unsigned int precalc_err_LR_R[8*4*4]; - - unsigned int precalc_err_UL_RG[8*4*4]; - unsigned int precalc_err_UR_RG[8*4*4]; - unsigned int precalc_err_LL_RG[8*4*4]; - unsigned int precalc_err_LR_RG[8*4*4]; - - int diffbit; - uint8 block_2x2[4*4*4]; - - unsigned int error, error_lying, error_standing, best_err, total_best_err; - unsigned int *err_lower_adr; - int best_flip; - unsigned int *err_right_adr; - - int xx,yy,count = 0; - - // Reshuffle pixels so that the top left 2x2 pixels arrive first, then the top right 2x2 pixels etc. Also put use 4 bytes per pixel to make it 32-word aligned. - for(xx = 0; xx<2; xx++) - { - for(yy=0; yy<2; yy++) - { - block_2x2[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block_2x2[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block_2x2[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block_2x2[(count)*4+3] = 0; - count++; - } - } - for(xx = 2; xx<4; xx++) - { - for(yy=0; yy<2; yy++) - { - block_2x2[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block_2x2[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block_2x2[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block_2x2[(count)*4+3] = 0; - count++; - } - } - for(xx = 0; xx<2; xx++) - { - for(yy=2; yy<4; yy++) - { - block_2x2[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block_2x2[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block_2x2[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block_2x2[(count)*4+3] = 0; - count++; - } - } - for(xx = 2; xx<4; xx++) - { - for(yy=2; yy<4; yy++) - { - block_2x2[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block_2x2[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block_2x2[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block_2x2[(count)*4+3] = 0; - count++; - } - } - - - unsigned int test1, test2; - best_err = (unsigned int)compressBlockOnlyDiffFlipAverage(img, width, height, startx, starty, test1, test2, best_enc_color1, best_enc_color2, best_flip); - if(previous_best_err < best_err) - total_best_err = previous_best_err; - else - total_best_err = best_err; - - // allocate memory for errors: - err_upper = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); - if(!err_upper){printf("Out of memory allocating \n");exit(1);} - err_lower = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); - if(!err_lower){printf("Out of memory allocating \n");exit(1);} - err_left = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); - if(!err_left){printf("Out of memory allocating \n");exit(1);} - err_right = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); - if(!err_right){printf("Out of memory allocating \n");exit(1);} - - int q; - // Calculate all errors - for(enc_color1[0]=0; enc_color1[0]<32; enc_color1[0]++) - { - color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); - if(precompute_3bittable_all_subblocksR_with_test(block_2x2, color_quant1, precalc_err_UL_R, precalc_err_UR_R, precalc_err_LL_R, precalc_err_LR_R, total_best_err)) - { - for(enc_color1[1]=0; enc_color1[1]<32; enc_color1[1]++) - { - color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); - if(precompute_3bittable_all_subblocksRG_withtest(block_2x2, color_quant1, precalc_err_UL_R, precalc_err_UR_R, precalc_err_LL_R, precalc_err_LR_R, precalc_err_UL_RG, precalc_err_UR_RG, precalc_err_LL_RG, precalc_err_LR_RG, total_best_err)) - { - for(enc_color1[2]=0; enc_color1[2]<32; enc_color1[2]++) - { - color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); - tryalltables_3bittable_all_subblocks_using_precalc(block_2x2, color_quant1, precalc_err_UL_RG, precalc_err_UR_RG, precalc_err_LL_RG, precalc_err_LR_RG, err_upper[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_lower[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_left[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_right[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], total_best_err); - } - } - else - { - for(q=0;q<32;q++) - { - err_upper[32*32*enc_color1[0]+32*enc_color1[1]+q] = 255*255*16*3; - err_lower[32*32*enc_color1[0]+32*enc_color1[1]+q] = 255*255*16*3; - err_left[32*32*enc_color1[0]+32*enc_color1[1]+q] = 255*255*16*3; - err_right[32*32*enc_color1[0]+32*enc_color1[1]+q] = 255*255*16*3; - } - } - } - } - else - { - for(q=0;q<32*32;q++) - { - err_upper[32*32*enc_color1[0]+q] = 255*255*16*3; - err_lower[32*32*enc_color1[0]+q] = 255*255*16*3; - err_left[32*32*enc_color1[0]+q] = 255*255*16*3; - err_right[32*32*enc_color1[0]+q] = 255*255*16*3; - } - } - } - - for(enc_color1[0]=0; enc_color1[0]<32; enc_color1[0]++) - { - for(enc_color1[1]=0; enc_color1[1]<32; enc_color1[1]++) - { - for(enc_color1[2]=0; enc_color1[2]<4; enc_color1[2]++) - { - error_lying = err_upper[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]]; - error_standing = err_left[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]]; - if(error_lying < total_best_err || error_standing < total_best_err) - { - for(enc_color2[0]=JAS_MAX(0,enc_color1[0]-4); enc_color2[0]> 2); - color_quant1[1] = best_enc_color1[1] << 3 | (best_enc_color1[1] >> 2); - color_quant1[2] = best_enc_color1[2] << 3 | (best_enc_color1[2] >> 2); - if(best_flip == 0) - tryalltables_3bittable2x4(img,width,height,startx,starty,color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - else - tryalltables_3bittable4x2(img,width,height,startx,starty,color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); - - color_quant2[0] = best_enc_color2[0] << 3 | (best_enc_color2[0] >> 2); - color_quant2[1] = best_enc_color2[1] << 3 | (best_enc_color2[1] >> 2); - color_quant2[2] = best_enc_color2[2] << 3 | (best_enc_color2[2] >> 2); - if(best_flip == 0) - tryalltables_3bittable2x4(img,width,height,startx+2,starty,color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - else - tryalltables_3bittable4x2(img,width,height,startx,starty+2,color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); - - diff[0] = best_enc_color2[0]-best_enc_color1[0]; - diff[1] = best_enc_color2[1]-best_enc_color1[1]; - diff[2] = best_enc_color2[2]-best_enc_color1[2]; - - // ETC1_RGB8_OES: - // - // a) bit layout in bits 63 through 32 if diffbit = 0 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| - // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // b) bit layout in bits 63 through 32 if diffbit = 1 - // - // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 - // --------------------------------------------------------------------------------------------------- - // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| - // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | - // --------------------------------------------------------------------------------------------------- - // - // c) bit layout in bits 31 through 0 (in both cases) - // - // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - // -------------------------------------------------------------------------------------------------- - // | most significant pixel index bits | least significant pixel index bits | - // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | - // -------------------------------------------------------------------------------------------------- - - diffbit = 1; - compressed1 = 0; - PUTBITSHIGH( compressed1, diffbit, 1, 33); - PUTBITSHIGH( compressed1, best_enc_color1[0], 5, 63); - PUTBITSHIGH( compressed1, best_enc_color1[1], 5, 55); - PUTBITSHIGH( compressed1, best_enc_color1[2], 5, 47); - PUTBITSHIGH( compressed1, diff[0], 3, 58); - PUTBITSHIGH( compressed1, diff[1], 3, 50); - PUTBITSHIGH( compressed1, diff[2], 3, 42); - PUTBITSHIGH( compressed1, best_table1, 3, 39); - PUTBITSHIGH( compressed1, best_table2, 3, 36); - PUTBITSHIGH( compressed1, best_flip, 1, 32); - - if(best_flip == 0) - { - compressed2 = 0; - PUTBITS( compressed2, (best_pixel_indices1_MSB ), 8, 23); - PUTBITS( compressed2, (best_pixel_indices2_MSB ), 8, 31); - PUTBITS( compressed2, (best_pixel_indices1_LSB ), 8, 7); - PUTBITS( compressed2, (best_pixel_indices2_LSB ), 8, 15); - } - else - { - best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); - best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); - compressed2 = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); - } - return best_err; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// This function uses real exhaustive search for the planar mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockPlanar57ExhaustivePerceptual(uint8 *img, int width,int height,int startx,int starty, unsigned int &compressed57_1, unsigned int &compressed57_2, unsigned int best_error_sofar, unsigned int best_error_planar_red, unsigned int best_error_planar_green, unsigned int best_error_planar_blue) -{ - int colorO_enc[3], colorH_enc[3], colorV_enc[3]; - int best_colorO_enc[3], best_colorH_enc[3], best_colorV_enc[3]; - - unsigned int error; - unsigned int best_error; - unsigned int lowest_possible_error; - unsigned int best_error_red_sofar; - unsigned int best_error_green_sofar; - unsigned int best_error_blue_sofar; - unsigned int BBBtable[128*128]; - unsigned int CCCtable[128*128]; - - uint8 block[4*4*4]; - - // Use 4 bytes per pixel to make it 32-word aligned. - int count = 0; - int xx, yy; - for(yy=0; yy<4; yy++) - { - for(xx = 0; xx<4; xx++) - { - block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block[(count)*4+3] = 0; - count++; - } - } - - // The task is to calculate the sum of the error over the entire area of the block. - // - // The block can be partitioned into: O A A A - // B D D C - // B D C D - // B C D D - // where the error in - // O only depends on colorO - // A only depends on colorO and colorH - // B only depends on colorO and colorV - // C only depends on colorH and colorV - // D depends on all three (colorO, colorH and colorV) - // - // Note that B can be precalculated for all combinations of colorO and colorV - // and the precalculated values can be used instead of calculating it in the inner loop. - // The same applies to C. - // - // In the code below, the squared error over O A A A is calculated and stored in lowest_possible_error - - // Precalc BBB errors - for(colorO_enc[0] = 0; colorO_enc[0]<64; colorO_enc[0]++) - { - for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) - { - BBBtable[colorO_enc[0]*64+colorV_enc[0]] = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*calcBBBred(block, colorO_enc[0], colorV_enc[0]); - } - } - // Precalc CCC errors - for(colorH_enc[0] = 0; colorH_enc[0]<64; colorH_enc[0]++) - { - for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) - { - CCCtable[colorH_enc[0]*64+colorV_enc[0]] = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*calcCCCred(block, colorH_enc[0], colorV_enc[0]); - } - } - best_error = MAXERR1000; - - best_error_red_sofar = JAS_MIN(best_error_planar_red, best_error_sofar); - for(colorO_enc[0] = 0; colorO_enc[0]<64; colorO_enc[0]++) - { - for(colorH_enc[0] = 0; colorH_enc[0]<64; colorH_enc[0]++) - { - lowest_possible_error = calcLowestPossibleRedOHperceptual(block, colorO_enc[0], colorH_enc[0], best_error_red_sofar); - if(lowest_possible_error <= best_error_red_sofar) - { - for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) - { - error = calcErrorPlanarOnlyRedPerceptual(block, colorO_enc[0], colorH_enc[0], colorV_enc[0], lowest_possible_error, BBBtable[colorO_enc[0]*64+colorV_enc[0]], CCCtable[colorH_enc[0]*64+colorV_enc[0]], best_error_red_sofar); - if(error < best_error) - { - best_error = error; - best_colorO_enc[0] = colorO_enc[0]; - best_colorH_enc[0] = colorH_enc[0]; - best_colorV_enc[0] = colorV_enc[0]; - } - } - } - } - } - - if(best_error < best_error_planar_red) - best_error_planar_red = best_error; - - if(best_error_planar_red > best_error_sofar) - { - // The red component in itself is already bigger than the previously best value ---- we can give up. - // use the dummy color black for all colors and report that the errors for the different color components are infinite - best_error_planar_green = MAXERR1000; - best_error_planar_blue = MAXERR1000; - compressed57_1 = 0; - compressed57_2 = 0; - return; - } - - // The task is to calculate the sum of the error over the entire area of the block. - // - // The block can be partitioned into: O A A A - // B D D C - // B D C D - // B C D D - // where the error in - // O only depends on colorO - // A only depends on colorO and colorH - // B only depends on colorO and colorV - // C only depends on colorH and colorV - // D depends on all three (colorO, colorH and colorV) - // - // Note that B can be precalculated for all combinations of colorO and colorV - // and the precalculated values can be used instead of calculating it in the inner loop. - // The same applies to C. - // - // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error - - // Precalc BBB errors - for(colorO_enc[1] = 0; colorO_enc[1]<128; colorO_enc[1]++) - { - for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) - { - BBBtable[colorO_enc[1]*128+colorV_enc[1]] = PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*calcBBBgreen(block, colorO_enc[1], colorV_enc[1]); - } - } - // Precalc CCC errors - for(colorH_enc[1] = 0; colorH_enc[1]<128; colorH_enc[1]++) - { - for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) - { - CCCtable[colorH_enc[1]*128+colorV_enc[1]] = PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*calcCCCgreen(block, colorH_enc[1], colorV_enc[1]); - } - } - best_error = MAXERR1000; - best_error_green_sofar = JAS_MIN(best_error_planar_green, best_error_sofar); - for(colorO_enc[1] = 0; colorO_enc[1]<128; colorO_enc[1]++) - { - for(colorH_enc[1] = 0; colorH_enc[1]<128; colorH_enc[1]++) - { - lowest_possible_error = calcLowestPossibleGreenOHperceptual(block, colorO_enc[1], colorH_enc[1], best_error_green_sofar); - if(lowest_possible_error <= best_error_green_sofar) - { - for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) - { - error = calcErrorPlanarOnlyGreenPerceptual(block, colorO_enc[1], colorH_enc[1], colorV_enc[1], lowest_possible_error, BBBtable[colorO_enc[1]*128+colorV_enc[1]], CCCtable[colorH_enc[1]*128+colorV_enc[1]], best_error_green_sofar); - if(error < best_error) - { - best_error = error; - best_colorO_enc[1] = colorO_enc[1]; - best_colorH_enc[1] = colorH_enc[1]; - best_colorV_enc[1] = colorV_enc[1]; - } - } - } - } - } - - if(best_error < best_error_planar_green) - best_error_planar_green = best_error; - - if(best_error_planar_red + best_error_planar_green > best_error_sofar) - { - // The red component in itself is already bigger than the previously best value ---- we can give up. - // use the dummy color black for all colors and report that the errors for the different color components are infinite - best_error_planar_blue = MAXERR1000; - compressed57_1 = 0; - compressed57_2 = 0; - return; - } - - // The task is to calculate the sum of the error over the entire area of the block. - // - // The block can be partitioned into: O A A A - // B D D C - // B D C D - // B C D D - // where the error in - // O only depends on colorO - // A only depends on colorO and colorH - // B only depends on colorO and colorV - // C only depends on colorH and colorV - // D depends on all three (colorO, colorH and colorV) - // - // Note that B can be precalculated for all combinations of colorO and colorV - // and the precalculated values can be used instead of calculating it in the inner loop. - // The same applies to C. - // - // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error - - // Precalc BBB errors - for(colorO_enc[2] = 0; colorO_enc[2]<64; colorO_enc[2]++) - { - for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) - { - BBBtable[colorO_enc[2]*64+colorV_enc[2]] = calcBBBbluePerceptual(block, colorO_enc[2], colorV_enc[2]); - } - } - // Precalc CCC errors - for(colorH_enc[2] = 0; colorH_enc[2]<64; colorH_enc[2]++) - { - for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) - { - CCCtable[colorH_enc[2]*64+colorV_enc[2]] = calcCCCbluePerceptual(block, colorH_enc[2], colorV_enc[2]); - } - } - best_error = MAXERR1000; - best_error_blue_sofar = JAS_MIN(best_error_planar_blue, best_error_sofar); - for(colorO_enc[2] = 0; colorO_enc[2]<64; colorO_enc[2]++) - { - for(colorH_enc[2] = 0; colorH_enc[2]<64; colorH_enc[2]++) - { - lowest_possible_error = calcLowestPossibleBlueOHperceptual(block, colorO_enc[2], colorH_enc[2], best_error_blue_sofar); - if(lowest_possible_error <= best_error_blue_sofar) - { - for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) - { - error = calcErrorPlanarOnlyBluePerceptual(block, colorO_enc[2], colorH_enc[2], colorV_enc[2], lowest_possible_error, BBBtable[colorO_enc[2]*64+colorV_enc[2]], CCCtable[colorH_enc[2]*64+colorV_enc[2]], best_error_blue_sofar); - if(error < best_error) - { - best_error = error; - best_colorO_enc[2] = colorO_enc[2]; - best_colorH_enc[2] = colorH_enc[2]; - best_colorV_enc[2] = colorV_enc[2]; - } - } - } - } - } - - if(best_error < best_error_planar_blue) - best_error_planar_blue = best_error; - - compressed57_1 = 0; - compressed57_2 = 0; - PUTBITSHIGH( compressed57_1, best_colorO_enc[0], 6, 63); - PUTBITSHIGH( compressed57_1, best_colorO_enc[1], 7, 57); - PUTBITSHIGH( compressed57_1, best_colorO_enc[2], 6, 50); - PUTBITSHIGH( compressed57_1, best_colorH_enc[0], 6, 44); - PUTBITSHIGH( compressed57_1, best_colorH_enc[1], 7, 38); - PUTBITS( compressed57_2, best_colorH_enc[2], 6, 31); - PUTBITS( compressed57_2, best_colorV_enc[0], 6, 25); - PUTBITS( compressed57_2, best_colorV_enc[1], 7, 19); - PUTBITS( compressed57_2, best_colorV_enc[2], 6, 12); - -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// This function uses real exhaustive search for the planar mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockPlanar57Exhaustive(uint8 *img, int width,int height,int startx,int starty, unsigned int &compressed57_1, unsigned int &compressed57_2, unsigned int best_error_sofar, unsigned int best_error_red, unsigned int best_error_green, unsigned int best_error_blue) -{ - int colorO_enc[3], colorH_enc[3], colorV_enc[3]; - int best_colorO_enc[3], best_colorH_enc[3], best_colorV_enc[3]; - - unsigned int error; - unsigned int best_error; - unsigned int lowest_possible_error; - unsigned int best_error_red_sofar; - unsigned int best_error_green_sofar; - unsigned int best_error_blue_sofar; - unsigned int BBBtable[128*128]; - unsigned int CCCtable[128*128]; - - uint8 block[4*4*4]; - - // Use 4 bytes per pixel to make it 32-word aligned. - int count = 0; - int xx, yy; - for(yy=0; yy<4; yy++) - { - for(xx = 0; xx<4; xx++) - { - block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block[(count)*4+3] = 0; - count++; - } - } - - // The task is to calculate the sum of the error over the entire area of the block. - // - // The block can be partitioned into: O A A A - // B D D C - // B D C D - // B C D D - // where the error in - // O only depends on colorO - // A only depends on colorO and colorH - // B only depends on colorO and colorV - // C only depends on colorH and colorV - // D depends on all three (colorO, colorH and colorV) - // - // Note that B can be precalculated for all combinations of colorO and colorV - // and the precalculated values can be used instead of calculating it in the inner loop. - // The same applies to C. - // - // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error - - // Precalc BBB errors - for(colorO_enc[0] = 0; colorO_enc[0]<64; colorO_enc[0]++) - { - for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) - { - BBBtable[colorO_enc[0]*64+colorV_enc[0]] = calcBBBred(block, colorO_enc[0], colorV_enc[0]); - } - } - // Precalc CCC errors - for(colorH_enc[0] = 0; colorH_enc[0]<64; colorH_enc[0]++) - { - for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) - { - CCCtable[colorH_enc[0]*64+colorV_enc[0]] = calcCCCred(block, colorH_enc[0], colorV_enc[0]); - } - } - best_error = MAXERR1000; - best_error_red_sofar = JAS_MIN(best_error_red, best_error_sofar); - for(colorO_enc[0] = 0; colorO_enc[0]<64; colorO_enc[0]++) - { - for(colorH_enc[0] = 0; colorH_enc[0]<64; colorH_enc[0]++) - { - lowest_possible_error = calcLowestPossibleRedOH(block, colorO_enc[0], colorH_enc[0], best_error_red_sofar); - if(lowest_possible_error <= best_error_red_sofar) - { - for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) - { - error = calcErrorPlanarOnlyRed(block, colorO_enc[0], colorH_enc[0], colorV_enc[0], lowest_possible_error, BBBtable[colorO_enc[0]*64+colorV_enc[0]], CCCtable[colorH_enc[0]*64+colorV_enc[0]], best_error_red_sofar); - if(error < best_error) - { - best_error = error; - best_colorO_enc[0] = colorO_enc[0]; - best_colorH_enc[0] = colorH_enc[0]; - best_colorV_enc[0] = colorV_enc[0]; - } - } - } - } - } - - // The task is to calculate the sum of the error over the entire area of the block. - // - // The block can be partitioned into: O A A A - // B D D C - // B D C D - // B C D D - // where the error in - // O only depends on colorO - // A only depends on colorO and colorH - // B only depends on colorO and colorV - // C only depends on colorH and colorV - // D depends on all three (colorO, colorH and colorV) - // - // Note that B can be precalculated for all combinations of colorO and colorV - // and the precalculated values can be used instead of calculating it in the inner loop. - // The same applies to C. - // - // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error - - // Precalc BBB errors - for(colorO_enc[1] = 0; colorO_enc[1]<128; colorO_enc[1]++) - { - for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) - { - BBBtable[colorO_enc[1]*128+colorV_enc[1]] = calcBBBgreen(block, colorO_enc[1], colorV_enc[1]); - } - } - // Precalc CCC errors - for(colorH_enc[1] = 0; colorH_enc[1]<128; colorH_enc[1]++) - { - for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) - { - CCCtable[colorH_enc[1]*128+colorV_enc[1]] = calcCCCgreen(block, colorH_enc[1], colorV_enc[1]); - } - } - best_error = MAXERR1000; - best_error_green_sofar = JAS_MIN(best_error_green, best_error_sofar); - for(colorO_enc[1] = 0; colorO_enc[1]<128; colorO_enc[1]++) - { - for(colorH_enc[1] = 0; colorH_enc[1]<128; colorH_enc[1]++) - { - lowest_possible_error = calcLowestPossibleGreenOH(block, colorO_enc[1], colorH_enc[1], best_error_green_sofar); - if(lowest_possible_error <= best_error_green_sofar) - { - for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) - { - error = calcErrorPlanarOnlyGreen(block, colorO_enc[1], colorH_enc[1], colorV_enc[1], lowest_possible_error, BBBtable[colorO_enc[1]*128+colorV_enc[1]], CCCtable[colorH_enc[1]*128+colorV_enc[1]], best_error_green_sofar); - if(error < best_error) - { - best_error = error; - best_colorO_enc[1] = colorO_enc[1]; - best_colorH_enc[1] = colorH_enc[1]; - best_colorV_enc[1] = colorV_enc[1]; - } - } - } - } - } - - // The task is to calculate the sum of the error over the entire area of the block. - // - // The block can be partitioned into: O A A A - // B D D C - // B D C D - // B C D D - // where the error in - // O only depends on colorO - // A only depends on colorO and colorH - // B only depends on colorO and colorV - // C only depends on colorH and colorV - // D depends on all three (colorO, colorH and colorV) - // - // Note that B can be precalculated for all combinations of colorO and colorV - // and the precalculated values can be used instead of calculating it in the inner loop. - // The same applies to C. - // - // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error - - // Precalc BBB errors - for(colorO_enc[2] = 0; colorO_enc[2]<64; colorO_enc[2]++) - { - for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) - { - BBBtable[colorO_enc[2]*64+colorV_enc[2]] = calcBBBblue(block, colorO_enc[2], colorV_enc[2]); - } - } - // Precalc CCC errors - for(colorH_enc[2] = 0; colorH_enc[2]<64; colorH_enc[2]++) - { - for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) - { - CCCtable[colorH_enc[2]*64+colorV_enc[2]] = calcCCCblue(block, colorH_enc[2], colorV_enc[2]); - } - } - best_error = MAXERR1000; - best_error_blue_sofar = JAS_MIN(best_error_blue, best_error_sofar); - for(colorO_enc[2] = 0; colorO_enc[2]<64; colorO_enc[2]++) - { - for(colorH_enc[2] = 0; colorH_enc[2]<64; colorH_enc[2]++) - { - lowest_possible_error = calcLowestPossibleBlueOH(block, colorO_enc[2], colorH_enc[2], best_error_blue_sofar); - if(lowest_possible_error <= best_error_blue_sofar) - { - for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) - { - error = calcErrorPlanarOnlyBlue(block, colorO_enc[2], colorH_enc[2], colorV_enc[2], lowest_possible_error, BBBtable[colorO_enc[2]*64+colorV_enc[2]], CCCtable[colorH_enc[2]*64+colorV_enc[2]], best_error_blue_sofar); - if(error < best_error) - { - best_error = error; - best_colorO_enc[2] = colorO_enc[2]; - best_colorH_enc[2] = colorH_enc[2]; - best_colorV_enc[2] = colorV_enc[2]; - } - } - } - } - } - - compressed57_1 = 0; - compressed57_2 = 0; - PUTBITSHIGH( compressed57_1, best_colorO_enc[0], 6, 63); - PUTBITSHIGH( compressed57_1, best_colorO_enc[1], 7, 57); - PUTBITSHIGH( compressed57_1, best_colorO_enc[2], 6, 50); - PUTBITSHIGH( compressed57_1, best_colorH_enc[0], 6, 44); - PUTBITSHIGH( compressed57_1, best_colorH_enc[1], 7, 38); - PUTBITS( compressed57_2, best_colorH_enc[2], 6, 31); - PUTBITS( compressed57_2, best_colorV_enc[0], 6, 25); - PUTBITS( compressed57_2, best_colorV_enc[1], 7, 19); - PUTBITS( compressed57_2, best_colorV_enc[2], 6, 12); - -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col0_Rpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_R) -{ - unsigned int block_error = 0, - best_block_error = MAXERR1000, - pixel_error, - best_pixel_error; - int diff; - uint8 color; - uint8 possible_colors[3]; - - color = ((colorRGB444_packed >> 8) & 0xf)*17; - - // Test all distances - for (uint8 d = 0; d < 8; d++) - { - - possible_colors[0] = CLAMP(0,color - table59T[d],255); - possible_colors[1] = CLAMP(0,color,255); - possible_colors[2] = CLAMP(0,color + table59T[d],255); - - // Loop block - for (int x = 0; x < 16; x++) - { - best_pixel_error = MAXERR1000; - - // Loop possible block colors - for (uint8 c = 0; c < 3; c++) - { - - diff = block[4*x + R] - CLAMP(0,possible_colors[c],255); - - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff); - - // Choose best error - if (pixel_error < best_pixel_error) - best_pixel_error = pixel_error; - } - - precalc_err_col0_R[((colorRGB444_packed>>8)*8 + d)*16 + x] = (unsigned int) best_pixel_error; - } - - } - -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col0_R(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_R) -{ - unsigned int block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - int diff; - uint8 color; - uint8 possible_colors[3]; - - color = ((colorRGB444_packed >> 8) & 0xf)*17; - - // Test all distances - for (uint8 d = 0; d < 8; d++) - { - - possible_colors[0] = CLAMP(0,color - table59T[d],255); - possible_colors[1] = CLAMP(0,color,255); - possible_colors[2] = CLAMP(0,color + table59T[d],255); - - // Loop block - for (int x = 0; x < 16; x++) - { - best_pixel_error = MAXIMUM_ERROR; - - // Loop possible block colors - for (uint8 c = 0; c < 3; c++) - { - - diff = block[4*x + R] - CLAMP(0,possible_colors[c],255); - - pixel_error = SQUARE(diff); - - // Choose best error - if (pixel_error < best_pixel_error) - best_pixel_error = pixel_error; - } - precalc_err_col0_R[((colorRGB444_packed>>8)*8 + d)*16 + x] = (unsigned int) best_pixel_error; - } - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col0_RGpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_RG) -{ - unsigned int block_error = 0, - best_block_error = MAXERR1000, - pixel_error, - best_pixel_error; - int diff[3]; - uint8 color[3]; - uint8 possible_colors[3][2]; - - color[R] = ((colorRGB444_packed >> 8) & 0xf)*17; - color[G] = ((colorRGB444_packed >> 4) & 0xf)*17; - - // Test all distances - for (uint8 d = 0; d < 8; d++) - { - - possible_colors[0][R] = CLAMP(0,color[R] - table59T[d],255); - possible_colors[0][G] = CLAMP(0,color[G] - table59T[d],255); - - possible_colors[1][R] = CLAMP(0,color[R],255); - possible_colors[1][G] = CLAMP(0,color[G],255); - - possible_colors[2][R] = CLAMP(0,color[R] + table59T[d],255); - possible_colors[2][G] = CLAMP(0,color[G] + table59T[d],255); - - - - // Loop block - for (int x = 0; x < 16; x++) - { - best_pixel_error = MAXERR1000; - - // Loop possible block colors - for (uint8 c = 0; c < 3; c++) - { - - diff[R] = block[4*x + R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = block[4*x + G] - CLAMP(0,possible_colors[c][G],255); - - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]); - - // Choose best error - if (pixel_error < best_pixel_error) - best_pixel_error = pixel_error; - } - precalc_err_col0_RG[((colorRGB444_packed>>4)*8 + d)*16 + x] = (unsigned int) best_pixel_error; - } - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col0_RG(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_RG) -{ - unsigned int block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - int diff[3]; - uint8 color[3]; - uint8 possible_colors[3][2]; - - color[R] = ((colorRGB444_packed >> 8) & 0xf)*17; - color[G] = ((colorRGB444_packed >> 4) & 0xf)*17; - - // Test all distances - for (uint8 d = 0; d < 8; d++) - { - - possible_colors[0][R] = CLAMP(0,color[R] - table59T[d],255); - possible_colors[0][G] = CLAMP(0,color[G] - table59T[d],255); - - possible_colors[1][R] = CLAMP(0,color[R],255); - possible_colors[1][G] = CLAMP(0,color[G],255); - - possible_colors[2][R] = CLAMP(0,color[R] + table59T[d],255); - possible_colors[2][G] = CLAMP(0,color[G] + table59T[d],255); - - // Loop block - for (int x = 0; x < 16; x++) - { - best_pixel_error = MAXIMUM_ERROR; - - // Loop possible block colors - for (uint8 c = 0; c < 3; c++) - { - diff[R] = block[4*x + R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = block[4*x + G] - CLAMP(0,possible_colors[c][G],255); - - pixel_error = SQUARE(diff[R]) + SQUARE(diff[G]); - - // Choose best error - if (pixel_error < best_pixel_error) - best_pixel_error = pixel_error; - } - precalc_err_col0_RG[((colorRGB444_packed>>4)*8 + d)*16 + x] = (unsigned int) best_pixel_error; - } - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col1_Rpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_R) -{ - unsigned int pixel_error; - int diff; - uint8 color; - - color = ((colorRGB444_packed >> 8) & 0xf)*17; - - // Loop block - for (int x = 0; x < 16; x++) - { - diff = block[4*x + R] - color; - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff); - precalc_err_col1_R[((colorRGB444_packed>>8))*16 + x] = (unsigned int) pixel_error; - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -/** - * Calculate the error for the block at position (startx,starty) - * The parameters needed for reconstruction is calculated as well - * - * In the 59T bit mode, we only have pattern T. - */ -void precalcError59T_col1_R(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_R) -{ - unsigned int pixel_error; - int diff; - uint8 color; - - color = ((colorRGB444_packed >> 8) & 0xf)*17; - - // Loop block - for (int x = 0; x < 16; x++) - { - diff = block[4*x + R] - color; - pixel_error = SQUARE(diff); - precalc_err_col1_R[((colorRGB444_packed>>8))*16 + x] = (unsigned int) pixel_error; - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col1_RGpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_RG) -{ - unsigned int pixel_error; - int diff[3]; - uint8 color[2]; - - color[R] = ((colorRGB444_packed >> 8) & 0xf)*17; - color[G] = ((colorRGB444_packed >> 4) & 0xf)*17; - - // Loop block - for (int x = 0; x < 16; x++) - { - diff[R] = block[4*x + R] - color[R]; - diff[G] = block[4*x + G] - color[G]; - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]); - precalc_err_col1_RG[((colorRGB444_packed>>4))*16 + x] = (unsigned int) pixel_error; - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col1_RG(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_RG) -{ - unsigned int pixel_error; - int diff[3]; - uint8 color[2]; - - color[R] = ((colorRGB444_packed >> 8) & 0xf)*17; - color[G] = ((colorRGB444_packed >> 4) & 0xf)*17; - - // Loop block - for (int x = 0; x < 16; x++) - { - diff[R] = block[4*x + R] - color[R]; - diff[G] = block[4*x + G] - color[G]; - pixel_error = SQUARE(diff[R]) + SQUARE(diff[G]); - precalc_err_col1_RG[((colorRGB444_packed>>4))*16 + x] = (unsigned int) pixel_error; - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col0_RGBpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_RGB) -{ - unsigned int block_error = 0, - best_block_error = MAXERR1000, - pixel_error, - best_pixel_error; - uint8 color[3]; - int possible_colors[3][3]; - unsigned int *precalc_err_col0_RGB_adr; - -#define ONEPOINT59RGB_PERCEP(xval) \ - /* Loop possible block colors */\ - /* unroll loop for (uint8 c = 0; c < 3; c++) */\ - {\ - best_pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[block[4*xval + R] - possible_colors[0][R]]\ - + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[block[4*xval + G] - possible_colors[0][G]] \ - + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[block[4*xval + B] - possible_colors[0][B]];\ - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[block[4*xval + R] - possible_colors[1][R]]\ - + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[block[4*xval + G] - possible_colors[1][G]]\ - + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[block[4*xval + B] - possible_colors[1][B]];\ - if (pixel_error < best_pixel_error)\ - best_pixel_error = pixel_error;\ - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[block[4*xval + R] - possible_colors[2][R]]\ - + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[block[4*xval + G] - possible_colors[2][G]]\ - + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[block[4*xval + B] - possible_colors[2][B]];\ - if (pixel_error < best_pixel_error)\ - best_pixel_error = pixel_error;\ - }\ - precalc_err_col0_RGB_adr[xval] = (unsigned int) best_pixel_error;\ - -#define ONETABLE59RGB_PERCEP(dval) \ - possible_colors[0][R] = clamp_table[color[R] - table59T[dval]+255]-255;\ - possible_colors[0][G] = clamp_table[color[G] - table59T[dval]+255]-255;\ - possible_colors[0][B] = clamp_table[color[B] - table59T[dval]+255]-255;\ - possible_colors[1][R] = color[R]-255;\ - possible_colors[1][G] = color[G]-255;\ - possible_colors[1][B] = color[B]-255;\ - possible_colors[2][R] = clamp_table[color[R] + table59T[dval]+255]-255;\ - possible_colors[2][G] = clamp_table[color[G] + table59T[dval]+255]-255;\ - possible_colors[2][B] = clamp_table[color[B] + table59T[dval]+255]-255;\ - precalc_err_col0_RGB_adr = &precalc_err_col0_RGB[(colorRGB444_packed*8 + dval)*16];\ - /* Loop block */\ - /* unroll loop for (int x = 0; x < 16; x++) */\ - {\ - ONEPOINT59RGB_PERCEP(0)\ - ONEPOINT59RGB_PERCEP(1)\ - ONEPOINT59RGB_PERCEP(2)\ - ONEPOINT59RGB_PERCEP(3)\ - ONEPOINT59RGB_PERCEP(4)\ - ONEPOINT59RGB_PERCEP(5)\ - ONEPOINT59RGB_PERCEP(6)\ - ONEPOINT59RGB_PERCEP(7)\ - ONEPOINT59RGB_PERCEP(8)\ - ONEPOINT59RGB_PERCEP(9)\ - ONEPOINT59RGB_PERCEP(10)\ - ONEPOINT59RGB_PERCEP(11)\ - ONEPOINT59RGB_PERCEP(12)\ - ONEPOINT59RGB_PERCEP(13)\ - ONEPOINT59RGB_PERCEP(14)\ - ONEPOINT59RGB_PERCEP(15)\ - }\ - - color[R] = (((colorRGB444_packed >> 8) ) << 4) | ((colorRGB444_packed >> 8) ) ; - color[G] = (((colorRGB444_packed >> 4) & 0xf) << 4) | ((colorRGB444_packed >> 4) & 0xf) ; - color[B] = (((colorRGB444_packed) & 0xf) << 4) | ((colorRGB444_packed) & 0xf) ; - - /* Test all distances */ - /* unroll loop for (uint8 d = 0; d < 8; ++d) */ - { - ONETABLE59RGB_PERCEP(0) - ONETABLE59RGB_PERCEP(1) - ONETABLE59RGB_PERCEP(2) - ONETABLE59RGB_PERCEP(3) - ONETABLE59RGB_PERCEP(4) - ONETABLE59RGB_PERCEP(5) - ONETABLE59RGB_PERCEP(6) - ONETABLE59RGB_PERCEP(7) - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col0_RGB(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_RGB) -{ - unsigned int block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - uint8 color[3]; - int possible_colors[3][3]; - unsigned int *precalc_err_col0_RGB_adr; - -#define ONEPOINT59RGB(xval) \ - /* Loop possible block colors */\ - /* unroll loop for (uint8 c = 0; c < 3; c++) */\ - {\ - best_pixel_error = square_table[block[4*xval + R] - possible_colors[0][R]]\ - + square_table[block[4*xval + G] - possible_colors[0][G]] \ - + square_table[block[4*xval + B] - possible_colors[0][B]];\ - pixel_error = square_table[block[4*xval + R] - possible_colors[1][R]]\ - + square_table[block[4*xval + G] - possible_colors[1][G]]\ - + square_table[block[4*xval + B] - possible_colors[1][B]];\ - if (pixel_error < best_pixel_error)\ - best_pixel_error = pixel_error;\ - pixel_error = square_table[block[4*xval + R] - possible_colors[2][R]]\ - + square_table[block[4*xval + G] - possible_colors[2][G]]\ - + square_table[block[4*xval + B] - possible_colors[2][B]];\ - if (pixel_error < best_pixel_error)\ - best_pixel_error = pixel_error;\ - }\ - precalc_err_col0_RGB_adr[xval] = (unsigned int) best_pixel_error;\ - -#define ONETABLE59RGB(dval) \ - possible_colors[0][R] = clamp_table[color[R] - table59T[dval]+255]-255;\ - possible_colors[0][G] = clamp_table[color[G] - table59T[dval]+255]-255;\ - possible_colors[0][B] = clamp_table[color[B] - table59T[dval]+255]-255;\ - possible_colors[1][R] = color[R]-255;\ - possible_colors[1][G] = color[G]-255;\ - possible_colors[1][B] = color[B]-255;\ - possible_colors[2][R] = clamp_table[color[R] + table59T[dval]+255]-255;\ - possible_colors[2][G] = clamp_table[color[G] + table59T[dval]+255]-255;\ - possible_colors[2][B] = clamp_table[color[B] + table59T[dval]+255]-255;\ - precalc_err_col0_RGB_adr = &precalc_err_col0_RGB[(colorRGB444_packed*8 + dval)*16];\ - /* Loop block */\ - /* unroll loop for (int x = 0; x < 16; x++) */\ - {\ - ONEPOINT59RGB(0)\ - ONEPOINT59RGB(1)\ - ONEPOINT59RGB(2)\ - ONEPOINT59RGB(3)\ - ONEPOINT59RGB(4)\ - ONEPOINT59RGB(5)\ - ONEPOINT59RGB(6)\ - ONEPOINT59RGB(7)\ - ONEPOINT59RGB(8)\ - ONEPOINT59RGB(9)\ - ONEPOINT59RGB(10)\ - ONEPOINT59RGB(11)\ - ONEPOINT59RGB(12)\ - ONEPOINT59RGB(13)\ - ONEPOINT59RGB(14)\ - ONEPOINT59RGB(15)\ - }\ - - color[R] = (((colorRGB444_packed >> 8) ) << 4) | ((colorRGB444_packed >> 8) ) ; - color[G] = (((colorRGB444_packed >> 4) & 0xf) << 4) | ((colorRGB444_packed >> 4) & 0xf) ; - color[B] = (((colorRGB444_packed) & 0xf) << 4) | ((colorRGB444_packed) & 0xf) ; - - /* Test all distances */ - /* unroll loop for (uint8 d = 0; d < 8; ++d) */ - { - ONETABLE59RGB(0) - ONETABLE59RGB(1) - ONETABLE59RGB(2) - ONETABLE59RGB(3) - ONETABLE59RGB(4) - ONETABLE59RGB(5) - ONETABLE59RGB(6) - ONETABLE59RGB(7) - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col1_RGBpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_RGB) -{ - unsigned int pixel_error; - int diff[3]; - uint8 colorRGB[3]; - - colorRGB[0] = ((colorRGB444_packed >> 8) & 0xf)*17; - colorRGB[1] = ((colorRGB444_packed >> 4) & 0xf)*17; - colorRGB[2] = ((colorRGB444_packed >> 0) & 0xf)*17; - - // Loop block - for (int x = 0; x < 16; x++) - { - diff[R] = block[4*x + R] - colorRGB[R]; - diff[G] = block[4*x + G] - colorRGB[G]; - diff[B] = block[4*x + B] - colorRGB[B]; - - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]) + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*SQUARE(diff[B]); - - precalc_err_col1_RGB[(colorRGB444_packed)*16 + x] = (unsigned int) pixel_error; - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in exhaustive compression of the T-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError59T_col1_RGB(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_RGB) -{ - unsigned int pixel_error; - int diff[3]; - uint8 colorRGB[3]; - - colorRGB[0] = ((colorRGB444_packed >> 8) & 0xf)*17; - colorRGB[1] = ((colorRGB444_packed >> 4) & 0xf)*17; - colorRGB[2] = ((colorRGB444_packed >> 0) & 0xf)*17; - - // Loop block - for (int x = 0; x < 16; x++) - { - diff[R] = block[4*x + R] - colorRGB[R]; - diff[G] = block[4*x + G] - colorRGB[G]; - diff[B] = block[4*x + B] - colorRGB[B]; - - pixel_error = SQUARE(diff[R]) + SQUARE(diff[G]) + SQUARE(diff[B]); - precalc_err_col1_RGB[(colorRGB444_packed)*16 + x] = (unsigned int) pixel_error; - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimal error for the T-mode when compressing exhaustively. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateError59TusingPrecalcRperceptual1000(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_R, unsigned int *precalc_err_col1_R, unsigned int best_error_so_far) -{ - unsigned int block_error = 0, - best_block_error = MAXERR1000; - - unsigned int *pixel_error_col0_base_adr; - unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; - -#define FIRSTCHOICE59R_PERCEP\ - if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ - block_error = *pixel_error_col0_adr;\ - else\ - block_error = *pixel_error_col1_adr;\ - -#define CHOICE59R_PERCEP(xval)\ - if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ - block_error += pixel_error_col0_adr[xval];\ - else\ - block_error += pixel_error_col1_adr[xval];\ - -#define ONETABLE59R_PERCEP(dval) \ - pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ - /* unroll loop for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ - {\ - FIRSTCHOICE59R_PERCEP\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R_PERCEP(1)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R_PERCEP(2)\ - CHOICE59R_PERCEP(3)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R_PERCEP(4)\ - CHOICE59R_PERCEP(5)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R_PERCEP(6)\ - CHOICE59R_PERCEP(7)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R_PERCEP(8)\ - CHOICE59R_PERCEP(9)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R_PERCEP(10)\ - CHOICE59R_PERCEP(11)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R_PERCEP(12)\ - CHOICE59R_PERCEP(13)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R_PERCEP(14)\ - CHOICE59R_PERCEP(15)\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - if (block_error < best_block_error)\ - best_block_error = block_error;\ - - pixel_error_col0_base_adr = &precalc_err_col0_R[((colorsRGB444_packed[0]>>8)*8)*16]; - pixel_error_col1_adr = &precalc_err_col1_R[((colorsRGB444_packed[1]>>8))*16]; - - // Test all distances - /* unroll loop for (uint8 d = 0; d < 8; d++) */ - { - ONETABLE59R_PERCEP(0) - ONETABLE59R_PERCEP(1) - ONETABLE59R_PERCEP(2) - ONETABLE59R_PERCEP(3) - ONETABLE59R_PERCEP(4) - ONETABLE59R_PERCEP(5) - ONETABLE59R_PERCEP(6) - ONETABLE59R_PERCEP(7) - } - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimal error for the T-mode when compressing exhaustively. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateError59TusingPrecalcR(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_R, unsigned int *precalc_err_col1_R, unsigned int best_error_so_far) -{ - unsigned int block_error = 0, - best_block_error = MAXIMUM_ERROR; - - unsigned int *pixel_error_col0_base_adr; - unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; - -#define FIRSTCHOICE59R\ - if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ - block_error = *pixel_error_col0_adr;\ - else\ - block_error = *pixel_error_col1_adr;\ - -#define CHOICE59R(xval)\ - if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ - block_error += pixel_error_col0_adr[xval];\ - else\ - block_error += pixel_error_col1_adr[xval];\ - -#define ONETABLE59R(dval) \ - pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ - /* unroll loop for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ - {\ - FIRSTCHOICE59R\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R(1)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R(2)\ - CHOICE59R(3)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R(4)\ - CHOICE59R(5)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R(6)\ - CHOICE59R(7)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R(8)\ - CHOICE59R(9)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R(10)\ - CHOICE59R(11)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R(12)\ - CHOICE59R(13)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59R(14)\ - CHOICE59R(15)\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - if (block_error < best_block_error)\ - best_block_error = block_error;\ - - pixel_error_col0_base_adr = &precalc_err_col0_R[((colorsRGB444_packed[0]>>8)*8)*16]; - pixel_error_col1_adr = &precalc_err_col1_R[((colorsRGB444_packed[1]>>8))*16]; - - - // Test all distances - /* unroll loop for (uint8 d = 0; d < 8; d++) */ - { - ONETABLE59R(0) - ONETABLE59R(1) - ONETABLE59R(2) - ONETABLE59R(3) - ONETABLE59R(4) - ONETABLE59R(5) - ONETABLE59R(6) - ONETABLE59R(7) - } - - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimal error for the T-mode when compressing exhaustively. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateError59TusingPrecalcRGperceptual1000(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_RG, unsigned int *precalc_err_col1_RG, unsigned int best_error_so_far) -{ - unsigned int block_error = 0, - best_block_error = MAXERR1000; - - unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; - unsigned int *pixel_error_col0_base_adr; - -#define FIRSTCHOICE59RG_PERCEP \ - if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ - block_error = *pixel_error_col0_adr;\ - else\ - block_error = *pixel_error_col1_adr;\ - - -#define CHOICE59RG_PERCEP(xval) \ - if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ - block_error += pixel_error_col0_adr[xval];\ - else\ - block_error += pixel_error_col1_adr[xval];\ - -#define ONETABLE59RG_PERCEP(dval)\ - pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ - /* unroll loop for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ - {\ - FIRSTCHOICE59RG_PERCEP\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG_PERCEP(1)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG_PERCEP(2)\ - CHOICE59RG_PERCEP(3)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG_PERCEP(4)\ - CHOICE59RG_PERCEP(5)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG_PERCEP(6)\ - CHOICE59RG_PERCEP(7)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG_PERCEP(8)\ - CHOICE59RG_PERCEP(9)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG_PERCEP(10)\ - CHOICE59RG_PERCEP(11)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG_PERCEP(12)\ - CHOICE59RG_PERCEP(13)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG_PERCEP(14)\ - CHOICE59RG_PERCEP(15)\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - if (block_error < best_block_error)\ - best_block_error = block_error;\ - - - pixel_error_col0_base_adr = &precalc_err_col0_RG[((colorsRGB444_packed[0]>>4)*8)*16]; - pixel_error_col1_adr = &precalc_err_col1_RG[((colorsRGB444_packed[1]>>4))*16]; - - // Test all distances - /* unroll loop for (uint8 d = 0; d < 8; d++) */ - { - - ONETABLE59RG_PERCEP(0) - ONETABLE59RG_PERCEP(1) - ONETABLE59RG_PERCEP(2) - ONETABLE59RG_PERCEP(3) - ONETABLE59RG_PERCEP(4) - ONETABLE59RG_PERCEP(5) - ONETABLE59RG_PERCEP(6) - ONETABLE59RG_PERCEP(7) - } - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimal error for the T-mode when compressing exhaustively. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateError59TusingPrecalcRG(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_RG, unsigned int *precalc_err_col1_RG, unsigned int best_error_so_far) -{ - unsigned int block_error = 0, - best_block_error = MAXIMUM_ERROR; - - unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; - unsigned int *pixel_error_col0_base_adr; - -#define FIRSTCHOICE59RG \ - if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ - block_error = *pixel_error_col0_adr;\ - else\ - block_error = *pixel_error_col1_adr;\ - -#define CHOICE59RG(xval) \ - if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ - block_error += pixel_error_col0_adr[xval];\ - else\ - block_error += pixel_error_col1_adr[xval];\ - -#define ONETABLE59RG(dval)\ - pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ - /* unroll loop for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ - {\ - FIRSTCHOICE59RG\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG(1)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG(2)\ - CHOICE59RG(3)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG(4)\ - CHOICE59RG(5)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG(6)\ - CHOICE59RG(7)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG(8)\ - CHOICE59RG(9)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG(10)\ - CHOICE59RG(11)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG(12)\ - CHOICE59RG(13)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59RG(14)\ - CHOICE59RG(15)\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - if (block_error < best_block_error)\ - best_block_error = block_error;\ - - pixel_error_col0_base_adr = &precalc_err_col0_RG[((colorsRGB444_packed[0]>>4)*8)*16]; - pixel_error_col1_adr = &precalc_err_col1_RG[((colorsRGB444_packed[1]>>4))*16]; - - // Test all distances - /* unroll loop for (uint8 d = 0; d < 8; d++) */ - { - ONETABLE59RG(0) - ONETABLE59RG(1) - ONETABLE59RG(2) - ONETABLE59RG(3) - ONETABLE59RG(4) - ONETABLE59RG(5) - ONETABLE59RG(6) - ONETABLE59RG(7) - } - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimal error for the T-mode when compressing exhaustively. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateError59TusingPrecalcRGBperceptual1000(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_RGB, unsigned int *precalc_err_col1_RGB, unsigned int best_error_so_far) -{ - unsigned int block_error = 0, - best_block_error = MAXERR1000; - unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; - unsigned int *pixel_error_col0_base_adr; - -#define FIRSTCHOICE59_PERCEP \ - if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ - block_error = *pixel_error_col0_adr;\ - else\ - block_error = *pixel_error_col1_adr;\ - -#define CHOICE59_PERCEP(xval) \ - if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ - block_error += pixel_error_col0_adr[xval];\ - else\ - block_error += pixel_error_col1_adr[xval];\ - -#define ONETABLE59T_PERCEP(dval)\ - pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ - /* unroll for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ - {\ - FIRSTCHOICE59_PERCEP\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59_PERCEP(1)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59_PERCEP(2)\ - CHOICE59_PERCEP(3)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59_PERCEP(4)\ - CHOICE59_PERCEP(5)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59_PERCEP(6)\ - CHOICE59_PERCEP(7)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59_PERCEP(8)\ - CHOICE59_PERCEP(9)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59_PERCEP(10)\ - CHOICE59_PERCEP(11)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59_PERCEP(12)\ - CHOICE59_PERCEP(13)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59_PERCEP(14)\ - CHOICE59_PERCEP(15)\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - if (block_error < best_block_error)\ - best_block_error = block_error;\ - - pixel_error_col1_adr = &precalc_err_col1_RGB[(colorsRGB444_packed[1])*16]; - pixel_error_col0_base_adr = &precalc_err_col0_RGB[(colorsRGB444_packed[0]*8)*16]; - - // Test all distances - /* unroll loop for (uint8 d = 0; d < 8; d++)*/ - { - ONETABLE59T_PERCEP(0) - ONETABLE59T_PERCEP(1) - ONETABLE59T_PERCEP(2) - ONETABLE59T_PERCEP(3) - ONETABLE59T_PERCEP(4) - ONETABLE59T_PERCEP(5) - ONETABLE59T_PERCEP(6) - ONETABLE59T_PERCEP(7) - } - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimal error for the T-mode when compressing exhaustively. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateError59TusingPrecalcRGB(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_RGB, unsigned int *precalc_err_col1_RGB, unsigned int best_error_so_far) -{ - unsigned int block_error = 0, - best_block_error = MAXIMUM_ERROR; - unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; - unsigned int *pixel_error_col0_base_adr; - -#define FIRSTCHOICE59 \ - if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ - block_error = *pixel_error_col0_adr;\ - else\ - block_error = *pixel_error_col1_adr;\ - -#define CHOICE59(xval) \ - if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ - block_error += pixel_error_col0_adr[xval];\ - else\ - block_error += pixel_error_col1_adr[xval];\ - -#define ONETABLE59T(dval)\ - pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ - /* unroll for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ - {\ - FIRSTCHOICE59\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59(1)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59(2)\ - CHOICE59(3)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59(4)\ - CHOICE59(5)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59(6)\ - CHOICE59(7)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59(8)\ - CHOICE59(9)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59(10)\ - CHOICE59(11)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59(12)\ - CHOICE59(13)\ - if( block_error < best_error_so_far)\ - {\ - CHOICE59(14)\ - CHOICE59(15)\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - if (block_error < best_block_error)\ - best_block_error = block_error;\ - - pixel_error_col1_adr = &precalc_err_col1_RGB[(colorsRGB444_packed[1])*16]; - pixel_error_col0_base_adr = &precalc_err_col0_RGB[(colorsRGB444_packed[0]*8)*16]; - - // Test all distances - /* unroll loop for (uint8 d = 0; d < 8; d++)*/ - { - ONETABLE59T(0) - ONETABLE59T(1) - ONETABLE59T(2) - ONETABLE59T(3) - ONETABLE59T(4) - ONETABLE59T(5) - ONETABLE59T(6) - ONETABLE59T(7) - } - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// The below code should compress the block to 59 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// -//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| -//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// Note that this method might not return the best possible compression for the T-mode. It will only do so if the best possible T-representation -// is less than best_error_so_far. To guarantee that the best possible T-representation is found, the function should be called using -// best_error_so_far = 255*255*3*16, which is the maximum error for a block. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockTHUMB59TExhaustivePerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int best_error_so_far) -{ - uint8 colorsRGB444[2][3]; - unsigned int pixel_indices; - uint8 distance; - - uint8 block[4*4*4]; - - unsigned int *precalc_err_col0_RGB; - unsigned int *precalc_err_col1_RGB; - unsigned int *precalc_err_col0_RG; - unsigned int *precalc_err_col1_RG; - unsigned int *precalc_err_col0_R; - unsigned int *precalc_err_col1_R; - - int colorRGB444_packed; - - int colorsRGB444_packed[2]; - int best_colorsRGB444_packed[2]; - - unsigned int best_error_using_Tmode; - - // First compress block quickly to a resonable quality so that we can - // rule out all blocks that are of worse quality than that. - best_error_using_Tmode = (unsigned int) compressBlockTHUMB59TFastestOnlyColorPerceptual1000(img, width, height, startx, starty, best_colorsRGB444_packed); - if(best_error_using_Tmode < best_error_so_far) - best_error_so_far = best_error_using_Tmode; - - // Color numbering is reversed between the above function and the precalc functions below; swap colors. - int temp = best_colorsRGB444_packed[0]; - best_colorsRGB444_packed[0] = best_colorsRGB444_packed[1]; - best_colorsRGB444_packed[1] = temp; - - int xx,yy,count = 0; - - // Use 4 bytes per pixel to make it 32-word aligned. - for(xx = 0; xx<4; xx++) - { - for(yy=0; yy<4; yy++) - { - block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block[(count)*4+3] = 0; - count++; - } - } - - // Precalculate error for color 0 (which produces the upper half of the T) - precalc_err_col0_RGB = (unsigned int*) malloc(4096*8*16*sizeof(unsigned int)); - if(!precalc_err_col0_RGB){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) - { - precalcError59T_col0_RGBpercep1000(block, colorRGB444_packed, precalc_err_col0_RGB); - } - - // Precalculate error for color 1 (which produces the lower half of the T -- the lone color) - precalc_err_col1_RGB = (unsigned int*) malloc(4096*16*sizeof(unsigned int)); - if(!precalc_err_col1_RGB){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) - { - precalcError59T_col1_RGBpercep1000(block, colorRGB444_packed, precalc_err_col1_RGB); - } - - precalc_err_col0_RG = (unsigned int*) malloc(16*16*8*16*sizeof(unsigned int)); - if(!precalc_err_col0_RG){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) - { - precalcError59T_col0_RGpercep1000(block, colorRGB444_packed, precalc_err_col0_RG); - } - - precalc_err_col1_RG = (unsigned int*) malloc(16*16*16*sizeof(unsigned int)); - if(!precalc_err_col1_RG){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) - { - precalcError59T_col1_RGpercep1000(block, colorRGB444_packed, precalc_err_col1_RG); - } - - precalc_err_col0_R = (unsigned int*) malloc(16*8*16*sizeof(unsigned int)); - if(!precalc_err_col0_R){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) - { - precalcError59T_col0_Rpercep1000(block, colorRGB444_packed, precalc_err_col0_R); - } - - precalc_err_col1_R = (unsigned int*) malloc(16*16*sizeof(unsigned int)); - if(!precalc_err_col1_R){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) - { - precalcError59T_col1_Rpercep1000(block, colorRGB444_packed, precalc_err_col1_R); - } - - unsigned int error; - unsigned int avoided = 0; - unsigned int notavoided = 0; - - for(colorsRGB444[0][0] = 0; colorsRGB444[0][0] < 16; colorsRGB444[0][0]++) - { - for(colorsRGB444[1][0] = 0; colorsRGB444[1][0] < 16; colorsRGB444[1][0]++) - { - colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8); - colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8); - error = calculateError59TusingPrecalcRperceptual1000(block, colorsRGB444_packed, precalc_err_col0_R, precalc_err_col1_R, best_error_so_far); - if(error < best_error_so_far) - { - notavoided = notavoided + 1; - for(colorsRGB444[0][1] = 0; colorsRGB444[0][1] < 16; colorsRGB444[0][1]++) - { - colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8) + (colorsRGB444[0][1] <<4); - for(colorsRGB444[1][1] = 0; colorsRGB444[1][1] < 16; colorsRGB444[1][1]++) - { - colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8) + (colorsRGB444[1][1] <<4); - error = calculateError59TusingPrecalcRGperceptual1000(block, colorsRGB444_packed, precalc_err_col0_RG, precalc_err_col1_RG, best_error_so_far); - if(error < best_error_so_far) - { - for(colorsRGB444[0][2] = 0; colorsRGB444[0][2] < 16; colorsRGB444[0][2]++) - { - colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8) + (colorsRGB444[0][1] <<4) + colorsRGB444[0][2]; - for(colorsRGB444[1][2] = 0; colorsRGB444[1][2] < 16; colorsRGB444[1][2]++) - { - colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8) + (colorsRGB444[1][1] <<4) + colorsRGB444[1][2]; - error = calculateError59TusingPrecalcRGBperceptual1000(block, colorsRGB444_packed, precalc_err_col0_RGB, precalc_err_col1_RGB, best_error_so_far); - - if(error < best_error_so_far) - { - best_error_so_far = error; - best_error_using_Tmode = error; - best_colorsRGB444_packed[0] = colorsRGB444_packed[0]; - best_colorsRGB444_packed[1] = colorsRGB444_packed[1]; - } - } - } - } - } - } - } - } - } - - free(precalc_err_col0_RGB); - free(precalc_err_col1_RGB); - free(precalc_err_col0_RG); - free(precalc_err_col1_RG); - free(precalc_err_col0_R); - free(precalc_err_col1_R); - - // We have got the two best colors. Now find the best distance and pixel indices. - - // Color numbering are reversed between precalc and noSwap - colorsRGB444[0][0] = (best_colorsRGB444_packed[1] >> 8) & 0xf; - colorsRGB444[0][1] = (best_colorsRGB444_packed[1] >> 4) & 0xf; - colorsRGB444[0][2] = (best_colorsRGB444_packed[1] >> 0) & 0xf; - - colorsRGB444[1][0] = (best_colorsRGB444_packed[0] >> 8) & 0xf; - colorsRGB444[1][1] = (best_colorsRGB444_packed[0] >> 4) & 0xf; - colorsRGB444[1][2] = (best_colorsRGB444_packed[0] >> 0) & 0xf; - - calculateError59TnoSwapPerceptual1000(img, width, startx, starty, colorsRGB444, distance, pixel_indices); - - // Put the compress params into the compression block - packBlock59T(colorsRGB444, distance, pixel_indices, compressed1, compressed2); - - return best_error_using_Tmode; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// The below code should compress the block to 59 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// -//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| -//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// Note that this method might not return the best possible compression for the T-mode. It will only do so if the best possible T-representation -// is less than best_error_so_far. To guarantee that the best possible T-representation is found, the function should be called using -// best_error_so_far = 255*255*3*16, which is the maximum error for a block. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockTHUMB59TExhaustive(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int best_error_so_far) -{ - uint8 colorsRGB444[2][3]; - unsigned int pixel_indices; - uint8 distance; - - uint8 block[4*4*4]; - - unsigned int *precalc_err_col0_RGB; - unsigned int *precalc_err_col1_RGB; - unsigned int *precalc_err_col0_RG; - unsigned int *precalc_err_col1_RG; - unsigned int *precalc_err_col0_R; - unsigned int *precalc_err_col1_R; - - int colorRGB444_packed; - - int colorsRGB444_packed[2]; - int best_colorsRGB444_packed[2]; - - unsigned int best_error_using_Tmode; - - // First compress block quickly to a resonable quality so that we can - // rule out all blocks that are of worse quality than that. - best_error_using_Tmode = (unsigned int) compressBlockTHUMB59TFastestOnlyColor(img, width, height, startx, starty, best_colorsRGB444_packed); - if(best_error_using_Tmode < best_error_so_far) - best_error_so_far = best_error_using_Tmode; - - - // Colors numbering is reversed between the above function and the precalc below: - int temp = best_colorsRGB444_packed[0]; - best_colorsRGB444_packed[0] = best_colorsRGB444_packed[1]; - best_colorsRGB444_packed[1] = temp; - - int xx,yy,count = 0; - - // Use 4 bytes per pixel to make it 32-word aligned. - for(xx = 0; xx<4; xx++) - { - for(yy=0; yy<4; yy++) - { - block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block[(count)*4+3] = 0; - count++; - } - } - - // Precalculate error for color 0 (which produces the upper half of the T) - precalc_err_col0_RGB = (unsigned int*) malloc(4096*8*16*sizeof(unsigned int)); - if(!precalc_err_col0_RGB){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) - { - precalcError59T_col0_RGB(block, colorRGB444_packed, precalc_err_col0_RGB); - } - - // Precalculate error for color 1 (which produces the lower half of the T -- the lone color) - precalc_err_col1_RGB = (unsigned int*) malloc(4096*16*sizeof(unsigned int)); - if(!precalc_err_col1_RGB){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) - { - precalcError59T_col1_RGB(block, colorRGB444_packed, precalc_err_col1_RGB); - } - - precalc_err_col0_RG = (unsigned int*) malloc(16*16*8*16*sizeof(unsigned int)); - if(!precalc_err_col0_RG){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) - { - precalcError59T_col0_RG(block, colorRGB444_packed, precalc_err_col0_RG); - } - - precalc_err_col1_RG = (unsigned int*) malloc(16*16*16*sizeof(unsigned int)); - if(!precalc_err_col1_RG){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) - { - precalcError59T_col1_RG(block, colorRGB444_packed, precalc_err_col1_RG); - } - - precalc_err_col0_R = (unsigned int*) malloc(16*8*16*sizeof(unsigned int)); - if(!precalc_err_col0_R){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) - { - precalcError59T_col0_R(block, colorRGB444_packed, precalc_err_col0_R); - } - - precalc_err_col1_R = (unsigned int*) malloc(16*16*sizeof(unsigned int)); - if(!precalc_err_col1_R){printf("Out of memory allocating \n");exit(1);} - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) - { - precalcError59T_col1_R(block, colorRGB444_packed, precalc_err_col1_R); - } - - unsigned int error; - unsigned int avoided = 0; - unsigned int notavoided = 0; - - for(colorsRGB444[0][0] = 0; colorsRGB444[0][0] < 16; colorsRGB444[0][0]++) - { - for(colorsRGB444[1][0] = 0; colorsRGB444[1][0] < 16; colorsRGB444[1][0]++) - { - colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8); - colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8); - error = calculateError59TusingPrecalcR(block, colorsRGB444_packed, precalc_err_col0_R, precalc_err_col1_R, best_error_so_far); - if(error < best_error_so_far) - { - notavoided = notavoided + 1; - for(colorsRGB444[0][1] = 0; colorsRGB444[0][1] < 16; colorsRGB444[0][1]++) - { - colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8) + (colorsRGB444[0][1] <<4); - for(colorsRGB444[1][1] = 0; colorsRGB444[1][1] < 16; colorsRGB444[1][1]++) - { - colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8) + (colorsRGB444[1][1] <<4); - error = calculateError59TusingPrecalcRG(block, colorsRGB444_packed, precalc_err_col0_RG, precalc_err_col1_RG, best_error_so_far); - if(error < best_error_so_far) - { - for(colorsRGB444[0][2] = 0; colorsRGB444[0][2] < 16; colorsRGB444[0][2]++) - { - colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8) + (colorsRGB444[0][1] <<4) + colorsRGB444[0][2]; - for(colorsRGB444[1][2] = 0; colorsRGB444[1][2] < 16; colorsRGB444[1][2]++) - { - colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8) + (colorsRGB444[1][1] <<4) + colorsRGB444[1][2]; - error = calculateError59TusingPrecalcRGB(block, colorsRGB444_packed, precalc_err_col0_RGB, precalc_err_col1_RGB, best_error_so_far); - - if(error < best_error_so_far) - { - best_error_so_far = error; - best_error_using_Tmode = error; - best_colorsRGB444_packed[0] = colorsRGB444_packed[0]; - best_colorsRGB444_packed[1] = colorsRGB444_packed[1]; - } - } - } - } - } - } - } - } - } - - free(precalc_err_col0_RGB); - free(precalc_err_col1_RGB); - free(precalc_err_col0_RG); - free(precalc_err_col1_RG); - free(precalc_err_col0_R); - free(precalc_err_col1_R); - - // We have got the two best colors. Now find the best distance and pixel indices. - - // Color numbering are reversed between precalc and noSwap - colorsRGB444[0][0] = (best_colorsRGB444_packed[1] >> 8) & 0xf; - colorsRGB444[0][1] = (best_colorsRGB444_packed[1] >> 4) & 0xf; - colorsRGB444[0][2] = (best_colorsRGB444_packed[1] >> 0) & 0xf; - - colorsRGB444[1][0] = (best_colorsRGB444_packed[0] >> 8) & 0xf; - colorsRGB444[1][1] = (best_colorsRGB444_packed[0] >> 4) & 0xf; - colorsRGB444[1][2] = (best_colorsRGB444_packed[0] >> 0) & 0xf; - - calculateError59TnoSwap(img, width, startx, starty, colorsRGB444, distance, pixel_indices); - - // Put the compress params into the compression block - packBlock59T(colorsRGB444, distance, pixel_indices, compressed1, compressed2); - - return best_error_using_Tmode; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates tables used in the exhaustive compression of the H-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcErrorR_58Hperceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_errR) -{ - unsigned int block_error = 0, - best_block_error = MAXERR1000, - pixel_error, - best_pixel_error; - int diff[3]; - unsigned int pixel_colors; - uint8 possible_colors[2][3]; - uint8 colors[2][3]; - - decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) - { - possible_colors[0][R] = CLAMP(0,colors[0][R] - table58H[d],255); - possible_colors[1][R] = CLAMP(0,colors[0][R] + table58H[d],255); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXERR1000; - - // Loop possible block colors - for (uint8 c = 0; c < 2; ++c) - { - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - } - } - precalc_errR[((colorRGB444_packed>>8)*8 + d)*16 + (y*4)+x] = (unsigned int) best_pixel_error; - } - } - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates tables used in the exhaustive compression of the H-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcErrorR_58H(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_errR) -{ - double block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - int diff[3]; - unsigned int pixel_colors; - uint8 possible_colors[2][3]; - uint8 colors[2][3]; - - decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) - { - possible_colors[0][R] = CLAMP(0,colors[0][R] - table58H[d],255); - possible_colors[1][R] = CLAMP(0,colors[0][R] + table58H[d],255); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXIMUM_ERROR; - - // Loop possible block colors - for (uint8 c = 0; c < 2; ++c) - { - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - - pixel_error = weight[R]*SQUARE(diff[R]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - } - } - precalc_errR[((colorRGB444_packed>>8)*8 + d)*16 + (y*4)+x] = (unsigned int) best_pixel_error; - } - } - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates tables used in the exhaustive compression of the H-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcErrorRG_58Hperceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_errRG) -{ - unsigned int block_error = 0, - best_block_error = MAXERR1000, - pixel_error, - best_pixel_error; - int diff[3]; - unsigned int pixel_colors; - uint8 possible_colors[2][3]; - uint8 colors[2][3]; - - decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) - { - possible_colors[0][R] = CLAMP(0,colors[0][R] - table58H[d],255); - possible_colors[0][G] = CLAMP(0,colors[0][G] - table58H[d],255); - possible_colors[1][R] = CLAMP(0,colors[0][R] + table58H[d],255); - possible_colors[1][G] = CLAMP(0,colors[0][G] + table58H[d],255); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXERR1000; - - // Loop possible block colors - for (uint8 c = 0; c < 2; ++c) - { - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + - PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - } - } - precalc_errRG[((colorRGB444_packed>>4)*8 + d)*16 + (y*4)+x] = (unsigned int) best_pixel_error; - } - } - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates tables used in the exhaustive compression of the H-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcErrorRG_58H(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_errRG) -{ - double block_error = 0, - best_block_error = MAXIMUM_ERROR, - pixel_error, - best_pixel_error; - int diff[3]; - unsigned int pixel_colors; - uint8 possible_colors[2][3]; - uint8 colors[2][3]; - - decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); - - // Test all distances - for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) - { - possible_colors[0][R] = CLAMP(0,colors[0][R] - table58H[d],255); - possible_colors[0][G] = CLAMP(0,colors[0][G] - table58H[d],255); - possible_colors[1][R] = CLAMP(0,colors[0][R] + table58H[d],255); - possible_colors[1][G] = CLAMP(0,colors[0][G] + table58H[d],255); - - block_error = 0; - pixel_colors = 0; - - // Loop block - for (size_t y = 0; y < BLOCKHEIGHT; ++y) - { - for (size_t x = 0; x < BLOCKWIDTH; ++x) - { - best_pixel_error = MAXIMUM_ERROR; - - // Loop possible block colors - for (uint8 c = 0; c < 2; ++c) - { - diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); - diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); - - pixel_error = weight[R]*SQUARE(diff[R]) + - weight[G]*SQUARE(diff[G]); - - // Choose best error - if (pixel_error < best_pixel_error) - { - best_pixel_error = pixel_error; - } - } - precalc_errRG[((colorRGB444_packed>>4)*8 + d)*16 + (y*4)+x] = (unsigned int) best_pixel_error; - } - } - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in the exhaustive compression of the H-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError58Hperceptual1000(uint8* block, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_err) -{ - unsigned int pixel_error, - best_pixel_error; - int possible_colors[2][3]; - uint8 colors[2][3]; - unsigned int *precalc_err_tab; - int red_original; - int green_original; - int blue_original; - -#define PRECALC_ONE_58H_PERCEP(qvalue)\ - red_original = block[qvalue*4];\ - green_original = block[qvalue*4+1];\ - blue_original = block[qvalue*4+2];\ - /* unroll loop for (color = 0; color< 2; color++) */\ - best_pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[(possible_colors[0][R] - red_original)] \ - + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[(possible_colors[0][G] - green_original)]\ - + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[(possible_colors[0][B] - blue_original)];\ - pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[(possible_colors[1][R] - red_original)]\ - + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[(possible_colors[1][G] - green_original)]\ - + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[(possible_colors[1][B] - blue_original)];\ - if (pixel_error < best_pixel_error)\ - best_pixel_error = pixel_error;\ - /* end unroll loop */\ - precalc_err_tab[qvalue] = best_pixel_error;\ - -#define PRECALC_ONE_TABLE_58H_PERCEP(dvalue)\ - precalc_err_tab = &precalc_err[((colorRGB444_packed*8)+dvalue)*16];\ - possible_colors[0][R] = CLAMP_LEFT_ZERO(colors[0][R] - table58H[dvalue])+255;\ - possible_colors[0][G] = CLAMP_LEFT_ZERO(colors[0][G] - table58H[dvalue])+255;\ - possible_colors[0][B] = CLAMP_LEFT_ZERO(colors[0][B] - table58H[dvalue])+255;\ - possible_colors[1][R] = CLAMP_RIGHT_255(colors[0][R] + table58H[dvalue])+255;\ - possible_colors[1][G] = CLAMP_RIGHT_255(colors[0][G] + table58H[dvalue])+255;\ - possible_colors[1][B] = CLAMP_RIGHT_255(colors[0][B] + table58H[dvalue])+255;\ - /* unrolled loop for(q = 0; q<16; q++)*/\ - PRECALC_ONE_58H_PERCEP(0)\ - PRECALC_ONE_58H_PERCEP(1)\ - PRECALC_ONE_58H_PERCEP(2)\ - PRECALC_ONE_58H_PERCEP(3)\ - PRECALC_ONE_58H_PERCEP(4)\ - PRECALC_ONE_58H_PERCEP(5)\ - PRECALC_ONE_58H_PERCEP(6)\ - PRECALC_ONE_58H_PERCEP(7)\ - PRECALC_ONE_58H_PERCEP(8)\ - PRECALC_ONE_58H_PERCEP(9)\ - PRECALC_ONE_58H_PERCEP(10)\ - PRECALC_ONE_58H_PERCEP(11)\ - PRECALC_ONE_58H_PERCEP(12)\ - PRECALC_ONE_58H_PERCEP(13)\ - PRECALC_ONE_58H_PERCEP(14)\ - PRECALC_ONE_58H_PERCEP(15)\ - /* end unroll loop */\ - - colors[0][R] = (colorsRGB444[0][R] << 4) | colorsRGB444[0][R]; - colors[0][G] = (colorsRGB444[0][G] << 4) | colorsRGB444[0][G]; - colors[0][B] = (colorsRGB444[0][B] << 4) | colorsRGB444[0][B]; - - // Test all distances - /* unroll loop for (uint8 d = 0; d < 8; ++d) */ - - PRECALC_ONE_TABLE_58H_PERCEP(0) - PRECALC_ONE_TABLE_58H_PERCEP(1) - PRECALC_ONE_TABLE_58H_PERCEP(2) - PRECALC_ONE_TABLE_58H_PERCEP(3) - PRECALC_ONE_TABLE_58H_PERCEP(4) - PRECALC_ONE_TABLE_58H_PERCEP(5) - PRECALC_ONE_TABLE_58H_PERCEP(6) - PRECALC_ONE_TABLE_58H_PERCEP(7) - - /* end unroll loop */ -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Precalculates a table used in the exhaustive compression of the H-mode. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void precalcError58H(uint8* block, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_err) -{ - unsigned int pixel_error, - best_pixel_error; - int possible_colors[2][3]; - uint8 colors[2][3]; - unsigned int *precalc_err_tab; - int red_original; - int green_original; - int blue_original; - -#define PRECALC_ONE_58H(qvalue)\ - red_original = block[qvalue*4];\ - green_original = block[qvalue*4+1];\ - blue_original = block[qvalue*4+2];\ - /* unroll loop for (color = 0; color< 2; color++) */\ - best_pixel_error = square_table[(possible_colors[0][R] - red_original)] + square_table[(possible_colors[0][G] - green_original)] + square_table[(possible_colors[0][B] - blue_original)];\ - pixel_error = square_table[(possible_colors[1][R] - red_original)] + square_table[(possible_colors[1][G] - green_original)] + square_table[(possible_colors[1][B] - blue_original)];\ - if (pixel_error < best_pixel_error)\ - best_pixel_error = pixel_error;\ - /* end unroll loop */\ - precalc_err_tab[qvalue] = best_pixel_error;\ - -#define PRECALC_ONE_TABLE_58H(dvalue)\ - precalc_err_tab = &precalc_err[((colorRGB444_packed*8)+dvalue)*16];\ - possible_colors[0][R] = CLAMP_LEFT_ZERO(colors[0][R] - table58H[dvalue])+255;\ - possible_colors[0][G] = CLAMP_LEFT_ZERO(colors[0][G] - table58H[dvalue])+255;\ - possible_colors[0][B] = CLAMP_LEFT_ZERO(colors[0][B] - table58H[dvalue])+255;\ - possible_colors[1][R] = CLAMP_RIGHT_255(colors[0][R] + table58H[dvalue])+255;\ - possible_colors[1][G] = CLAMP_RIGHT_255(colors[0][G] + table58H[dvalue])+255;\ - possible_colors[1][B] = CLAMP_RIGHT_255(colors[0][B] + table58H[dvalue])+255;\ - /* unrolled loop for(q = 0; q<16; q++)*/\ - PRECALC_ONE_58H(0)\ - PRECALC_ONE_58H(1)\ - PRECALC_ONE_58H(2)\ - PRECALC_ONE_58H(3)\ - PRECALC_ONE_58H(4)\ - PRECALC_ONE_58H(5)\ - PRECALC_ONE_58H(6)\ - PRECALC_ONE_58H(7)\ - PRECALC_ONE_58H(8)\ - PRECALC_ONE_58H(9)\ - PRECALC_ONE_58H(10)\ - PRECALC_ONE_58H(11)\ - PRECALC_ONE_58H(12)\ - PRECALC_ONE_58H(13)\ - PRECALC_ONE_58H(14)\ - PRECALC_ONE_58H(15)\ - /* end unroll loop */\ - - colors[0][R] = (colorsRGB444[0][R] << 4) | colorsRGB444[0][R]; - colors[0][G] = (colorsRGB444[0][G] << 4) | colorsRGB444[0][G]; - colors[0][B] = (colorsRGB444[0][B] << 4) | colorsRGB444[0][B]; - - // Test all distances - /* unroll loop for (uint8 d = 0; d < 8; ++d) */ - - PRECALC_ONE_TABLE_58H(0) - PRECALC_ONE_TABLE_58H(1) - PRECALC_ONE_TABLE_58H(2) - PRECALC_ONE_TABLE_58H(3) - PRECALC_ONE_TABLE_58H(4) - PRECALC_ONE_TABLE_58H(5) - PRECALC_ONE_TABLE_58H(6) - PRECALC_ONE_TABLE_58H(7) - - /* end unroll loop */ -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimum error for the H-mode when doing exhaustive compression. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateErrorFromPrecalcR58Hperceptual1000(int *colorsRGB444_packed, unsigned int *precalc_errR, unsigned int best_err_so_far) -{ - unsigned int block_error = 0; - unsigned int best_block_error = MAXERR1000; - unsigned int *precalc_col1, *precalc_col2; - unsigned int *precalc_col1tab, *precalc_col2tab; - - precalc_col1 = &precalc_errR[(colorsRGB444_packed[0]>>8)*8*16]; - precalc_col2 = &precalc_errR[(colorsRGB444_packed[1]>>8)*8*16]; - -#define CHOICE_R58H_PERCEP(value)\ - if(precalc_col1tab[value] < precalc_col2tab[value])\ - block_error += precalc_col1tab[value];\ - else\ - block_error += precalc_col2tab[value];\ - - // Test all distances - for (uint8 d = 0; d < 8; ++d) - { - block_error = 0; - precalc_col1tab = &precalc_col1[d*16]; - precalc_col2tab = &precalc_col2[d*16]; - // Loop block - - /* unroll loop for(q = 0; q<16 && block_error < best_err_so_far; q++) */ - CHOICE_R58H_PERCEP(0) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(1) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(2) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(3) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(4) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(5) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(6) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(7) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(8) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(9) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(10) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(11) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(12) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(13) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(14) - if( block_error < best_err_so_far ) - { - CHOICE_R58H_PERCEP(15) - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - /* end unroll loop */ - - if (block_error < best_block_error) - best_block_error = block_error; - } - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimum error for the H-mode when doing exhaustive compression. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateErrorFromPrecalcR58H(int *colorsRGB444_packed, unsigned int *precalc_errR, unsigned int best_err_so_far) -{ - unsigned int block_error = 0; - unsigned int best_block_error = MAXIMUM_ERROR; - unsigned int *precalc_col1, *precalc_col2; - unsigned int *precalc_col1tab, *precalc_col2tab; - - precalc_col1 = &precalc_errR[(colorsRGB444_packed[0]>>8)*8*16]; - precalc_col2 = &precalc_errR[(colorsRGB444_packed[1]>>8)*8*16]; - -#define CHOICE_R58H(value)\ - if(precalc_col1tab[value] < precalc_col2tab[value])\ - block_error += precalc_col1tab[value];\ - else\ - block_error += precalc_col2tab[value];\ - - // Test all distances - for (uint8 d = 0; d < 8; ++d) - { - block_error = 0; - precalc_col1tab = &precalc_col1[d*16]; - precalc_col2tab = &precalc_col2[d*16]; - // Loop block - - /* unroll loop for(q = 0; q<16 && block_error < best_err_so_far; q++) */ - CHOICE_R58H(0) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(1) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(2) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(3) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(4) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(5) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(6) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(7) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(8) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(9) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(10) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(11) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(12) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(13) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(14) - if( block_error < best_err_so_far ) - { - CHOICE_R58H(15) - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - /* end unroll loop */ - - if (block_error < best_block_error) - best_block_error = block_error; - - } - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimum error for the H-mode when doing exhaustive compression. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateErrorFromPrecalcRG58Hperceptual1000(int *colorsRGB444_packed, unsigned int *precalc_errRG, unsigned int best_err_so_far) -{ - unsigned int block_error = 0; - unsigned int best_block_error = MAXIMUM_ERROR; - unsigned int *precalc_col1, *precalc_col2; - unsigned int *precalc_col1tab, *precalc_col2tab; - - precalc_col1 = &precalc_errRG[(colorsRGB444_packed[0]>>4)*8*16]; - precalc_col2 = &precalc_errRG[(colorsRGB444_packed[1]>>4)*8*16]; - -#define CHOICE_RG58H_PERCEP(value)\ - if(precalc_col1tab[value] < precalc_col2tab[value])\ - block_error += precalc_col1tab[value];\ - else\ - block_error += precalc_col2tab[value];\ - - // Test all distances - for (uint8 d = 0; d < 8; ++d) - { - block_error = 0; - precalc_col1tab = &precalc_col1[d*16]; - precalc_col2tab = &precalc_col2[d*16]; - // Loop block - - /* unroll loop for(q = 0; q<16 && block_error < best_err_so_far; q++) */ - CHOICE_RG58H_PERCEP(0) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(1) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(2) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(3) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(4) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(5) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(6) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(7) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(8) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(9) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(10) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(11) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(12) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(13) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(14) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H_PERCEP(15) - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - /* end unroll loop */ - - if (block_error < best_block_error) - best_block_error = block_error; - } - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimum error for the H-mode when doing exhaustive compression. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateErrorFromPrecalcRG58H(int *colorsRGB444_packed, unsigned int *precalc_errRG, unsigned int best_err_so_far) -{ - unsigned int block_error = 0; - unsigned int best_block_error = MAXIMUM_ERROR; - unsigned int *precalc_col1, *precalc_col2; - unsigned int *precalc_col1tab, *precalc_col2tab; - - precalc_col1 = &precalc_errRG[(colorsRGB444_packed[0]>>4)*8*16]; - precalc_col2 = &precalc_errRG[(colorsRGB444_packed[1]>>4)*8*16]; - -#define CHOICE_RG58H(value)\ - if(precalc_col1tab[value] < precalc_col2tab[value])\ - block_error += precalc_col1tab[value];\ - else\ - block_error += precalc_col2tab[value];\ - - // Test all distances - for (uint8 d = 0; d < 8; ++d) - { - block_error = 0; - precalc_col1tab = &precalc_col1[d*16]; - precalc_col2tab = &precalc_col2[d*16]; - // Loop block - - /* unroll loop for(q = 0; q<16 && block_error < best_err_so_far; q++) */ - CHOICE_RG58H(0) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(1) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(2) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(3) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(4) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(5) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(6) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(7) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(8) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(9) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(10) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(11) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(12) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(13) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(14) - if( block_error < best_err_so_far ) - { - CHOICE_RG58H(15) - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - /* end unroll loop */ - - if (block_error < best_block_error) - best_block_error = block_error; - } - return best_block_error; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimum error for the H-mode when doing exhaustive compression. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateErrorFromPrecalc58Hperceptual1000(int *colorsRGB444_packed, unsigned int *precalc_err, unsigned int total_best_err) -{ - unsigned int block_error;\ - unsigned int *precalc_col1, *precalc_col2;\ - unsigned int *precalc_col1tab, *precalc_col2tab;\ - - unsigned int error; - -#define FIRSTCHOICE_RGB58H_PERCEP(value)\ - if(precalc_col1tab[value] < precalc_col2tab[value])\ - block_error = precalc_col1tab[value];\ - else\ - block_error = precalc_col2tab[value];\ - -#define CHOICE_RGB58H_PERCEP(value)\ - if(precalc_col1tab[value] < precalc_col2tab[value])\ - block_error += precalc_col1tab[value];\ - else\ - block_error += precalc_col2tab[value];\ - -#define ONETABLE_RGB58H_PERCEP(distance)\ - precalc_col1tab = &precalc_col1[distance*16];\ - precalc_col2tab = &precalc_col2[distance*16];\ - /* unroll loop for(q = 0; q<16 && block_error < total_best_err; q++) */\ - FIRSTCHOICE_RGB58H_PERCEP(0)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H_PERCEP(1)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H_PERCEP(2)\ - CHOICE_RGB58H_PERCEP(3)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H_PERCEP(4)\ - CHOICE_RGB58H_PERCEP(5)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H_PERCEP(6)\ - CHOICE_RGB58H_PERCEP(7)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H_PERCEP(8)\ - CHOICE_RGB58H_PERCEP(9)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H_PERCEP(10)\ - CHOICE_RGB58H_PERCEP(11)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H_PERCEP(12)\ - CHOICE_RGB58H_PERCEP(13)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H_PERCEP(14)\ - CHOICE_RGB58H_PERCEP(15)\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - /* end unroll loop */\ - if (block_error < error)\ - error = block_error;\ - -#define CALCULATE_ERROR_FROM_PRECALC_RGB58H_PERCEP\ - error = MAXERR1000;\ - precalc_col1 = &precalc_err[colorsRGB444_packed[0]*8*16];\ - precalc_col2 = &precalc_err[colorsRGB444_packed[1]*8*16];\ - /* Test all distances*/\ - /* unroll loop for (uint8 d = 0; d < 8; ++d) */\ - ONETABLE_RGB58H_PERCEP(0)\ - ONETABLE_RGB58H_PERCEP(1)\ - ONETABLE_RGB58H_PERCEP(2)\ - ONETABLE_RGB58H_PERCEP(3)\ - ONETABLE_RGB58H_PERCEP(4)\ - ONETABLE_RGB58H_PERCEP(5)\ - ONETABLE_RGB58H_PERCEP(6)\ - ONETABLE_RGB58H_PERCEP(7)\ - /* end unroll loop */\ - - CALCULATE_ERROR_FROM_PRECALC_RGB58H_PERCEP - return error;\ -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Calculate a minimum error for the H-mode when doing exhaustive compression. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int calculateErrorFromPrecalc58H(int *colorsRGB444_packed, unsigned int *precalc_err, unsigned int total_best_err) -{ - unsigned int block_error;\ - unsigned int *precalc_col1, *precalc_col2;\ - unsigned int *precalc_col1tab, *precalc_col2tab;\ - - unsigned int error; - -#define FIRSTCHOICE_RGB58H(value)\ - if(precalc_col1tab[value] < precalc_col2tab[value])\ - block_error = precalc_col1tab[value];\ - else\ - block_error = precalc_col2tab[value];\ - -#define CHOICE_RGB58H(value)\ - if(precalc_col1tab[value] < precalc_col2tab[value])\ - block_error += precalc_col1tab[value];\ - else\ - block_error += precalc_col2tab[value];\ - -#define ONETABLE_RGB58H(distance)\ - precalc_col1tab = &precalc_col1[distance*16];\ - precalc_col2tab = &precalc_col2[distance*16];\ - /* unroll loop for(q = 0; q<16 && block_error < total_best_err; q++) */\ - FIRSTCHOICE_RGB58H(0)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H(1)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H(2)\ - CHOICE_RGB58H(3)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H(4)\ - CHOICE_RGB58H(5)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H(6)\ - CHOICE_RGB58H(7)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H(8)\ - CHOICE_RGB58H(9)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H(10)\ - CHOICE_RGB58H(11)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H(12)\ - CHOICE_RGB58H(13)\ - if( block_error < total_best_err)\ - {\ - CHOICE_RGB58H(14)\ - CHOICE_RGB58H(15)\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - }\ - /* end unroll loop */\ - if (block_error < error)\ - error = block_error;\ - -#define CALCULATE_ERROR_FROM_PRECALC_RGB58H\ - error = MAXIMUM_ERROR;\ - precalc_col1 = &precalc_err[colorsRGB444_packed[0]*8*16];\ - precalc_col2 = &precalc_err[colorsRGB444_packed[1]*8*16];\ - /* Test all distances*/\ - /* unroll loop for (uint8 d = 0; d < 8; ++d) */\ - ONETABLE_RGB58H(0)\ - ONETABLE_RGB58H(1)\ - ONETABLE_RGB58H(2)\ - ONETABLE_RGB58H(3)\ - ONETABLE_RGB58H(4)\ - ONETABLE_RGB58H(5)\ - ONETABLE_RGB58H(6)\ - ONETABLE_RGB58H(7)\ - /* end unroll loop */\ - - CALCULATE_ERROR_FROM_PRECALC_RGB58H - return error;\ -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// The below code should compress the block to 58 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// The bit layout is thought to be: -// -//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| -//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. -// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. -// Else, it is assumed to be 1. - -// The below code should compress the block to 58 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// The bit layout is thought to be: -// -//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| -//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. -// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. -// Else, it is assumed to be 1. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockTHUMB58HExhaustivePerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int best_error_so_far) -{ - unsigned int best_error_using_Hmode; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - unsigned int error; - uint8 colorsRGB444[2][3]; - int colorsRGB444_packed[2]; - int best_colorsRGB444_packed[2]; - int colorRGB444_packed; - unsigned int pixel_indices; - uint8 distance; - unsigned int *precalc_err; // smallest error per color, table and pixel - unsigned int *precalc_err_RG; // smallest pixel error for an entire table - unsigned int *precalc_err_R; // smallest pixel error for an entire table - uint8 block[4*4*4]; - - best_error_using_Hmode = MAXERR1000; - - precalc_err = (unsigned int*) malloc(4096*8*16*sizeof(unsigned int)); - if(!precalc_err){printf("Out of memory allocating \n");exit(1);} - - precalc_err_RG = (unsigned int*) malloc(16*16*8*16*sizeof(unsigned int)); - if(!precalc_err_RG){printf("Out of memory allocating \n");exit(1);} - - precalc_err_R = (unsigned int*) malloc(16*8*16*sizeof(unsigned int)); - if(!precalc_err_R){printf("Out of memory allocating \n");exit(1);} - - unsigned int test1, test2; - best_error_using_Hmode = (unsigned int)compressBlockTHUMB58HFastestPerceptual1000(img,width, height, startx, starty, test1, test2); - best_colorsRGB444_packed[0] = 0; - best_colorsRGB444_packed[0] = GETBITSHIGH(test1, 12, 57); - best_colorsRGB444_packed[1] = 0; - best_colorsRGB444_packed[1] = GETBITSHIGH(test1, 12, 45); - - if(best_error_using_Hmode < best_error_so_far) - best_error_so_far = best_error_using_Hmode; - - int xx,yy,count = 0; - - // Use 4 bytes per pixel to make it 32-word aligned. - for(xx = 0; xx<4; xx++) - { - for(yy=0; yy<4; yy++) - { - block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block[(count)*4+3] = 0; - count++; - } - } - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) - { - colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; - colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; - colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; - - precalcError58Hperceptual1000(block, colorsRGB444, colorRGB444_packed, precalc_err); - } - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) - { - colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; - colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; - colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; - precalcErrorRG_58Hperceptual1000(img, width, startx, starty, colorsRGB444, colorRGB444_packed, precalc_err_RG); - } - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) - { - colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; - colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; - colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; - precalcErrorR_58Hperceptual1000(img, width, startx, starty, colorsRGB444, colorRGB444_packed, precalc_err_R); - } - - int trycols = 0; - int allcols = 0; - - for( colorsRGB444[0][0] = 0; colorsRGB444[0][0] <16; colorsRGB444[0][0]++) - { - colorsRGB444_packed[0] = colorsRGB444[0][0]*256; - for( colorsRGB444[1][0] = 0; colorsRGB444[1][0] <16; colorsRGB444[1][0]++) - { - colorsRGB444_packed[1] = colorsRGB444[1][0]*256; - if(colorsRGB444_packed[0] <= colorsRGB444_packed[1]) - { - error = calculateErrorFromPrecalcR58Hperceptual1000(colorsRGB444_packed, precalc_err_R, best_error_so_far); - if(error < best_error_so_far) - { - for( colorsRGB444[0][1] = 0; colorsRGB444[0][1] <16; colorsRGB444[0][1]++) - { - colorsRGB444_packed[0] = colorsRGB444[0][0]*256 + colorsRGB444[0][1]*16; - for( colorsRGB444[1][1] = 0; colorsRGB444[1][1] <16; colorsRGB444[1][1]++) - { - colorsRGB444_packed[1] = colorsRGB444[1][0]*256 + colorsRGB444[1][1]*16; - if(colorsRGB444_packed[0] <= colorsRGB444_packed[1]) - { - error = calculateErrorFromPrecalcRG58Hperceptual1000(colorsRGB444_packed, precalc_err_RG, best_error_so_far); - if(error < best_error_so_far) - { - for( colorsRGB444[0][2] = 0; colorsRGB444[0][2] <16; colorsRGB444[0][2]++) - { - colorsRGB444_packed[0] = colorsRGB444[0][0]*256 + colorsRGB444[0][1]*16 + colorsRGB444[0][2]; - for( colorsRGB444[1][2] = 0; colorsRGB444[1][2] <16; colorsRGB444[1][2]++) - { - colorsRGB444_packed[1] = colorsRGB444[1][0]*256 + colorsRGB444[1][1]*16 + colorsRGB444[1][2]; - if(colorsRGB444_packed[0] < colorsRGB444_packed[1]) - { - error = calculateErrorFromPrecalc58Hperceptual1000(colorsRGB444_packed, precalc_err, best_error_so_far); - if(error < best_error_so_far) - { - best_error_so_far = error; - best_error_using_Hmode = error; - best_colorsRGB444_packed[0] = colorsRGB444_packed[0]; - best_colorsRGB444_packed[1] = colorsRGB444_packed[1]; - } - } - } - } - } - } - } - } - } - } - } - } - best_colorsRGB444[0][0] = (best_colorsRGB444_packed[0] >> 8) & 0xf; - best_colorsRGB444[0][1] = (best_colorsRGB444_packed[0] >> 4) & 0xf; - best_colorsRGB444[0][2] = (best_colorsRGB444_packed[0]) & 0xf; - best_colorsRGB444[1][0] = (best_colorsRGB444_packed[1] >> 8) & 0xf; - best_colorsRGB444[1][1] = (best_colorsRGB444_packed[1] >> 4) & 0xf; - best_colorsRGB444[1][2] = (best_colorsRGB444_packed[1]) & 0xf; - - free(precalc_err); - free(precalc_err_RG); - free(precalc_err_R); - - error = (unsigned int) calculateErrorAndCompress58Hperceptual1000(img, width, startx, starty, best_colorsRGB444, distance, pixel_indices); - best_distance = distance; - best_pixel_indices = pixel_indices; - - // | col0 >= col1 col0 < col1 - //------------------------------------------------------ - // (dist & 1) = 1 | no need to swap | need to swap - // |-----------------+---------------- - // (dist & 1) = 0 | need to swap | no need to swap - // - // This can be done with an xor test. - - best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; - best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; - if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) - { - swapColors(best_colorsRGB444); - - // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 - best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); - } - - // Put the compress params into the compression block - compressed1 = 0; - - PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); - PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); - best_pixel_indices=indexConversion(best_pixel_indices); - compressed2 = 0; - PUTBITS( compressed2, best_pixel_indices, 32, 31); - - return best_error_using_Hmode; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// The below code should compress the block to 58 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// The bit layout is thought to be: -// -//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| -//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. -// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. -// Else, it is assumed to be 1. - -// The below code should compress the block to 58 bits. -// This is supposed to match the first of the three modes in TWOTIMER. -// The bit layout is thought to be: -// -//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| -//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| -// -//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| -//|----------------------------------------index bits---------------------------------------------| -// -// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. -// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. -// Else, it is assumed to be 1. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -unsigned int compressBlockTHUMB58HExhaustive(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int best_error_so_far) -{ - unsigned int best_error_using_Hmode; - uint8 best_colorsRGB444[2][3]; - unsigned int best_pixel_indices; - uint8 best_distance; - - unsigned int error; - uint8 colorsRGB444[2][3]; - int colorsRGB444_packed[2]; - int best_colorsRGB444_packed[2]; - int colorRGB444_packed; - unsigned int pixel_indices; - uint8 distance; - unsigned int *precalc_err; // smallest error per color, table and pixel - unsigned int *precalc_err_RG; // smallest pixel error for an entire table - unsigned int *precalc_err_R; // smallest pixel error for an entire table - uint8 block[4*4*4]; - - best_error_using_Hmode = MAXIMUM_ERROR; - - precalc_err = (unsigned int*) malloc(4096*8*16*sizeof(unsigned int)); - if(!precalc_err){printf("Out of memory allocating \n");exit(1);} - - precalc_err_RG = (unsigned int*) malloc(16*16*8*16*sizeof(unsigned int)); - if(!precalc_err_RG){printf("Out of memory allocating \n");exit(1);} - - precalc_err_R = (unsigned int*) malloc(16*8*16*sizeof(unsigned int)); - if(!precalc_err_R){printf("Out of memory allocating \n");exit(1);} - - unsigned int test1, test2; - best_error_using_Hmode = (unsigned int)compressBlockTHUMB58HFastest(img,width, height, startx, starty, test1, test2); - best_colorsRGB444_packed[0] = 0; - best_colorsRGB444_packed[0] = GETBITSHIGH(test1, 12, 57); - best_colorsRGB444_packed[1] = 0; - best_colorsRGB444_packed[1] = GETBITSHIGH(test1, 12, 45); - - if(best_error_using_Hmode < best_error_so_far) - best_error_so_far = best_error_using_Hmode; - - int xx,yy,count = 0; - - // Reshuffle pixels so that the top left 2x2 pixels arrive first, then the top right 2x2 pixels etc. Also put use 4 bytes per pixel to make it 32-word aligned. - for(xx = 0; xx<4; xx++) - { - for(yy=0; yy<4; yy++) - { - block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; - block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; - block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; - block[(count)*4+3] = 0; - count++; - } - } - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) - { - colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; - colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; - colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; - precalcError58H(block, colorsRGB444, colorRGB444_packed, precalc_err); - } - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) - { - colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; - colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; - colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; - precalcErrorRG_58H(img, width, startx, starty, colorsRGB444, colorRGB444_packed, precalc_err_RG); - } - - for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) - { - colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; - colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; - colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; - precalcErrorR_58H(img, width, startx, starty, colorsRGB444, colorRGB444_packed, precalc_err_R); - } - - int trycols = 0; - int allcols = 0; - - for( colorsRGB444[0][0] = 0; colorsRGB444[0][0] <16; colorsRGB444[0][0]++) - { - colorsRGB444_packed[0] = colorsRGB444[0][0]*256; - for( colorsRGB444[1][0] = 0; colorsRGB444[1][0] <16; colorsRGB444[1][0]++) - { - colorsRGB444_packed[1] = colorsRGB444[1][0]*256; - if(colorsRGB444_packed[0] <= colorsRGB444_packed[1]) - { - error = calculateErrorFromPrecalcR58H(colorsRGB444_packed, precalc_err_R, best_error_so_far); - if(error < best_error_so_far) - { - for( colorsRGB444[0][1] = 0; colorsRGB444[0][1] <16; colorsRGB444[0][1]++) - { - colorsRGB444_packed[0] = colorsRGB444[0][0]*256 + colorsRGB444[0][1]*16; - for( colorsRGB444[1][1] = 0; colorsRGB444[1][1] <16; colorsRGB444[1][1]++) - { - colorsRGB444_packed[1] = colorsRGB444[1][0]*256 + colorsRGB444[1][1]*16; - if(colorsRGB444_packed[0] <= colorsRGB444_packed[1]) - { - error = calculateErrorFromPrecalcRG58H(colorsRGB444_packed, precalc_err_RG, best_error_so_far); - if(error < best_error_so_far) - { - for( colorsRGB444[0][2] = 0; colorsRGB444[0][2] <16; colorsRGB444[0][2]++) - { - colorsRGB444_packed[0] = colorsRGB444[0][0]*256 + colorsRGB444[0][1]*16 + colorsRGB444[0][2]; - for( colorsRGB444[1][2] = 0; colorsRGB444[1][2] <16; colorsRGB444[1][2]++) - { - colorsRGB444_packed[1] = colorsRGB444[1][0]*256 + colorsRGB444[1][1]*16 + colorsRGB444[1][2]; - if(colorsRGB444_packed[0] < colorsRGB444_packed[1]) - { - error = calculateErrorFromPrecalc58H(colorsRGB444_packed, precalc_err, best_error_so_far); - if(error < best_error_so_far) - { - best_error_so_far = error; - best_error_using_Hmode = error; - best_colorsRGB444_packed[0] = colorsRGB444_packed[0]; - best_colorsRGB444_packed[1] = colorsRGB444_packed[1]; - } - } - } - } - } - } - } - } - } - } - } - } - best_colorsRGB444[0][0] = (best_colorsRGB444_packed[0] >> 8) & 0xf; - best_colorsRGB444[0][1] = (best_colorsRGB444_packed[0] >> 4) & 0xf; - best_colorsRGB444[0][2] = (best_colorsRGB444_packed[0]) & 0xf; - best_colorsRGB444[1][0] = (best_colorsRGB444_packed[1] >> 8) & 0xf; - best_colorsRGB444[1][1] = (best_colorsRGB444_packed[1] >> 4) & 0xf; - best_colorsRGB444[1][2] = (best_colorsRGB444_packed[1]) & 0xf; - - free(precalc_err); - free(precalc_err_RG); - free(precalc_err_R); - - error = (unsigned int) calculateErrorAndCompress58H(img, width, startx, starty, best_colorsRGB444, distance, pixel_indices); - best_distance = distance; - best_pixel_indices = pixel_indices; - - // | col0 >= col1 col0 < col1 - //------------------------------------------------------ - // (dist & 1) = 1 | no need to swap | need to swap - // |-----------------+---------------- - // (dist & 1) = 0 | need to swap | no need to swap - // - // This can be done with an xor test. - - best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; - best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; - if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) - { - swapColors(best_colorsRGB444); - - // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 - best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); - } - - // Put the compress params into the compression block - compressed1 = 0; - - PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); - PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); - PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); - PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); - best_pixel_indices=indexConversion(best_pixel_indices); - compressed2 = 0; - PUTBITS( compressed2, best_pixel_indices, 32, 31); - - return best_error_using_Hmode; -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Compress a block exhaustively for the ETC1 codec. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockETC1Exhaustive(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - unsigned int error_currently_best; - - unsigned int etc1_differential_word1; - unsigned int etc1_differential_word2; - unsigned int error_etc1_differential; - - unsigned int etc1_individual_word1; - unsigned int etc1_individual_word2; - unsigned int error_etc1_individual; - - unsigned int error_best; - signed char best_char; - int best_mode; - - error_currently_best = 255*255*16*3; - - // First pass -- quickly find a low error so that we can later cull away a lot of - // calculations later that are guaranteed to be higher than that error. - unsigned int error_etc1; - unsigned int etc1_word1; - unsigned int etc1_word2; - - error_etc1 = (unsigned int) compressBlockDiffFlipFast(img, imgdec, width, height, startx, starty, etc1_word1, etc1_word2); - if(error_etc1 < error_currently_best) - error_currently_best = error_etc1; - - error_etc1_individual = compressBlockIndividualExhaustive(img, width, height, startx, starty, etc1_individual_word1, etc1_individual_word2, error_currently_best); - if(error_etc1_individual < error_currently_best) - error_currently_best = error_etc1_individual; - - error_etc1_differential = compressBlockDifferentialExhaustive(img, width, height, startx, starty, etc1_differential_word1, etc1_differential_word2, error_currently_best); - if(error_etc1_differential < error_currently_best) - error_currently_best = error_etc1_differential; - - error_best = error_etc1_differential; - compressed1 = etc1_differential_word1; - compressed2 = etc1_differential_word2; - best_char = '.'; - best_mode = MODE_ETC1; - - if(error_etc1_individual < error_best) - { - compressed1 = etc1_individual_word1; - compressed2 = etc1_individual_word2; - best_char = ','; - error_best = error_etc1_individual; - best_mode = MODE_ETC1; - } - - if(error_etc1 < error_best) - { - compressed1 = etc1_word1; - compressed2 = etc1_word2; - best_char = '.'; - error_best = error_etc1; - best_mode = MODE_ETC1; - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Compress a block exhaustively for the ETC1 codec using perceptual error measure. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockETC1ExhaustivePerceptual(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - unsigned int error_currently_best; - - unsigned int etc1_differential_word1; - unsigned int etc1_differential_word2; - unsigned int error_etc1_differential; - - unsigned int etc1_individual_word1; - unsigned int etc1_individual_word2; - unsigned int error_etc1_individual; - - unsigned int error_best; - signed char best_char; - int best_mode; - - - error_currently_best = 255*255*16*1000; - - // First pass -- quickly find a low error so that we can later cull away a lot of - // calculations later that are guaranteed to be higher than that error. - unsigned int error_etc1; - unsigned int etc1_word1; - unsigned int etc1_word2; - - compressBlockDiffFlipFastPerceptual(img, imgdec, width, height, startx, starty, etc1_word1, etc1_word2); - decompressBlockDiffFlip(etc1_word1, etc1_word2, imgdec, width, height, startx, starty); - error_etc1 = 1000*calcBlockPerceptualErrorRGB(img, imgdec, width, height, startx, starty); - if(error_etc1 < error_currently_best) - error_currently_best = error_etc1; - - // Second pass --- now find the lowest error, but only if it is lower than error_currently_best - - error_etc1_differential = compressBlockDifferentialExhaustivePerceptual(img, width, height, startx, starty, etc1_differential_word1, etc1_differential_word2, error_currently_best); - if(error_etc1_differential < error_currently_best) - error_currently_best = error_etc1_differential; - - error_etc1_individual = compressBlockIndividualExhaustivePerceptual(img, width, height, startx, starty, etc1_individual_word1, etc1_individual_word2, error_currently_best); - if(error_etc1_individual < error_currently_best) - error_currently_best = error_etc1_individual; - - // Now find the best error. - error_best = error_etc1; - compressed1 = etc1_word1; - compressed2 = etc1_word2; - best_char = '.'; - best_mode = MODE_ETC1; - - if(error_etc1_differential < error_best) - { - error_best = error_etc1_differential; - compressed1 = etc1_differential_word1; - compressed2 = etc1_differential_word2; - best_char = '.'; - best_mode = MODE_ETC1; - } - - if(error_etc1_individual < error_best) - { - compressed1 = etc1_individual_word1; - compressed2 = etc1_individual_word2; - best_char = ','; - error_best = error_etc1_individual; - best_mode = MODE_ETC1; - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Compress a block exhaustively for the ETC2 RGB codec using perceptual error measure. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockETC2ExhaustivePerceptual(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - unsigned int error_currently_best; - - unsigned int etc1_differential_word1; - unsigned int etc1_differential_word2; - unsigned int error_etc1_differential; - - unsigned int etc1_individual_word1; - unsigned int etc1_individual_word2; - unsigned int error_etc1_individual; - - unsigned int planar57_word1; - unsigned int planar57_word2; - unsigned int planar_word1; - unsigned int planar_word2; - double error_planar; - unsigned int error_planar_red, error_planar_green, error_planar_blue; - - unsigned int thumbH58_word1; - unsigned int thumbH58_word2; - unsigned int thumbH_word1; - unsigned int thumbH_word2; - unsigned int error_thumbH; - - unsigned int thumbT59_word1; - unsigned int thumbT59_word2; - unsigned int thumbT_word1; - unsigned int thumbT_word2; - unsigned int error_thumbT; - - unsigned int error_best; - signed char best_char; - int best_mode; - - error_currently_best = 255*255*16*1000; - - // First pass -- quickly find a low error so that we can later cull away a lot of - // calculations later that are guaranteed to be higher than that error. - unsigned int error_etc1; - unsigned int etc1_word1; - unsigned int etc1_word2; - - compressBlockDiffFlipFastPerceptual(img, imgdec, width, height, startx, starty, etc1_word1, etc1_word2); - decompressBlockDiffFlip(etc1_word1, etc1_word2, imgdec, width, height, startx, starty); - error_etc1 = 1000*calcBlockPerceptualErrorRGB(img, imgdec, width, height, startx, starty); - if(error_etc1 < error_currently_best) - error_currently_best = error_etc1; - - // The planar mode treats every channel independently and should not be affected by the weights in the error measure. - // We can hence use the nonperceptual version of the encoder also to find the best perceptual description of the block. - compressBlockPlanar57(img, width, height, startx, starty, planar57_word1, planar57_word2); - decompressBlockPlanar57errorPerComponent(planar57_word1, planar57_word2, imgdec, width, height, startx, starty, img, error_planar_red, error_planar_green, error_planar_blue); - error_planar = 1000*calcBlockPerceptualErrorRGB(img, imgdec, width, height, startx, starty); - stuff57bits(planar57_word1, planar57_word2, planar_word1, planar_word2); - if(error_planar < error_currently_best) - error_currently_best = (unsigned int) error_planar; - - error_thumbT = (unsigned int) compressBlockTHUMB59TFastestPerceptual1000(img,width, height, startx, starty, thumbT59_word1, thumbT59_word2); - stuff59bits(thumbT59_word1, thumbT59_word2, thumbT_word1, thumbT_word2); - if(error_thumbT < error_currently_best) - error_currently_best = error_thumbT; - - error_thumbH = (unsigned int) compressBlockTHUMB58HFastestPerceptual1000(img,width,height,startx, starty, thumbH58_word1, thumbH58_word2); - stuff58bits(thumbH58_word1, thumbH58_word2, thumbH_word1, thumbH_word2); - if(error_thumbH < error_currently_best) - error_currently_best = error_thumbH; - - // Second pass --- now find the lowest error, but only if it is lower than error_currently_best - - // Correct the individual errors for the different planes so that they sum to 1000 instead of 1. - error_planar_red *=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000; - error_planar_green *=PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000; - error_planar_blue *=PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000; - compressBlockPlanar57ExhaustivePerceptual(img, width, height, startx, starty, planar57_word1, planar57_word2, error_currently_best, error_planar_red, error_planar_green, error_planar_blue); - decompressBlockPlanar57(planar57_word1, planar57_word2, imgdec, width, height, startx, starty); - error_planar = 1000*calcBlockPerceptualErrorRGB(img, imgdec, width, height, startx, starty); - stuff57bits(planar57_word1, planar57_word2, planar_word1, planar_word2); - if(error_planar < error_currently_best) - error_currently_best = (unsigned int) error_planar; - - error_etc1_differential = compressBlockDifferentialExhaustivePerceptual(img, width, height, startx, starty, etc1_differential_word1, etc1_differential_word2, error_currently_best); - if(error_etc1_differential < error_currently_best) - error_currently_best = error_etc1_differential; - - error_etc1_individual = compressBlockIndividualExhaustivePerceptual(img, width, height, startx, starty, etc1_individual_word1, etc1_individual_word2, error_currently_best); - if(error_etc1_individual < error_currently_best) - error_currently_best = error_etc1_individual; - - error_thumbH = compressBlockTHUMB58HExhaustivePerceptual(img,width,height,startx, starty, thumbH58_word1, thumbH58_word2, error_currently_best); - stuff58bits(thumbH58_word1, thumbH58_word2, thumbH_word1, thumbH_word2); - if( error_thumbH < error_currently_best) - error_currently_best = error_thumbH; - - error_thumbT = compressBlockTHUMB59TExhaustivePerceptual(img,width, height, startx, starty, thumbT59_word1, thumbT59_word2, error_currently_best); - stuff59bits(thumbT59_word1, thumbT59_word2, thumbT_word1, thumbT_word2); - if(error_thumbT < error_currently_best) - error_currently_best = error_thumbT; - - // Now find the best error. - error_best = error_etc1; - compressed1 = etc1_word1; - compressed2 = etc1_word2; - best_char = '.'; - best_mode = MODE_ETC1; - - if(error_etc1_differential < error_best) - { - error_best = error_etc1_differential; - compressed1 = etc1_differential_word1; - compressed2 = etc1_differential_word2; - best_char = '.'; - best_mode = MODE_ETC1; - } - - if(error_etc1_individual < error_best) - { - compressed1 = etc1_individual_word1; - compressed2 = etc1_individual_word2; - best_char = ','; - error_best = error_etc1_individual; - best_mode = MODE_ETC1; - } - if(error_planar < error_best) - { - compressed1 = planar_word1; - compressed2 = planar_word2; - best_char = 'p'; - error_best = (unsigned int) error_planar; - best_mode = MODE_PLANAR; - } - if(error_thumbH < error_best) - { - compressed1 = thumbH_word1; - compressed2 = thumbH_word2; - best_char = 'H'; - error_best = error_thumbH; - best_mode = MODE_THUMB_H; - } - if(error_thumbT < error_best) - { - compressed1 = thumbT_word1; - compressed2 = thumbT_word2; - best_char = 'T'; - error_best = error_thumbT; - best_mode = MODE_THUMB_T; - } -} -#endif - -#if EXHAUSTIVE_CODE_ACTIVE -// Compress a block exhaustively for the ETC2 RGB codec. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressBlockETC2Exhaustive(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) -{ - unsigned int error_currently_best; - - unsigned int etc1_differential_word1; - unsigned int etc1_differential_word2; - unsigned int error_etc1_differential; - - unsigned int etc1_individual_word1; - unsigned int etc1_individual_word2; - unsigned int error_etc1_individual; - - unsigned int planar57_word1; - unsigned int planar57_word2; - unsigned int planar_word1; - unsigned int planar_word2; - double error_planar; - unsigned int error_planar_red; - unsigned int error_planar_green; - unsigned int error_planar_blue; - - unsigned int thumbH58_word1; - unsigned int thumbH58_word2; - unsigned int thumbH_word1; - unsigned int thumbH_word2; - unsigned int error_thumbH; - - unsigned int thumbT59_word1; - unsigned int thumbT59_word2; - unsigned int thumbT_word1; - unsigned int thumbT_word2; - unsigned int error_thumbT; - - unsigned int error_best; - signed char best_char; - int best_mode; - - error_currently_best = 255*255*16*3; - - // First pass -- quickly find a low error so that we can later cull away a lot of - // calculations later that are guaranteed to be higher than that error. - unsigned int error_etc1; - unsigned int etc1_word1; - unsigned int etc1_word2; - - error_etc1 = (unsigned int) compressBlockDiffFlipFast(img, imgdec, width, height, startx, starty, etc1_word1, etc1_word2); - if(error_etc1 < error_currently_best) - error_currently_best = error_etc1; - - compressBlockPlanar57(img, width, height, startx, starty, planar57_word1, planar57_word2); - decompressBlockPlanar57errorPerComponent(planar57_word1, planar57_word2, imgdec, width, height, startx, starty, img, error_planar_red, error_planar_green, error_planar_blue); - error_planar = calcBlockErrorRGB(img, imgdec, width, height, startx, starty); - stuff57bits(planar57_word1, planar57_word2, planar_word1, planar_word2); - if(error_planar < error_currently_best) - error_currently_best = (unsigned int) error_planar; - - error_thumbT = (unsigned int) compressBlockTHUMB59TFastest(img,width, height, startx, starty, thumbT59_word1, thumbT59_word2); - stuff59bits(thumbT59_word1, thumbT59_word2, thumbT_word1, thumbT_word2); - if(error_thumbT < error_currently_best) - error_currently_best = error_thumbT; - - error_thumbH = (unsigned int) compressBlockTHUMB58HFastest(img,width,height,startx, starty, thumbH58_word1, thumbH58_word2); - stuff58bits(thumbH58_word1, thumbH58_word2, thumbH_word1, thumbH_word2); - if(error_thumbH < error_currently_best) - error_currently_best = error_thumbH; - - // Second pass --- now find the lowest error, but only if it is lower than error_currently_best - error_etc1_differential = compressBlockDifferentialExhaustive(img, width, height, startx, starty, etc1_differential_word1, etc1_differential_word2, error_currently_best); - if(error_etc1_differential < error_currently_best) - error_currently_best = error_etc1_differential; - - compressBlockPlanar57Exhaustive(img, width, height, startx, starty, planar57_word1, planar57_word2, error_currently_best, error_planar_red, error_planar_green, error_planar_blue); - decompressBlockPlanar57(planar57_word1, planar57_word2, imgdec, width, height, startx, starty); - error_planar = calcBlockErrorRGB(img, imgdec, width, height, startx, starty); - stuff57bits(planar57_word1, planar57_word2, planar_word1, planar_word2); - if(error_planar < error_currently_best) - error_currently_best = (unsigned int) error_planar; - - error_etc1_individual = compressBlockIndividualExhaustive(img, width, height, startx, starty, etc1_individual_word1, etc1_individual_word2, error_currently_best); - if(error_etc1_individual < error_currently_best) - error_currently_best = error_etc1_individual; - - error_thumbH = compressBlockTHUMB58HExhaustive(img,width,height,startx, starty, thumbH58_word1, thumbH58_word2, error_currently_best); - if( error_thumbH < error_currently_best) - error_currently_best = error_thumbH; - stuff58bits(thumbH58_word1, thumbH58_word2, thumbH_word1, thumbH_word2); - - error_thumbT = compressBlockTHUMB59TExhaustive(img,width, height, startx, starty, thumbT59_word1, thumbT59_word2, error_currently_best); - if(error_thumbT < error_currently_best) - error_currently_best = error_thumbT; - stuff59bits(thumbT59_word1, thumbT59_word2, thumbT_word1, thumbT_word2); - - error_best = 255*255*3*16; - // Now find the best error. - error_best = error_etc1; - compressed1 = etc1_word1; - compressed2 = etc1_word2; - best_char = '.'; - best_mode = MODE_ETC1; - - if(error_etc1_differential < error_best) - { - error_best = error_etc1_differential; - compressed1 = etc1_differential_word1; - compressed2 = etc1_differential_word2; - best_char = '.'; - best_mode = MODE_ETC1; - } - if(error_etc1_individual < error_best) - { - compressed1 = etc1_individual_word1; - compressed2 = etc1_individual_word2; - best_char = ','; - error_best = error_etc1_individual; - best_mode = MODE_ETC1; - } - if(error_planar < error_best) - { - compressed1 = planar_word1; - compressed2 = planar_word2; - best_char = 'p'; - error_best = (unsigned int) error_planar; - best_mode = MODE_PLANAR; - } - if(error_thumbH < error_best) - { - compressed1 = thumbH_word1; - compressed2 = thumbH_word2; - best_char = 'H'; - error_best = error_thumbH; - best_mode = MODE_THUMB_H; - } - if(error_thumbT < error_best) - { - compressed1 = thumbT_word1; - compressed2 = thumbT_word2; - best_char = 'T'; - error_best = error_thumbT; - best_mode = MODE_THUMB_T; - } -} -#endif - -//// Exhaustive code ends here. - - -// Compress an image file. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressImageFile(uint8 *img, uint8 *alphaimg,int width,int height,char *dstfile, int expandedwidth, int expandedheight) -{ - FILE *f; - int x,y,w,h; - unsigned int block1, block2; - unsigned short wi, hi; - unsigned char magic[4]; - unsigned char version[2]; - unsigned short texture_type=format; - uint8 *imgdec; - uint8* alphaimg2; - imgdec = (unsigned char*) malloc(expandedwidth*expandedheight*3); - if(!imgdec) - { - printf("Could not allocate decompression buffer --- exiting\n"); - } - - magic[0] = 'P'; magic[1] = 'K'; magic[2] = 'M'; magic[3] = ' '; - - if(codec==CODEC_ETC2) - { - version[0] = '2'; version[1] = '0'; - } - else - { - version[0] = '1'; version[1] = '0'; - } - - if(f=fopen(dstfile,"wb")) - { - w=expandedwidth/4; w*=4; - h=expandedheight/4; h*=4; - wi = w; - hi = h; - if(ktxFile) - { - //.ktx file: KTX header followed by compressed binary data. - KTX_header header; - //identifier - for(int i=0; i<12; i++) - { - header.identifier[i]=ktx_identifier[i]; - } - //endianess int.. if this comes out reversed, all of the other ints will too. - header.endianness=KTX_ENDIAN_REF; - - //these values are always 0/1 for compressed textures. - header.glType=0; - header.glTypeSize=1; - header.glFormat=0; - - header.pixelWidth=width; - header.pixelHeight=height; - header.pixelDepth=0; - - //we only support single non-mipmapped non-cubemap textures.. - header.numberOfArrayElements=0; - header.numberOfFaces=1; - header.numberOfMipmapLevels=1; - - //and no metadata.. - header.bytesOfKeyValueData=0; - - int halfbytes=1; - //header.glInternalFormat=? - //header.glBaseInternalFormat=? - if(format==ETC2PACKAGE_R_NO_MIPMAPS) - { - header.glBaseInternalFormat=GL_R; - if(formatSigned) - header.glInternalFormat=GL_COMPRESSED_SIGNED_R11_EAC; - else - header.glInternalFormat=GL_COMPRESSED_R11_EAC; - } - else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) - { - halfbytes=2; - header.glBaseInternalFormat=GL_RG; - if(formatSigned) - header.glInternalFormat=GL_COMPRESSED_SIGNED_RG11_EAC; - else - header.glInternalFormat=GL_COMPRESSED_RG11_EAC; - } - else if(format==ETC2PACKAGE_RGB_NO_MIPMAPS) - { - header.glBaseInternalFormat=GL_RGB; - header.glInternalFormat=GL_COMPRESSED_RGB8_ETC2; - } - else if(format==ETC2PACKAGE_sRGB_NO_MIPMAPS) - { - header.glBaseInternalFormat=GL_SRGB; - header.glInternalFormat=GL_COMPRESSED_SRGB8_ETC2; - } - else if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS) - { - halfbytes=2; - header.glBaseInternalFormat=GL_RGBA; - header.glInternalFormat=GL_COMPRESSED_RGBA8_ETC2_EAC; - } - else if(format==ETC2PACKAGE_sRGBA_NO_MIPMAPS) - { - halfbytes=2; - header.glBaseInternalFormat=GL_SRGB8_ALPHA8; - header.glInternalFormat=GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; - } - else if(format==ETC2PACKAGE_RGBA1_NO_MIPMAPS) - { - header.glBaseInternalFormat=GL_RGBA; - header.glInternalFormat=GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; - } - else if(format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) - { - header.glBaseInternalFormat=GL_SRGB8_ALPHA8; - header.glInternalFormat=GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; - } - else if(format==ETC1_RGB_NO_MIPMAPS) - { - header.glBaseInternalFormat=GL_RGB; - header.glInternalFormat=GL_ETC1_RGB8_OES; - } - else - { - printf("internal error: bad format!\n"); - exit(1); - } - //write header - fwrite(&header,sizeof(KTX_header),1,f); - - //write size of compressed data.. which depend on the expanded size.. - unsigned int imagesize=(w*h*halfbytes)/2; - fwrite(&imagesize,sizeof(int),1,f); - } - else - { - //.pkm file, contains small header.. - - // Write magic number - fwrite(&magic[0], sizeof(unsigned char), 1, f); - fwrite(&magic[1], sizeof(unsigned char), 1, f); - fwrite(&magic[2], sizeof(unsigned char), 1, f); - fwrite(&magic[3], sizeof(unsigned char), 1, f); - - // Write version - fwrite(&version[0], sizeof(unsigned char), 1, f); - fwrite(&version[1], sizeof(unsigned char), 1, f); - - // Write texture type - if(texture_type==ETC2PACKAGE_RG_NO_MIPMAPS&&formatSigned) - { - unsigned short temp = ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS; - write_big_endian_2byte_word(&temp,f); - } - else if(texture_type==ETC2PACKAGE_R_NO_MIPMAPS&&formatSigned) - { - unsigned short temp = ETC2PACKAGE_R_SIGNED_NO_MIPMAPS; - write_big_endian_2byte_word(&temp,f); - } - else - write_big_endian_2byte_word(&texture_type, f); - - // Write binary header: the width and height as unsigned 16-bit words - write_big_endian_2byte_word(&wi, f); - write_big_endian_2byte_word(&hi, f); - - // Also write the active pixels. For instance, if we want to compress - // a 128 x 129 image, we have to extend it to 128 x 132 pixels. - // Then the wi and hi written above will be 128 and 132, but the - // additional information that we write below will be 128 and 129, - // to indicate that it is only the top 129 lines of data in the - // decompressed image that will be valid data, and the rest will - // be just garbage. - - unsigned short activew, activeh; - activew = width; - activeh = height; - - write_big_endian_2byte_word(&activew, f); - write_big_endian_2byte_word(&activeh, f); - } - int totblocks = expandedheight/4 * expandedwidth/4; - int countblocks = 0; - double percentageblocks=-1.0; - double oldpercentageblocks; - - if(format==ETC2PACKAGE_RG_NO_MIPMAPS) - { - //extract data from red and green channel into two alpha channels. - //note that the image will be 16-bit per channel in this case. - alphaimg= (unsigned char*)malloc(expandedwidth*expandedheight*2); - alphaimg2=(unsigned char*)malloc(expandedwidth*expandedheight*2); - setupAlphaTableAndValtab(); - if(!alphaimg||!alphaimg2) - { - printf("failed allocating space for alpha buffers!\n"); - exit(1); - } - for(y=0;y.\n",dstfile); - } -} - -// Compress an file. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void compressFile(char *srcfile,char *dstfile) -{ - uint8 *srcimg; - int width,height; - int extendedwidth, extendedheight; - struct _timeb tstruct; - int tstart; - int tstop; - // 0: compress from .any to .pkm with SPEED_FAST, METRIC_NONPERCEPTUAL, ETC - // 1: compress from .any to .pkm with SPEED_MEDIUM, METRIC_NONPERCEPTUAL, ETC - // 2: compress from .any to .pkm with SPEED_SLOW, METRIC_NONPERCEPTUAL, ETC - // 3: compress from .any to .pkm with SPEED_FAST, METRIC_PERCEPTUAL, ETC - // 4: compress from .any to .pkm with SPEED_MEDIUM, METRIC_PERCEPTUAL, ETC - // 5: compress from .any to .pkm with SPEED_SLOW, METRIC_PERCEPTUAL, ETC - // 6: decompress from .pkm to .any - // 7: calculate PSNR between .any and .any - // 8: compress from .any to .pkm with SPEED_FAST, METRIC_NONPERCEPTUAL, ETC2 - // 9: compress from .any to .pkm with SPEED_MEDIUM, METRIC_NONPERCEPTUAL, ETC2 - //10: compress from .any to .pkm with SPEED_SLOW, METRIC_NONPERCEPTUAL, ETC2 - //11: compress from .any to .pkm with SPEED_FAST, METRIC_PERCEPTUAL, ETC2 - //12: compress from .any to .pkm with SPEED_MEDIUM, METRIC_PERCEPTUAL, ETC2 - //13: compress from .any to .pkm with SPEED_SLOW, METRIC_PERCEPTUAL, ETC2 - - printf("\n"); - if(codec==CODEC_ETC) - printf("ETC codec, "); - else - printf("ETC2 codec, "); - if(speed==SPEED_FAST) - printf("using FAST compression mode and "); - else if(speed==SPEED_MEDIUM) - printf("using MEDIUM compression mode and "); - else - printf("using SLOW compression mode and "); - if(metric==METRIC_PERCEPTUAL) - printf("PERCEPTUAL error metric, "); - else - printf("NONPERCEPTUAL error metric, "); - if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS) - printf("in RGBA format"); - else if(format==ETC2PACKAGE_sRGBA_NO_MIPMAPS) - printf("in sRGBA format"); - else if(format==ETC2PACKAGE_RGBA1_NO_MIPMAPS) - printf("in RGB + punch-through alpha format"); - else if(format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) - printf("in sRGB + punch-through alpha format"); - else if(format==ETC2PACKAGE_R_NO_MIPMAPS) - printf("in R format"); - else if(format==ETC2PACKAGE_RGB_NO_MIPMAPS||format==ETC1_RGB_NO_MIPMAPS) - printf("in RGB format"); - else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) - printf("in RG format"); - else - printf("in OTHER format"); - printf("\n"); - if(readCompressParams()) - { - if(format==ETC2PACKAGE_R_NO_MIPMAPS||readSrcFile(srcfile,srcimg,width,height,extendedwidth, extendedheight)) - { - //make sure that alphasrcimg contains the alpha channel or is null here, and pass it to compressimagefile - uint8* alphaimg=NULL; - if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS||format==ETC2PACKAGE_RGBA1_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) - { - char str[300]; - //printf("reading alpha channel...."); - sprintf(str,"magick convert %s -alpha extract alpha.pgm\n",srcfile); - system(str); - readAlpha(alphaimg,width,height,extendedwidth,extendedheight); - printf("ok!\n"); - setupAlphaTableAndValtab(); - } - else if(format==ETC2PACKAGE_R_NO_MIPMAPS) - { - char str[300]; - sprintf(str,"magick convert %s alpha.pgm\n",srcfile); - system(str); - readAlpha(alphaimg,width,height,extendedwidth,extendedheight); - printf("read alpha ok, size is %d,%d (%d,%d)",width,height,extendedwidth,extendedheight); - setupAlphaTableAndValtab(); - } - printf("Compressing...\n"); - - tstart=time(NULL); - _ftime( &tstruct ); - tstart=tstart*1000+tstruct.millitm; - compressImageFile(srcimg,alphaimg,width,height,dstfile,extendedwidth, extendedheight); - tstop = time(NULL); - _ftime( &tstruct ); - tstop = tstop*1000+tstruct.millitm; - printf( "It took %u milliseconds to compress:\n", tstop - tstart); - calculatePSNRfile(dstfile,srcimg,alphaimg); - } - } -} - -// Calculates the PSNR between two files. -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -double calculatePSNRTwoFiles(char *srcfile1,char *srcfile2) -{ - uint8 *srcimg1; - uint8 *srcimg2; - int width1, height1; - int width2, height2; - double PSNR; - double perceptually_weighted_PSNR; - - if(readSrcFileNoExpand(srcfile1,srcimg1,width1,height1)) - { - if(readSrcFileNoExpand(srcfile2,srcimg2,width2,height2)) - { - if((width1 == width2) && (height1 == height2)) - { - PSNR = calculatePSNR(srcimg1, srcimg2, width1, height1); - printf("%f\n",PSNR); - perceptually_weighted_PSNR = calculateWeightedPSNR(srcimg1, srcimg2, width1, height1, 0.299, 0.587, 0.114); - } - else - { - printf("\n Width and height do no not match for image: width, height = (%d, %d) and (%d, %d)\n",width1,height1, width2, height2); - } - } - else - { - printf("Couldn't open file %s.\n",srcfile2); - } - } - else - { - printf("Couldn't open file %s.\n",srcfile1); - } - - return PSNR; -} - -// Main function -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int main(int argc,char *argv[]) -{ - if(argc==3 || argc==4 || argc == 5 || argc == 7 || argc == 9 || argc == 11 || argc == 13) - { - // The source file is always the second last one. - char srcfile[200]; - char dstfile[200]; - readArguments(argc,argv,srcfile,dstfile); - - int q = find_pos_of_extension(srcfile); - int q2 = find_pos_of_extension(dstfile); - - if(!fileExist(srcfile)) - { - printf("Error: file <%s> does not exist.\n",srcfile); - exit(0); - } - - if(mode==MODE_UNCOMPRESS) - { - printf("Decompressing .pkm/.ktx file ...\n"); - uint8* alphaimg=NULL, *img; - int w, h; - uncompressFile(srcfile,img,alphaimg,w,h); - writeOutputFile(dstfile,img,alphaimg,w,h); - } - else if(mode==MODE_PSNR) - { - calculatePSNRTwoFiles(srcfile,dstfile); - } - else - { - compressFile(srcfile, dstfile); - } - } - else - { - printf("ETCPACK v2.74 For ETC and ETC2\n"); - printf("Compresses and decompresses images using the Ericsson Texture Compression (ETC) version 1.0 and 2.0.\n\nUsage: etcpack srcfile dstfile\n\n"); - printf(" -s {fast|slow} Compression speed. Slow = exhaustive \n"); - printf(" search for optimal quality\n"); - printf(" (default: fast)\n"); - printf(" -e {perceptual|nonperceptual} Error metric: Perceptual (nicest) or \n"); - printf(" nonperceptual (highest PSNR)\n"); - printf(" (default: perceptual)\n"); - printf(" -c {etc1|etc2} Codec: etc1 (most compatible) or \n"); - printf(" etc2 (highest quality)\n"); - printf(" (default: etc2)\n"); - printf(" -f {R|R_signed|RG|RG_signed| Format: one, two, three or four \n"); - printf(" RGB|RGBA1|RGBA8| channels, and 1 or 8 bits for alpha\n"); - printf(" sRGB|sRGBA1|sRGBA8|} RGB or sRGB.\n"); - printf(" (1 equals punchthrough)\n"); - printf(" (default: RGB)\n"); - printf(" -v {on|off} Detailed progress info. (default on)\n"); - printf(" \n"); - printf("Examples: \n"); - printf(" etcpack img.ppm img.pkm Compresses img.ppm to img.pkm in\n"); - printf(" ETC2 RGB format\n"); - printf(" etcpack img.ppm img.ktx Compresses img.ppm to img.ktx in\n"); - printf(" ETC2 RGB format\n"); - printf(" etcpack img.pkm img_copy.ppm Decompresses img.pkm to img_copy.ppm\n"); - printf(" etcpack -s slow img.ppm img.pkm Compress using the slow mode.\n"); - printf(" etcpack -p orig.ppm copy.ppm Calculate PSNR between orig and copy\n"); - printf(" etcpack -f RGBA8 img.tga img.pkm Compresses img.tga to img.pkm, using \n"); - printf(" etc2 + alpha.\n"); - printf(" etcpack -f RG img.ppm img.pkm Compresses red and green channels of\n"); - printf(" img.ppm\n"); - } - return 0; -} +//// etcpack v2.74 +//// +//// NO WARRANTY +//// +//// BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE THE PROGRAM IS PROVIDED +//// "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF ANY KIND, EXTENDS NO +//// WARRANTIES OR CONDITIONS OF ANY KIND; EITHER EXPRESS, IMPLIED OR +//// STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, IMPLIED OR +//// STATUTORY WARRANTIES OR CONDITIONS OF TITLE, MERCHANTABILITY, +//// SATISFACTORY QUALITY, SUITABILITY AND FITNESS FOR A PARTICULAR +//// PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +//// PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +//// THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ERICSSON +//// MAKES NO WARRANTY THAT THE MANUFACTURE, SALE, OFFERING FOR SALE, +//// DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER THE LICENSE WILL BE FREE +//// FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER INTELLECTUAL +//// PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE LICENSE IS SUBJECT +//// TO YOUR SOLE RESPONSIBILITY TO MAKE SUCH DETERMINATION AND ACQUIRE +//// SUCH LICENSES AS MAY BE NECESSARY WITH RESPECT TO PATENTS, COPYRIGHT +//// AND OTHER INTELLECTUAL PROPERTY OF THIRD PARTIES. +//// +//// FOR THE AVOIDANCE OF DOUBT THE PROGRAM (I) IS NOT LICENSED FOR; (II) +//// IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY NOT BE USED FOR; +//// ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT LIMITED TO +//// OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR NETWORKS, +//// AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR ANY OTHER +//// COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR COMMUNICATION +//// SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE PROGRAM COULD LEAD TO +//// DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR ENVIRONMENTAL +//// DAMAGE. YOUR RIGHTS UNDER THIS LICENSE WILL TERMINATE AUTOMATICALLY +//// AND IMMEDIATELY WITHOUT NOTICE IF YOU FAIL TO COMPLY WITH THIS +//// PARAGRAPH. +//// +//// IN NO EVENT WILL ERICSSON, BE LIABLE FOR ANY DAMAGES WHATSOEVER, +//// INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, +//// INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN +//// CONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +//// NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY OTHER +//// COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING RENDERED +//// INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +//// THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) REGARDLESS OF THE +//// THEORY OF LIABILITY (CONTRACT, TORT OR OTHERWISE), EVEN IF SUCH HOLDER +//// OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +//// +//// (C) Ericsson AB 2005-2013. All Rights Reserved. +//// + +#include +#include +#include +#include +#include +#include +#include "image.hpp" + +// Typedefs +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef short int16; + +// Functions needed for decrompession ---- in etcdec.cxx +void read_big_endian_2byte_word(unsigned short *blockadr, FILE *f); +void read_big_endian_4byte_word(unsigned int *blockadr, FILE *f); +void unstuff57bits(unsigned int planar_word1, unsigned int planar_word2, unsigned int &planar57_word1, unsigned int &planar57_word2); +void unstuff59bits(unsigned int thumbT_word1, unsigned int thumbT_word2, unsigned int &thumbT59_word1, unsigned int &thumbT59_word2); +void unstuff58bits(unsigned int thumbH_word1, unsigned int thumbH_word2, unsigned int &thumbH58_word1, unsigned int &thumbH58_word2); +void decompressColor(int R_B, int G_B, int B_B, uint8 (colors_RGB444)[2][3], uint8 (colors)[2][3]); +void calculatePaintColors59T(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]); +void calculatePaintColors58H(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]); +void decompressBlockTHUMB59T(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty); +void decompressBlockTHUMB58H(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty); +void decompressBlockPlanar57(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img,int width,int height,int startx,int starty); +void decompressBlockDiffFlip(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty); +void decompressBlockETC2(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty); +void decompressBlockDifferentialWithAlpha(unsigned int block_part1,unsigned int block_part2, uint8* img, uint8* alpha, int width, int height, int startx, int starty); +void decompressBlockETC21BitAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alphaimg, int width,int height,int startx,int starty); +void decompressBlockTHUMB58HAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha,int width,int height,int startx,int starty); +void decompressBlockTHUMB59TAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha,int width,int height,int startx,int starty); +uint8 getbit(uint8 input, int frompos, int topos); +int clamp(int val); +void decompressBlockAlpha(uint8* data,uint8* img,int width,int height,int ix,int iy); +uint16 get16bits11bits(int base, int table, int mul, int index); +void decompressBlockAlpha16bit(uint8* data,uint8* img,int width,int height,int ix,int iy); +int16 get16bits11signed(int base, int table, int mul, int index); +void setupAlphaTable(); + + +// This source code is quite long. You can make it shorter by not including the +// code doing the exhaustive code. Then the -slow modes will not work, but the +// code will be approximately half the number of lines of code. +// Then the lines between "exhaustive code starts here" and "exhaustive code ends here" +// can then be removed. +#define EXHAUSTIVE_CODE_ACTIVE 1 + +// Remove warnings for unsafe functions such as strcpy +#pragma warning(disable : 4996) +// Remove warnings for conversions between different time variables +#pragma warning(disable : 4244) +// Remove warnings for negative or too big shifts +//#pragma warning(disable : 4293) + +#define CLAMP(ll,x,ul) (((x)<(ll)) ? (ll) : (((x)>(ul)) ? (ul) : (x))) +// The below code works as CLAMP(0, x, 255) if x < 255 +#define CLAMP_LEFT_ZERO(x) ((~(((int)(x))>>31))&(x)) +// The below code works as CLAMP(0, x, 255) if x is in [0,511] +#define CLAMP_RIGHT_255(x) (((( ((((int)(x))<<23)>>31) ))|(x))&0x000000ff) + +#define SQUARE(x) ((x)*(x)) +#define JAS_ROUND(x) (((x) < 0.0 ) ? ((int)((x)-0.5)) : ((int)((x)+0.5))) +#define JAS_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define JAS_MAX(a,b) ((a) > (b) ? (a) : (b)) + +// The error metric Wr Wg Wb should be definied so that Wr^2 + Wg^2 + Wb^2 = 1. +// Hence it is easier to first define the squared values and derive the weights +// as their square-roots. + +#define PERCEPTUAL_WEIGHT_R_SQUARED 0.299 +#define PERCEPTUAL_WEIGHT_G_SQUARED 0.587 +#define PERCEPTUAL_WEIGHT_B_SQUARED 0.114 + +#define PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000 299 +#define PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 587 +#define PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000 114 + +#define RED(img,width,x,y) img[3*(y*width+x)+0] +#define GREEN(img,width,x,y) img[3*(y*width+x)+1] +#define BLUE(img,width,x,y) img[3*(y*width+x)+2] + +#define SHIFT(size,startpos) ((startpos)-(size)+1) +#define MASK(size, startpos) (((2<<(size-1))-1) << SHIFT(size,startpos)) +#define PUTBITS( dest, data, size, startpos) dest = ((dest & ~MASK(size, startpos)) | ((data << SHIFT(size, startpos)) & MASK(size,startpos))) +#define SHIFTHIGH(size, startpos) (((startpos)-32)-(size)+1) +#define MASKHIGH(size, startpos) (((1<<(size))-1) << SHIFTHIGH(size,startpos)) +#define PUTBITSHIGH(dest, data, size, startpos) dest = ((dest & ~MASKHIGH(size, startpos)) | ((data << SHIFTHIGH(size, startpos)) & MASKHIGH(size,startpos))) +#define GETBITS(source, size, startpos) (( (source) >> ((startpos)-(size)+1) ) & ((1<<(size)) -1)) +#define GETBITSHIGH(source, size, startpos) (( (source) >> (((startpos)-32)-(size)+1) ) & ((1<<(size)) -1)) + +// Thumb macros and definitions +#define R_BITS59T 4 +#define G_BITS59T 4 +#define B_BITS59T 4 +#define R_BITS58H 4 +#define G_BITS58H 4 +#define B_BITS58H 4 +#define MAXIMUM_ERROR (255*255*16*1000) +#define R 0 +#define G 1 +#define B 2 +#define BLOCKHEIGHT 4 +#define BLOCKWIDTH 4 +#define BINPOW(power) (1<<(power)) +//#define RADIUS 2 +#define TABLE_BITS_59T 3 +#define TABLE_BITS_58H 3 + +// Global tables +static uint8 table59T[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 59 bit T-mode +static uint8 table58H[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 58 bit H-mode +uint8 weight[3] = {1,1,1}; // Color weight + +// Enums +enum { + PATTERN_H = 0, + PATTERN_T = 1 +}; + +enum{MODE_ETC1, MODE_THUMB_T, MODE_THUMB_H, MODE_PLANAR}; +// The ETC2 package of codecs includes the following codecs: +// +// codec enum +// -------------------------------------------------------- +// GL_COMPRESSED_R11_EAC 0x9270 +// GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +// GL_COMPRESSED_RG11_EAC 0x9272 +// GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +// GL_COMPRESSED_RGB8_ETC2 0x9274 +// GL_COMPRESSED_SRGB8_ETC2 0x9275 +// GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +// GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +// GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +// GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +// +// The older codec ETC1 is not included in the package +// GL_ETC1_RGB8_OES 0x8d64 +// but since ETC2 is backwards compatible an ETC1 texture can +// be decoded using the RGB8_ETC2 enum (0x9274) +// +// In a PKM-file, the codecs are stored using the following identifiers +// +// identifier value codec +// -------------------------------------------------------------------- +// ETC1_RGB_NO_MIPMAPS 0 GL_ETC1_RGB8_OES +// ETC2PACKAGE_RGB_NO_MIPMAPS 1 GL_COMPRESSED_RGB8_ETC2 +// ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD 2, not used - +// ETC2PACKAGE_RGBA_NO_MIPMAPS 3 GL_COMPRESSED_RGBA8_ETC2_EAC +// ETC2PACKAGE_RGBA1_NO_MIPMAPS 4 GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 +// ETC2PACKAGE_R_NO_MIPMAPS 5 GL_COMPRESSED_R11_EAC +// ETC2PACKAGE_RG_NO_MIPMAPS 6 GL_COMPRESSED_RG11_EAC +// ETC2PACKAGE_R_SIGNED_NO_MIPMAPS 7 GL_COMPRESSED_SIGNED_R11_EAC +// ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS 8 GL_COMPRESSED_SIGNED_RG11_EAC +// +// In the code, the identifiers are not always used strictly. For instance, the +// identifier ETC2PACKAGE_R_NO_MIPMAPS is sometimes used for both the unsigned +// (GL_COMPRESSED_R11_EAC) and signed (GL_COMPRESSED_SIGNED_R11_EAC) version of +// the codec. +// +enum{ETC1_RGB_NO_MIPMAPS,ETC2PACKAGE_RGB_NO_MIPMAPS,ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD,ETC2PACKAGE_RGBA_NO_MIPMAPS,ETC2PACKAGE_RGBA1_NO_MIPMAPS,ETC2PACKAGE_R_NO_MIPMAPS,ETC2PACKAGE_RG_NO_MIPMAPS,ETC2PACKAGE_R_SIGNED_NO_MIPMAPS,ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS,ETC2PACKAGE_sRGB_NO_MIPMAPS,ETC2PACKAGE_sRGBA_NO_MIPMAPS,ETC2PACKAGE_sRGBA1_NO_MIPMAPS}; +enum {MODE_COMPRESS, MODE_UNCOMPRESS, MODE_PSNR}; +enum {SPEED_SLOW, SPEED_FAST, SPEED_MEDIUM}; +enum {METRIC_PERCEPTUAL, METRIC_NONPERCEPTUAL}; +enum {CODEC_ETC, CODEC_ETC2}; + +int mode = MODE_COMPRESS; +int speed = SPEED_FAST; +int metric = METRIC_PERCEPTUAL; +int codec = CODEC_ETC2; +int format = ETC2PACKAGE_RGB_NO_MIPMAPS; +int verbose = true; +extern int formatSigned; +int ktxFile=0; +bool first_time_message = true; + +static int scramble[4] = {3, 2, 0, 1}; +static int unscramble[4] = {2, 3, 1, 0}; + +typedef struct KTX_header_t +{ + uint8 identifier[12]; + unsigned int endianness; + unsigned int glType; + unsigned int glTypeSize; + unsigned int glFormat; + unsigned int glInternalFormat; + unsigned int glBaseInternalFormat; + unsigned int pixelWidth; + unsigned int pixelHeight; + unsigned int pixelDepth; + unsigned int numberOfArrayElements; + unsigned int numberOfFaces; + unsigned int numberOfMipmapLevels; + unsigned int bytesOfKeyValueData; +} +KTX_header; +#define KTX_IDENTIFIER_REF { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A } + +#define KTX_ENDIAN_REF (0x04030201) +#define KTX_ENDIAN_REF_REV (0x01020304) + +enum {GL_R=0x1903,GL_RG=0x8227,GL_RGB=0x1907,GL_RGBA=0x1908}; +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_ETC1_RGB8_OES 0x8d64 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 + + +int ktx_identifier[] = KTX_IDENTIFIER_REF; + + +//converts indices from |a0|a1|e0|e1|i0|i1|m0|m1|b0|b1|f0|f1|j0|j1|n0|n1|c0|c1|g0|g1|k0|k1|o0|o1|d0|d1|h0|h1|l0|l1|p0|p1| previously used by T- and H-modes +// into |p0|o0|n0|m0|l0|k0|j0|i0|h0|g0|f0|e0|d0|c0|b0|a0|p1|o1|n1|m1|l1|k1|j1|i1|h1|g1|f1|e1|d1|c1|b1|a1| which should be used for all modes. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int indexConversion(int pixelIndices) +{ + int correctIndices = 0; + int LSB[4][4]; + int MSB[4][4]; + int shift=0; + for(int y=3; y>=0; y--) + { + for(int x=3; x>=0; x--) + { + LSB[x][y] = (pixelIndices>>shift)&1; + shift++; + MSB[x][y] = (pixelIndices>>shift)&1; + shift++; + } + } + shift=0; + for(int x=0; x<4; x++) + { + for(int y=0; y<4; y++) + { + correctIndices|=(LSB[x][y]<=0) // find file name extension + { + if(src[q]=='.') break; + q--; + } + if(q<0) + return -1; + else + return q; +} + +// Read source file. Does conversion if file format is not .ppm. +// Will expand file to be divisible by four in the x- and y- dimension. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +bool readSrcFile(const char *filename,uint8 *&img,int &width,int &height, int &expandedwidth, int &expandedheight) +{ + int w1,h1; + int wdiv4, hdiv4; + char str[255]; + + + // Delete temp file if it exists. + if(fileExist("tmp.ppm")) + { + sprintf(str, "del tmp.ppm\n"); + system(str); + } + + int q = find_pos_of_extension(filename); + if(!strcmp(&filename[q],".ppm")) + { + // Already a .ppm file. Just copy. + sprintf(str,"copy %s tmp.ppm \n", filename); + printf("Copying source file to tmp.ppm\n", filename); + } + else + { + // Converting from other format to .ppm + // + // Use your favorite command line image converter program, + // for instance Image Magick. Just make sure the syntax can + // be written as below: + // + // C:\magick convert source.jpg dest.ppm + // + sprintf(str,"magick convert %s tmp.ppm\n", filename); + printf("Converting source file from %s to .ppm\n", filename); + } + // Execute system call + system(str); + + int bitrate=8; + if(format==ETC2PACKAGE_RG_NO_MIPMAPS) + bitrate=16; + if(fReadPPM("tmp.ppm",w1,h1,img,bitrate)) + { + width=w1; + height=h1; + system("del tmp.ppm"); + + // Width must be divisible by 4 and height must be + // divisible by 4. Otherwise, we will expand the image + + wdiv4 = width / 4; + hdiv4 = height / 4; + + expandedwidth = width; + expandedheight = height; + + if( !(wdiv4 * 4 == width) ) + { + printf(" Width = %d is not divisible by four... ", width); + printf(" expanding image in x-dir... "); + if(expandToWidthDivByFour(img, width, height, expandedwidth, expandedheight,bitrate)) + { + printf("OK.\n"); + } + else + { + printf("\n Error: could not expand image\n"); + return false; + } + } + if( !(hdiv4 * 4 == height)) + { + printf(" Height = %d is not divisible by four... ", height); + printf(" expanding image in y-dir..."); + if(expandToHeightDivByFour(img, expandedwidth, height, expandedwidth, expandedheight,bitrate)) + { + printf("OK.\n"); + } + else + { + printf("\n Error: could not expand image\n"); + return false; + } + } + if(!(expandedwidth == width && expandedheight == height)) + printf("Active pixels: %dx%d. Expanded image: %dx%d\n",width,height,expandedwidth,expandedheight); + return true; + } + else + { + printf("Could not read tmp.ppm file\n"); + exit(1); + } + return false; + +} + +// Reads a file without expanding it to be divisible by 4. +// Is used when doing PSNR calculation between two files. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +bool readSrcFileNoExpand(const char *filename,uint8 *&img,int &width,int &height) +{ + int w1,h1; + char str[255]; + + + // Delete temp file if it exists. + if(fileExist("tmp.ppm")) + { + sprintf(str, "del tmp.ppm\n"); + system(str); + } + + + int q = find_pos_of_extension(filename); + if(!strcmp(&filename[q],".ppm")) + { + // Already a .ppm file. Just copy. + sprintf(str,"copy %s tmp.ppm \n", filename); + printf("Copying source file to tmp.ppm\n", filename); + } + else + { + // Converting from other format to .ppm + // + // Use your favorite command line image converter program, + // for instance Image Magick. Just make sure the syntax can + // be written as below: + // + // C:\magick convert source.jpg dest.ppm + // + sprintf(str,"magick convert %s tmp.ppm\n", filename); +// printf("Converting source file from %s to .ppm\n", filename); + } + // Execute system call + system(str); + + if(fReadPPM("tmp.ppm",w1,h1,img,8)) + { + width=w1; + height=h1; + system("del tmp.ppm"); + + return true; + } + return false; +} + +// Parses the arguments from the command line. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void readArguments(int argc,char *argv[],char* src,char *dst) +{ + int q; + + //new code!! do this in a more nicer way! + bool srcfound=false,dstfound=false; + for(int i=1; i> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + sum_error+=min_error; + } + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + return sum_error; +} + +#define MAXERR1000 1000*255*255*16 + +// Finds all pixel indices for a 2x4 block using perceptual weighting of error. +// Done using fixed poinit arithmetics where weights are multiplied by 1000. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockWithTable2x4percep1000(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) +{ + uint8 orig[3],approx[3]; + unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; + unsigned int sum_error=0; + int q, i; + + i = 0; + for(int x=startx; x> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + + + sum_error+=min_error; + } + + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + + return sum_error; +} + +// Finds all pixel indices for a 2x4 block using perceptual weighting of error. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +float compressBlockWithTable2x4percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) +{ + uint8 orig[3],approx[3]; + unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; + float sum_error=0; + int q, i; + + double wR2 = PERCEPTUAL_WEIGHT_R_SQUARED; + double wG2 = PERCEPTUAL_WEIGHT_G_SQUARED; + double wB2 = PERCEPTUAL_WEIGHT_B_SQUARED; + + i = 0; + for(int x=startx; x> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + + sum_error+=min_error; + } + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + + return sum_error; +} + +// Finds all pixel indices for a 4x2 block. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int compressBlockWithTable4x2(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) +{ + uint8 orig[3],approx[3]; + unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; + int sum_error=0; + int q; + int i; + + i = 0; + for(int x=startx; x> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + + sum_error+=min_error; + } + i+=2; + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + + return sum_error; +} + +// Finds all pixel indices for a 4x2 block using perceptual weighting of error. +// Done using fixed point arithmetics where 1000 corresponds to 1.0. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockWithTable4x2percep1000(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) +{ + uint8 orig[3],approx[3]; + unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; + unsigned int sum_error=0; + int q; + int i; + + i = 0; + for(int x=startx; x> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + + sum_error+=min_error; + } + i+=2; + + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + + return sum_error; +} + +// Finds all pixel indices for a 4x2 block using perceptual weighting of error. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +float compressBlockWithTable4x2percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) +{ + uint8 orig[3],approx[3]; + unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; + float sum_error=0; + int q; + int i; + float wR2 = (float) PERCEPTUAL_WEIGHT_R_SQUARED; + float wG2 = (float) PERCEPTUAL_WEIGHT_G_SQUARED; + float wB2 = (float) PERCEPTUAL_WEIGHT_B_SQUARED; + + i = 0; + for(int x=startx; x> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + + sum_error+=min_error; + } + i+=2; + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + + return sum_error; +} + +// Table for fast implementation of clamping to the interval [0,255] followed by addition of 255. +const int clamp_table_plus_255[768] = {0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, 0+255, + 0+255, 1+255, 2+255, 3+255, 4+255, 5+255, 6+255, 7+255, 8+255, 9+255, 10+255, 11+255, 12+255, 13+255, 14+255, 15+255, 16+255, 17+255, 18+255, 19+255, 20+255, 21+255, 22+255, 23+255, 24+255, 25+255, 26+255, 27+255, 28+255, 29+255, 30+255, 31+255, 32+255, 33+255, 34+255, 35+255, 36+255, 37+255, 38+255, 39+255, 40+255, 41+255, 42+255, 43+255, 44+255, 45+255, 46+255, 47+255, 48+255, 49+255, 50+255, 51+255, 52+255, 53+255, 54+255, 55+255, 56+255, 57+255, 58+255, 59+255, 60+255, 61+255, 62+255, 63+255, 64+255, 65+255, 66+255, 67+255, 68+255, 69+255, 70+255, 71+255, 72+255, 73+255, 74+255, 75+255, 76+255, 77+255, 78+255, 79+255, 80+255, 81+255, 82+255, 83+255, 84+255, 85+255, 86+255, 87+255, 88+255, 89+255, 90+255, 91+255, 92+255, 93+255, 94+255, 95+255, 96+255, 97+255, 98+255, 99+255, 100+255, 101+255, 102+255, 103+255, 104+255, 105+255, 106+255, 107+255, 108+255, 109+255, 110+255, 111+255, 112+255, 113+255, 114+255, 115+255, 116+255, 117+255, 118+255, 119+255, 120+255, 121+255, 122+255, 123+255, 124+255, 125+255, 126+255, 127+255, 128+255, 129+255, 130+255, 131+255, 132+255, 133+255, 134+255, 135+255, 136+255, 137+255, 138+255, 139+255, 140+255, 141+255, 142+255, 143+255, 144+255, 145+255, 146+255, 147+255, 148+255, 149+255, 150+255, 151+255, 152+255, 153+255, 154+255, 155+255, 156+255, 157+255, 158+255, 159+255, 160+255, 161+255, 162+255, 163+255, 164+255, 165+255, 166+255, 167+255, 168+255, 169+255, 170+255, 171+255, 172+255, 173+255, 174+255, 175+255, 176+255, 177+255, 178+255, 179+255, 180+255, 181+255, 182+255, 183+255, 184+255, 185+255, 186+255, 187+255, 188+255, 189+255, 190+255, 191+255, 192+255, 193+255, 194+255, 195+255, 196+255, 197+255, 198+255, 199+255, 200+255, 201+255, 202+255, 203+255, 204+255, 205+255, 206+255, 207+255, 208+255, 209+255, 210+255, 211+255, + 212+255, 213+255, 214+255, 215+255, 216+255, 217+255, 218+255, 219+255, 220+255, 221+255, 222+255, 223+255, 224+255, 225+255, 226+255, 227+255, 228+255, 229+255, 230+255, 231+255, 232+255, 233+255, 234+255, 235+255, 236+255, 237+255, 238+255, 239+255, 240+255, 241+255, 242+255, 243+255, 244+255, 245+255, 246+255, 247+255, 248+255, 249+255, 250+255, 251+255, 252+255, 253+255, 254+255, 255+255, + 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, + 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255, 255+255}; + +// Table for fast implementationi of clamping to the interval [0,255] +const int clamp_table[768] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; + +// Table for fast implementation of squaring for numbers in the interval [-255, 255] +const unsigned int square_table[511] = {65025, 64516, 64009, 63504, 63001, 62500, 62001, 61504, 61009, 60516, 60025, 59536, 59049, 58564, 58081, 57600, + 57121, 56644, 56169, 55696, 55225, 54756, 54289, 53824, 53361, 52900, 52441, 51984, 51529, 51076, 50625, 50176, + 49729, 49284, 48841, 48400, 47961, 47524, 47089, 46656, 46225, 45796, 45369, 44944, 44521, 44100, 43681, 43264, + 42849, 42436, 42025, 41616, 41209, 40804, 40401, 40000, 39601, 39204, 38809, 38416, 38025, 37636, 37249, 36864, + 36481, 36100, 35721, 35344, 34969, 34596, 34225, 33856, 33489, 33124, 32761, 32400, 32041, 31684, 31329, 30976, + 30625, 30276, 29929, 29584, 29241, 28900, 28561, 28224, 27889, 27556, 27225, 26896, 26569, 26244, 25921, 25600, + 25281, 24964, 24649, 24336, 24025, 23716, 23409, 23104, 22801, 22500, 22201, 21904, 21609, 21316, 21025, 20736, + 20449, 20164, 19881, 19600, 19321, 19044, 18769, 18496, 18225, 17956, 17689, 17424, 17161, 16900, 16641, 16384, + 16129, 15876, 15625, 15376, 15129, 14884, 14641, 14400, 14161, 13924, 13689, 13456, 13225, 12996, 12769, 12544, + 12321, 12100, 11881, 11664, 11449, 11236, 11025, 10816, 10609, 10404, 10201, 10000, 9801, 9604, 9409, 9216, + 9025, 8836, 8649, 8464, 8281, 8100, 7921, 7744, 7569, 7396, 7225, 7056, 6889, 6724, 6561, 6400, + 6241, 6084, 5929, 5776, 5625, 5476, 5329, 5184, 5041, 4900, 4761, 4624, 4489, 4356, 4225, 4096, + 3969, 3844, 3721, 3600, 3481, 3364, 3249, 3136, 3025, 2916, 2809, 2704, 2601, 2500, 2401, 2304, + 2209, 2116, 2025, 1936, 1849, 1764, 1681, 1600, 1521, 1444, 1369, 1296, 1225, 1156, 1089, 1024, + 961, 900, 841, 784, 729, 676, 625, 576, 529, 484, 441, 400, 361, 324, 289, 256, + 225, 196, 169, 144, 121, 100, 81, 64, 49, 36, 25, 16, 9, 4, 1, + 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, + 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, + 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, + 2304, 2401, 2500, 2601, 2704, 2809, 2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, + 4096, 4225, 4356, 4489, 4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, + 6400, 6561, 6724, 6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, + 9216, 9409, 9604, 9801, 10000, 10201, 10404, 10609, 10816, 11025, 11236, 11449, 11664, 11881, 12100, 12321, + 12544, 12769, 12996, 13225, 13456, 13689, 13924, 14161, 14400, 14641, 14884, 15129, 15376, 15625, 15876, 16129, + 16384, 16641, 16900, 17161, 17424, 17689, 17956, 18225, 18496, 18769, 19044, 19321, 19600, 19881, 20164, 20449, + 20736, 21025, 21316, 21609, 21904, 22201, 22500, 22801, 23104, 23409, 23716, 24025, 24336, 24649, 24964, 25281, + 25600, 25921, 26244, 26569, 26896, 27225, 27556, 27889, 28224, 28561, 28900, 29241, 29584, 29929, 30276, 30625, + 30976, 31329, 31684, 32041, 32400, 32761, 33124, 33489, 33856, 34225, 34596, 34969, 35344, 35721, 36100, 36481, + 36864, 37249, 37636, 38025, 38416, 38809, 39204, 39601, 40000, 40401, 40804, 41209, 41616, 42025, 42436, 42849, + 43264, 43681, 44100, 44521, 44944, 45369, 45796, 46225, 46656, 47089, 47524, 47961, 48400, 48841, 49284, 49729, + 50176, 50625, 51076, 51529, 51984, 52441, 52900, 53361, 53824, 54289, 54756, 55225, 55696, 56169, 56644, 57121, + 57600, 58081, 58564, 59049, 59536, 60025, 60516, 61009, 61504, 62001, 62500, 63001, 63504, 64009, 64516, 65025}; + +// Abbreviated variable names to make below tables smaller in source code size +#define KR PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000 +#define KG PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 +#define KB PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000 + +// Table for fast implementation of squaring for numbers in the interval [-255, 255] multiplied by the perceptual weight for red. +const unsigned int square_table_percep_red[511] = { + 65025*KR, 64516*KR, 64009*KR, 63504*KR, 63001*KR, 62500*KR, 62001*KR, 61504*KR, 61009*KR, 60516*KR, 60025*KR, 59536*KR, 59049*KR, 58564*KR, 58081*KR, 57600*KR, + 57121*KR, 56644*KR, 56169*KR, 55696*KR, 55225*KR, 54756*KR, 54289*KR, 53824*KR, 53361*KR, 52900*KR, 52441*KR, 51984*KR, 51529*KR, 51076*KR, 50625*KR, 50176*KR, + 49729*KR, 49284*KR, 48841*KR, 48400*KR, 47961*KR, 47524*KR, 47089*KR, 46656*KR, 46225*KR, 45796*KR, 45369*KR, 44944*KR, 44521*KR, 44100*KR, 43681*KR, 43264*KR, + 42849*KR, 42436*KR, 42025*KR, 41616*KR, 41209*KR, 40804*KR, 40401*KR, 40000*KR, 39601*KR, 39204*KR, 38809*KR, 38416*KR, 38025*KR, 37636*KR, 37249*KR, 36864*KR, + 36481*KR, 36100*KR, 35721*KR, 35344*KR, 34969*KR, 34596*KR, 34225*KR, 33856*KR, 33489*KR, 33124*KR, 32761*KR, 32400*KR, 32041*KR, 31684*KR, 31329*KR, 30976*KR, + 30625*KR, 30276*KR, 29929*KR, 29584*KR, 29241*KR, 28900*KR, 28561*KR, 28224*KR, 27889*KR, 27556*KR, 27225*KR, 26896*KR, 26569*KR, 26244*KR, 25921*KR, 25600*KR, + 25281*KR, 24964*KR, 24649*KR, 24336*KR, 24025*KR, 23716*KR, 23409*KR, 23104*KR, 22801*KR, 22500*KR, 22201*KR, 21904*KR, 21609*KR, 21316*KR, 21025*KR, 20736*KR, + 20449*KR, 20164*KR, 19881*KR, 19600*KR, 19321*KR, 19044*KR, 18769*KR, 18496*KR, 18225*KR, 17956*KR, 17689*KR, 17424*KR, 17161*KR, 16900*KR, 16641*KR, 16384*KR, + 16129*KR, 15876*KR, 15625*KR, 15376*KR, 15129*KR, 14884*KR, 14641*KR, 14400*KR, 14161*KR, 13924*KR, 13689*KR, 13456*KR, 13225*KR, 12996*KR, 12769*KR, 12544*KR, + 12321*KR, 12100*KR, 11881*KR, 11664*KR, 11449*KR, 11236*KR, 11025*KR, 10816*KR, 10609*KR, 10404*KR, 10201*KR, 10000*KR, 9801*KR, 9604*KR, 9409*KR, 9216*KR, + 9025*KR, 8836*KR, 8649*KR, 8464*KR, 8281*KR, 8100*KR, 7921*KR, 7744*KR, 7569*KR, 7396*KR, 7225*KR, 7056*KR, 6889*KR, 6724*KR, 6561*KR, 6400*KR, + 6241*KR, 6084*KR, 5929*KR, 5776*KR, 5625*KR, 5476*KR, 5329*KR, 5184*KR, 5041*KR, 4900*KR, 4761*KR, 4624*KR, 4489*KR, 4356*KR, 4225*KR, 4096*KR, + 3969*KR, 3844*KR, 3721*KR, 3600*KR, 3481*KR, 3364*KR, 3249*KR, 3136*KR, 3025*KR, 2916*KR, 2809*KR, 2704*KR, 2601*KR, 2500*KR, 2401*KR, 2304*KR, + 2209*KR, 2116*KR, 2025*KR, 1936*KR, 1849*KR, 1764*KR, 1681*KR, 1600*KR, 1521*KR, 1444*KR, 1369*KR, 1296*KR, 1225*KR, 1156*KR, 1089*KR, 1024*KR, + 961*KR, 900*KR, 841*KR, 784*KR, 729*KR, 676*KR, 625*KR, 576*KR, 529*KR, 484*KR, 441*KR, 400*KR, 361*KR, 324*KR, 289*KR, 256*KR, + 225*KR, 196*KR, 169*KR, 144*KR, 121*KR, 100*KR, 81*KR, 64*KR, 49*KR, 36*KR, 25*KR, 16*KR, 9*KR, 4*KR, 1*KR, + 0*KR, 1*KR, 4*KR, 9*KR, 16*KR, 25*KR, 36*KR, 49*KR, 64*KR, 81*KR, 100*KR, 121*KR, 144*KR, 169*KR, 196*KR, 225*KR, + 256*KR, 289*KR, 324*KR, 361*KR, 400*KR, 441*KR, 484*KR, 529*KR, 576*KR, 625*KR, 676*KR, 729*KR, 784*KR, 841*KR, 900*KR, 961*KR, + 1024*KR, 1089*KR, 1156*KR, 1225*KR, 1296*KR, 1369*KR, 1444*KR, 1521*KR, 1600*KR, 1681*KR, 1764*KR, 1849*KR, 1936*KR, 2025*KR, 2116*KR, 2209*KR, + 2304*KR, 2401*KR, 2500*KR, 2601*KR, 2704*KR, 2809*KR, 2916*KR, 3025*KR, 3136*KR, 3249*KR, 3364*KR, 3481*KR, 3600*KR, 3721*KR, 3844*KR, 3969*KR, + 4096*KR, 4225*KR, 4356*KR, 4489*KR, 4624*KR, 4761*KR, 4900*KR, 5041*KR, 5184*KR, 5329*KR, 5476*KR, 5625*KR, 5776*KR, 5929*KR, 6084*KR, 6241*KR, + 6400*KR, 6561*KR, 6724*KR, 6889*KR, 7056*KR, 7225*KR, 7396*KR, 7569*KR, 7744*KR, 7921*KR, 8100*KR, 8281*KR, 8464*KR, 8649*KR, 8836*KR, 9025*KR, + 9216*KR, 9409*KR, 9604*KR, 9801*KR, 10000*KR, 10201*KR, 10404*KR, 10609*KR, 10816*KR, 11025*KR, 11236*KR, 11449*KR, 11664*KR, 11881*KR, 12100*KR, 12321*KR, + 12544*KR, 12769*KR, 12996*KR, 13225*KR, 13456*KR, 13689*KR, 13924*KR, 14161*KR, 14400*KR, 14641*KR, 14884*KR, 15129*KR, 15376*KR, 15625*KR, 15876*KR, 16129*KR, + 16384*KR, 16641*KR, 16900*KR, 17161*KR, 17424*KR, 17689*KR, 17956*KR, 18225*KR, 18496*KR, 18769*KR, 19044*KR, 19321*KR, 19600*KR, 19881*KR, 20164*KR, 20449*KR, + 20736*KR, 21025*KR, 21316*KR, 21609*KR, 21904*KR, 22201*KR, 22500*KR, 22801*KR, 23104*KR, 23409*KR, 23716*KR, 24025*KR, 24336*KR, 24649*KR, 24964*KR, 25281*KR, + 25600*KR, 25921*KR, 26244*KR, 26569*KR, 26896*KR, 27225*KR, 27556*KR, 27889*KR, 28224*KR, 28561*KR, 28900*KR, 29241*KR, 29584*KR, 29929*KR, 30276*KR, 30625*KR, + 30976*KR, 31329*KR, 31684*KR, 32041*KR, 32400*KR, 32761*KR, 33124*KR, 33489*KR, 33856*KR, 34225*KR, 34596*KR, 34969*KR, 35344*KR, 35721*KR, 36100*KR, 36481*KR, + 36864*KR, 37249*KR, 37636*KR, 38025*KR, 38416*KR, 38809*KR, 39204*KR, 39601*KR, 40000*KR, 40401*KR, 40804*KR, 41209*KR, 41616*KR, 42025*KR, 42436*KR, 42849*KR, + 43264*KR, 43681*KR, 44100*KR, 44521*KR, 44944*KR, 45369*KR, 45796*KR, 46225*KR, 46656*KR, 47089*KR, 47524*KR, 47961*KR, 48400*KR, 48841*KR, 49284*KR, 49729*KR, + 50176*KR, 50625*KR, 51076*KR, 51529*KR, 51984*KR, 52441*KR, 52900*KR, 53361*KR, 53824*KR, 54289*KR, 54756*KR, 55225*KR, 55696*KR, 56169*KR, 56644*KR, 57121*KR, + 57600*KR, 58081*KR, 58564*KR, 59049*KR, 59536*KR, 60025*KR, 60516*KR, 61009*KR, 61504*KR, 62001*KR, 62500*KR, 63001*KR, 63504*KR, 64009*KR, 64516*KR, 65025*KR}; + +// Table for fast implementation of squaring for numbers in the interval [-255, 255] multiplied by the perceptual weight for green. +const unsigned int square_table_percep_green[511] = { + 65025*KG, 64516*KG, 64009*KG, 63504*KG, 63001*KG, 62500*KG, 62001*KG, 61504*KG, 61009*KG, 60516*KG, 60025*KG, 59536*KG, 59049*KG, 58564*KG, 58081*KG, 57600*KG, + 57121*KG, 56644*KG, 56169*KG, 55696*KG, 55225*KG, 54756*KG, 54289*KG, 53824*KG, 53361*KG, 52900*KG, 52441*KG, 51984*KG, 51529*KG, 51076*KG, 50625*KG, 50176*KG, + 49729*KG, 49284*KG, 48841*KG, 48400*KG, 47961*KG, 47524*KG, 47089*KG, 46656*KG, 46225*KG, 45796*KG, 45369*KG, 44944*KG, 44521*KG, 44100*KG, 43681*KG, 43264*KG, + 42849*KG, 42436*KG, 42025*KG, 41616*KG, 41209*KG, 40804*KG, 40401*KG, 40000*KG, 39601*KG, 39204*KG, 38809*KG, 38416*KG, 38025*KG, 37636*KG, 37249*KG, 36864*KG, + 36481*KG, 36100*KG, 35721*KG, 35344*KG, 34969*KG, 34596*KG, 34225*KG, 33856*KG, 33489*KG, 33124*KG, 32761*KG, 32400*KG, 32041*KG, 31684*KG, 31329*KG, 30976*KG, + 30625*KG, 30276*KG, 29929*KG, 29584*KG, 29241*KG, 28900*KG, 28561*KG, 28224*KG, 27889*KG, 27556*KG, 27225*KG, 26896*KG, 26569*KG, 26244*KG, 25921*KG, 25600*KG, + 25281*KG, 24964*KG, 24649*KG, 24336*KG, 24025*KG, 23716*KG, 23409*KG, 23104*KG, 22801*KG, 22500*KG, 22201*KG, 21904*KG, 21609*KG, 21316*KG, 21025*KG, 20736*KG, + 20449*KG, 20164*KG, 19881*KG, 19600*KG, 19321*KG, 19044*KG, 18769*KG, 18496*KG, 18225*KG, 17956*KG, 17689*KG, 17424*KG, 17161*KG, 16900*KG, 16641*KG, 16384*KG, + 16129*KG, 15876*KG, 15625*KG, 15376*KG, 15129*KG, 14884*KG, 14641*KG, 14400*KG, 14161*KG, 13924*KG, 13689*KG, 13456*KG, 13225*KG, 12996*KG, 12769*KG, 12544*KG, + 12321*KG, 12100*KG, 11881*KG, 11664*KG, 11449*KG, 11236*KG, 11025*KG, 10816*KG, 10609*KG, 10404*KG, 10201*KG, 10000*KG, 9801*KG, 9604*KG, 9409*KG, 9216*KG, + 9025*KG, 8836*KG, 8649*KG, 8464*KG, 8281*KG, 8100*KG, 7921*KG, 7744*KG, 7569*KG, 7396*KG, 7225*KG, 7056*KG, 6889*KG, 6724*KG, 6561*KG, 6400*KG, + 6241*KG, 6084*KG, 5929*KG, 5776*KG, 5625*KG, 5476*KG, 5329*KG, 5184*KG, 5041*KG, 4900*KG, 4761*KG, 4624*KG, 4489*KG, 4356*KG, 4225*KG, 4096*KG, + 3969*KG, 3844*KG, 3721*KG, 3600*KG, 3481*KG, 3364*KG, 3249*KG, 3136*KG, 3025*KG, 2916*KG, 2809*KG, 2704*KG, 2601*KG, 2500*KG, 2401*KG, 2304*KG, + 2209*KG, 2116*KG, 2025*KG, 1936*KG, 1849*KG, 1764*KG, 1681*KG, 1600*KG, 1521*KG, 1444*KG, 1369*KG, 1296*KG, 1225*KG, 1156*KG, 1089*KG, 1024*KG, + 961*KG, 900*KG, 841*KG, 784*KG, 729*KG, 676*KG, 625*KG, 576*KG, 529*KG, 484*KG, 441*KG, 400*KG, 361*KG, 324*KG, 289*KG, 256*KG, + 225*KG, 196*KG, 169*KG, 144*KG, 121*KG, 100*KG, 81*KG, 64*KG, 49*KG, 36*KG, 25*KG, 16*KG, 9*KG, 4*KG, 1*KG, + 0*KG, 1*KG, 4*KG, 9*KG, 16*KG, 25*KG, 36*KG, 49*KG, 64*KG, 81*KG, 100*KG, 121*KG, 144*KG, 169*KG, 196*KG, 225*KG, + 256*KG, 289*KG, 324*KG, 361*KG, 400*KG, 441*KG, 484*KG, 529*KG, 576*KG, 625*KG, 676*KG, 729*KG, 784*KG, 841*KG, 900*KG, 961*KG, + 1024*KG, 1089*KG, 1156*KG, 1225*KG, 1296*KG, 1369*KG, 1444*KG, 1521*KG, 1600*KG, 1681*KG, 1764*KG, 1849*KG, 1936*KG, 2025*KG, 2116*KG, 2209*KG, + 2304*KG, 2401*KG, 2500*KG, 2601*KG, 2704*KG, 2809*KG, 2916*KG, 3025*KG, 3136*KG, 3249*KG, 3364*KG, 3481*KG, 3600*KG, 3721*KG, 3844*KG, 3969*KG, + 4096*KG, 4225*KG, 4356*KG, 4489*KG, 4624*KG, 4761*KG, 4900*KG, 5041*KG, 5184*KG, 5329*KG, 5476*KG, 5625*KG, 5776*KG, 5929*KG, 6084*KG, 6241*KG, + 6400*KG, 6561*KG, 6724*KG, 6889*KG, 7056*KG, 7225*KG, 7396*KG, 7569*KG, 7744*KG, 7921*KG, 8100*KG, 8281*KG, 8464*KG, 8649*KG, 8836*KG, 9025*KG, + 9216*KG, 9409*KG, 9604*KG, 9801*KG, 10000*KG, 10201*KG, 10404*KG, 10609*KG, 10816*KG, 11025*KG, 11236*KG, 11449*KG, 11664*KG, 11881*KG, 12100*KG, 12321*KG, + 12544*KG, 12769*KG, 12996*KG, 13225*KG, 13456*KG, 13689*KG, 13924*KG, 14161*KG, 14400*KG, 14641*KG, 14884*KG, 15129*KG, 15376*KG, 15625*KG, 15876*KG, 16129*KG, + 16384*KG, 16641*KG, 16900*KG, 17161*KG, 17424*KG, 17689*KG, 17956*KG, 18225*KG, 18496*KG, 18769*KG, 19044*KG, 19321*KG, 19600*KG, 19881*KG, 20164*KG, 20449*KG, + 20736*KG, 21025*KG, 21316*KG, 21609*KG, 21904*KG, 22201*KG, 22500*KG, 22801*KG, 23104*KG, 23409*KG, 23716*KG, 24025*KG, 24336*KG, 24649*KG, 24964*KG, 25281*KG, + 25600*KG, 25921*KG, 26244*KG, 26569*KG, 26896*KG, 27225*KG, 27556*KG, 27889*KG, 28224*KG, 28561*KG, 28900*KG, 29241*KG, 29584*KG, 29929*KG, 30276*KG, 30625*KG, + 30976*KG, 31329*KG, 31684*KG, 32041*KG, 32400*KG, 32761*KG, 33124*KG, 33489*KG, 33856*KG, 34225*KG, 34596*KG, 34969*KG, 35344*KG, 35721*KG, 36100*KG, 36481*KG, + 36864*KG, 37249*KG, 37636*KG, 38025*KG, 38416*KG, 38809*KG, 39204*KG, 39601*KG, 40000*KG, 40401*KG, 40804*KG, 41209*KG, 41616*KG, 42025*KG, 42436*KG, 42849*KG, + 43264*KG, 43681*KG, 44100*KG, 44521*KG, 44944*KG, 45369*KG, 45796*KG, 46225*KG, 46656*KG, 47089*KG, 47524*KG, 47961*KG, 48400*KG, 48841*KG, 49284*KG, 49729*KG, + 50176*KG, 50625*KG, 51076*KG, 51529*KG, 51984*KG, 52441*KG, 52900*KG, 53361*KG, 53824*KG, 54289*KG, 54756*KG, 55225*KG, 55696*KG, 56169*KG, 56644*KG, 57121*KG, + 57600*KG, 58081*KG, 58564*KG, 59049*KG, 59536*KG, 60025*KG, 60516*KG, 61009*KG, 61504*KG, 62001*KG, 62500*KG, 63001*KG, 63504*KG, 64009*KG, 64516*KG, 65025*KG}; + +// Table for fast implementation of squaring for numbers in the interval [-255, 255] multiplied by the perceptual weight for blue. +const unsigned int square_table_percep_blue[511] = { + 65025*KB, 64516*KB, 64009*KB, 63504*KB, 63001*KB, 62500*KB, 62001*KB, 61504*KB, 61009*KB, 60516*KB, 60025*KB, 59536*KB, 59049*KB, 58564*KB, 58081*KB, 57600*KB, + 57121*KB, 56644*KB, 56169*KB, 55696*KB, 55225*KB, 54756*KB, 54289*KB, 53824*KB, 53361*KB, 52900*KB, 52441*KB, 51984*KB, 51529*KB, 51076*KB, 50625*KB, 50176*KB, + 49729*KB, 49284*KB, 48841*KB, 48400*KB, 47961*KB, 47524*KB, 47089*KB, 46656*KB, 46225*KB, 45796*KB, 45369*KB, 44944*KB, 44521*KB, 44100*KB, 43681*KB, 43264*KB, + 42849*KB, 42436*KB, 42025*KB, 41616*KB, 41209*KB, 40804*KB, 40401*KB, 40000*KB, 39601*KB, 39204*KB, 38809*KB, 38416*KB, 38025*KB, 37636*KB, 37249*KB, 36864*KB, + 36481*KB, 36100*KB, 35721*KB, 35344*KB, 34969*KB, 34596*KB, 34225*KB, 33856*KB, 33489*KB, 33124*KB, 32761*KB, 32400*KB, 32041*KB, 31684*KB, 31329*KB, 30976*KB, + 30625*KB, 30276*KB, 29929*KB, 29584*KB, 29241*KB, 28900*KB, 28561*KB, 28224*KB, 27889*KB, 27556*KB, 27225*KB, 26896*KB, 26569*KB, 26244*KB, 25921*KB, 25600*KB, + 25281*KB, 24964*KB, 24649*KB, 24336*KB, 24025*KB, 23716*KB, 23409*KB, 23104*KB, 22801*KB, 22500*KB, 22201*KB, 21904*KB, 21609*KB, 21316*KB, 21025*KB, 20736*KB, + 20449*KB, 20164*KB, 19881*KB, 19600*KB, 19321*KB, 19044*KB, 18769*KB, 18496*KB, 18225*KB, 17956*KB, 17689*KB, 17424*KB, 17161*KB, 16900*KB, 16641*KB, 16384*KB, + 16129*KB, 15876*KB, 15625*KB, 15376*KB, 15129*KB, 14884*KB, 14641*KB, 14400*KB, 14161*KB, 13924*KB, 13689*KB, 13456*KB, 13225*KB, 12996*KB, 12769*KB, 12544*KB, + 12321*KB, 12100*KB, 11881*KB, 11664*KB, 11449*KB, 11236*KB, 11025*KB, 10816*KB, 10609*KB, 10404*KB, 10201*KB, 10000*KB, 9801*KB, 9604*KB, 9409*KB, 9216*KB, + 9025*KB, 8836*KB, 8649*KB, 8464*KB, 8281*KB, 8100*KB, 7921*KB, 7744*KB, 7569*KB, 7396*KB, 7225*KB, 7056*KB, 6889*KB, 6724*KB, 6561*KB, 6400*KB, + 6241*KB, 6084*KB, 5929*KB, 5776*KB, 5625*KB, 5476*KB, 5329*KB, 5184*KB, 5041*KB, 4900*KB, 4761*KB, 4624*KB, 4489*KB, 4356*KB, 4225*KB, 4096*KB, + 3969*KB, 3844*KB, 3721*KB, 3600*KB, 3481*KB, 3364*KB, 3249*KB, 3136*KB, 3025*KB, 2916*KB, 2809*KB, 2704*KB, 2601*KB, 2500*KB, 2401*KB, 2304*KB, + 2209*KB, 2116*KB, 2025*KB, 1936*KB, 1849*KB, 1764*KB, 1681*KB, 1600*KB, 1521*KB, 1444*KB, 1369*KB, 1296*KB, 1225*KB, 1156*KB, 1089*KB, 1024*KB, + 961*KB, 900*KB, 841*KB, 784*KB, 729*KB, 676*KB, 625*KB, 576*KB, 529*KB, 484*KB, 441*KB, 400*KB, 361*KB, 324*KB, 289*KB, 256*KB, + 225*KB, 196*KB, 169*KB, 144*KB, 121*KB, 100*KB, 81*KB, 64*KB, 49*KB, 36*KB, 25*KB, 16*KB, 9*KB, 4*KB, 1*KB, + 0*KB, 1*KB, 4*KB, 9*KB, 16*KB, 25*KB, 36*KB, 49*KB, 64*KB, 81*KB, 100*KB, 121*KB, 144*KB, 169*KB, 196*KB, 225*KB, + 256*KB, 289*KB, 324*KB, 361*KB, 400*KB, 441*KB, 484*KB, 529*KB, 576*KB, 625*KB, 676*KB, 729*KB, 784*KB, 841*KB, 900*KB, 961*KB, + 1024*KB, 1089*KB, 1156*KB, 1225*KB, 1296*KB, 1369*KB, 1444*KB, 1521*KB, 1600*KB, 1681*KB, 1764*KB, 1849*KB, 1936*KB, 2025*KB, 2116*KB, 2209*KB, + 2304*KB, 2401*KB, 2500*KB, 2601*KB, 2704*KB, 2809*KB, 2916*KB, 3025*KB, 3136*KB, 3249*KB, 3364*KB, 3481*KB, 3600*KB, 3721*KB, 3844*KB, 3969*KB, + 4096*KB, 4225*KB, 4356*KB, 4489*KB, 4624*KB, 4761*KB, 4900*KB, 5041*KB, 5184*KB, 5329*KB, 5476*KB, 5625*KB, 5776*KB, 5929*KB, 6084*KB, 6241*KB, + 6400*KB, 6561*KB, 6724*KB, 6889*KB, 7056*KB, 7225*KB, 7396*KB, 7569*KB, 7744*KB, 7921*KB, 8100*KB, 8281*KB, 8464*KB, 8649*KB, 8836*KB, 9025*KB, + 9216*KB, 9409*KB, 9604*KB, 9801*KB, 10000*KB, 10201*KB, 10404*KB, 10609*KB, 10816*KB, 11025*KB, 11236*KB, 11449*KB, 11664*KB, 11881*KB, 12100*KB, 12321*KB, + 12544*KB, 12769*KB, 12996*KB, 13225*KB, 13456*KB, 13689*KB, 13924*KB, 14161*KB, 14400*KB, 14641*KB, 14884*KB, 15129*KB, 15376*KB, 15625*KB, 15876*KB, 16129*KB, + 16384*KB, 16641*KB, 16900*KB, 17161*KB, 17424*KB, 17689*KB, 17956*KB, 18225*KB, 18496*KB, 18769*KB, 19044*KB, 19321*KB, 19600*KB, 19881*KB, 20164*KB, 20449*KB, + 20736*KB, 21025*KB, 21316*KB, 21609*KB, 21904*KB, 22201*KB, 22500*KB, 22801*KB, 23104*KB, 23409*KB, 23716*KB, 24025*KB, 24336*KB, 24649*KB, 24964*KB, 25281*KB, + 25600*KB, 25921*KB, 26244*KB, 26569*KB, 26896*KB, 27225*KB, 27556*KB, 27889*KB, 28224*KB, 28561*KB, 28900*KB, 29241*KB, 29584*KB, 29929*KB, 30276*KB, 30625*KB, + 30976*KB, 31329*KB, 31684*KB, 32041*KB, 32400*KB, 32761*KB, 33124*KB, 33489*KB, 33856*KB, 34225*KB, 34596*KB, 34969*KB, 35344*KB, 35721*KB, 36100*KB, 36481*KB, + 36864*KB, 37249*KB, 37636*KB, 38025*KB, 38416*KB, 38809*KB, 39204*KB, 39601*KB, 40000*KB, 40401*KB, 40804*KB, 41209*KB, 41616*KB, 42025*KB, 42436*KB, 42849*KB, + 43264*KB, 43681*KB, 44100*KB, 44521*KB, 44944*KB, 45369*KB, 45796*KB, 46225*KB, 46656*KB, 47089*KB, 47524*KB, 47961*KB, 48400*KB, 48841*KB, 49284*KB, 49729*KB, + 50176*KB, 50625*KB, 51076*KB, 51529*KB, 51984*KB, 52441*KB, 52900*KB, 53361*KB, 53824*KB, 54289*KB, 54756*KB, 55225*KB, 55696*KB, 56169*KB, 56644*KB, 57121*KB, + 57600*KB, 58081*KB, 58564*KB, 59049*KB, 59536*KB, 60025*KB, 60516*KB, 61009*KB, 61504*KB, 62001*KB, 62500*KB, 63001*KB, 63504*KB, 64009*KB, 64516*KB, 65025*KB}; + +// Find the best table to use for a 2x4 area by testing all. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int tryalltables_3bittable2x4(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + int min_error = 3*255*255*16; + int q; + int err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + err=compressBlockWithTable2x4(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + } + } + return min_error; +} + +// Find the best table to use for a 2x4 area by testing all. +// Uses perceptual weighting. +// Uses fixed point implementation where 1000 equals 1.0 +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int tryalltables_3bittable2x4percep1000(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + unsigned int min_error = MAXERR1000; + int q; + unsigned int err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + + err=compressBlockWithTable2x4percep1000(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + + } + } + return min_error; +} + +// Find the best table to use for a 2x4 area by testing all. +// Uses perceptual weighting. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int tryalltables_3bittable2x4percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + float min_error = 3*255*255*16; + int q; + float err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + err=compressBlockWithTable2x4percep(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + } + } + return (int) min_error; +} + +// Find the best table to use for a 4x2 area by testing all. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int tryalltables_3bittable4x2(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + int min_error = 3*255*255*16; + int q; + int err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + err=compressBlockWithTable4x2(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + } + } + return min_error; +} + +// Find the best table to use for a 4x2 area by testing all. +// Uses perceptual weighting. +// Uses fixed point implementation where 1000 equals 1.0 +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int tryalltables_3bittable4x2percep1000(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + unsigned int min_error = MAXERR1000; + int q; + unsigned int err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + err=compressBlockWithTable4x2percep1000(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + } + } + return min_error; +} + +// Find the best table to use for a 4x2 area by testing all. +// Uses perceptual weighting. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int tryalltables_3bittable4x2percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + float min_error = 3*255*255*16; + int q; + float err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + err=compressBlockWithTable4x2percep(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + } + } + return (int) min_error; +} + +// The below code quantizes a float RGB value to RGB444. +// +// The format often allows a pixel to completely compensate an intensity error of the base +// color. Hence the closest RGB444 point may not be the best, and the code below uses +// this fact to find a better RGB444 color as the base color. +// +// (See the presentation http://www.jacobstrom.com/publications/PACKMAN.ppt for more info.) +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void quantize444ColorCombined(float *avg_col_in, int *enc_color, uint8 *avg_color) +{ + float dr, dg, db; + float kr, kg, kb; + float wR2, wG2, wB2; + uint8 low_color[3]; + uint8 high_color[3]; + float min_error=255*255*8*3; + float lowhightable[8]; + unsigned int best_table=0; + unsigned int best_index=0; + int q; + float kval = (float) (255.0/15.0); + + // These are the values that we want to have: + float red_average, green_average, blue_average; + + int red_4bit_low, green_4bit_low, blue_4bit_low; + int red_4bit_high, green_4bit_high, blue_4bit_high; + + // These are the values that we approximate with: + int red_low, green_low, blue_low; + int red_high, green_high, blue_high; + + red_average = avg_col_in[0]; + green_average = avg_col_in[1]; + blue_average = avg_col_in[2]; + + // Find the 5-bit reconstruction levels red_low, red_high + // so that red_average is in interval [red_low, red_high]. + // (The same with green and blue.) + + red_4bit_low = (int) (red_average/kval); + green_4bit_low = (int) (green_average/kval); + blue_4bit_low = (int) (blue_average/kval); + + red_4bit_high = CLAMP(0, red_4bit_low + 1, 15); + green_4bit_high = CLAMP(0, green_4bit_low + 1, 15); + blue_4bit_high = CLAMP(0, blue_4bit_low + 1, 15); + + red_low = (red_4bit_low << 4) | (red_4bit_low >> 0); + green_low = (green_4bit_low << 4) | (green_4bit_low >> 0); + blue_low = (blue_4bit_low << 4) | (blue_4bit_low >> 0); + + red_high = (red_4bit_high << 4) | (red_4bit_high >> 0); + green_high = (green_4bit_high << 4) | (green_4bit_high >> 0); + blue_high = (blue_4bit_high << 4) | (blue_4bit_high >> 0); + + kr = (float)red_high - (float)red_low; + kg = (float)green_high - (float)green_low; + kb = (float)blue_high - (float)blue_low; + + // Note that dr, dg, and db are all negative. + dr = red_low - red_average; + dg = green_low - green_average; + db = blue_low - blue_average; + + // Use straight (nonperceptive) weights. + wR2 = (float) 1.0; + wG2 = (float) 1.0; + wB2 = (float) 1.0; + + lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + + float min_value = lowhightable[0]; + int min_index = 0; + + for(q = 1; q<8; q++) + { + if(lowhightable[q] < min_value) + { + min_value = lowhightable[q]; + min_index = q; + } + } + + float drh = red_high-red_average; + float dgh = green_high-green_average; + float dbh = blue_high-blue_average; + + low_color[0] = red_4bit_low; + low_color[1] = green_4bit_low; + low_color[2] = blue_4bit_low; + + high_color[0] = red_4bit_high; + high_color[1] = green_4bit_high; + high_color[2] = blue_4bit_high; + + switch(min_index) + { + case 0: + // Since the step size is always 17 in RGB444 format (15*17=255), + // kr = kg = kb = 17, which means that case 0 and case 7 will + // always have equal projected error. Choose the one that is + // closer to the desired color. + if(dr*dr + dg*dg + db*db > 3*8*8) + { + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + } + else + { + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + } + break; + case 1: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 2: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 3: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 4: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 5: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 6: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + case 7: + if(dr*dr + dg*dg + db*db > 3*8*8) + { + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + } + else + { + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + } + break; + } + // Expand 5-bit encoded color to 8-bit color + avg_color[0] = (enc_color[0] << 3) | (enc_color[0] >> 2); + avg_color[1] = (enc_color[1] << 3) | (enc_color[1] >> 2); + avg_color[2] = (enc_color[2] << 3) | (enc_color[2] >> 2); +} + +// The below code quantizes a float RGB value to RGB555. +// +// The format often allows a pixel to completely compensate an intensity error of the base +// color. Hence the closest RGB555 point may not be the best, and the code below uses +// this fact to find a better RGB555 color as the base color. +// +// (See the presentation http://www.jacobstrom.com/publications/PACKMAN.ppt for more info.) +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void quantize555ColorCombined(float *avg_col_in, int *enc_color, uint8 *avg_color) +{ + float dr, dg, db; + float kr, kg, kb; + float wR2, wG2, wB2; + uint8 low_color[3]; + uint8 high_color[3]; + float min_error=255*255*8*3; + float lowhightable[8]; + unsigned int best_table=0; + unsigned int best_index=0; + int q; + float kval = (float) (255.0/31.0); + + // These are the values that we want to have: + float red_average, green_average, blue_average; + + int red_5bit_low, green_5bit_low, blue_5bit_low; + int red_5bit_high, green_5bit_high, blue_5bit_high; + + // These are the values that we approximate with: + int red_low, green_low, blue_low; + int red_high, green_high, blue_high; + + red_average = avg_col_in[0]; + green_average = avg_col_in[1]; + blue_average = avg_col_in[2]; + + // Find the 5-bit reconstruction levels red_low, red_high + // so that red_average is in interval [red_low, red_high]. + // (The same with green and blue.) + + red_5bit_low = (int) (red_average/kval); + green_5bit_low = (int) (green_average/kval); + blue_5bit_low = (int) (blue_average/kval); + + red_5bit_high = CLAMP(0, red_5bit_low + 1, 31); + green_5bit_high = CLAMP(0, green_5bit_low + 1, 31); + blue_5bit_high = CLAMP(0, blue_5bit_low + 1, 31); + + red_low = (red_5bit_low << 3) | (red_5bit_low >> 2); + green_low = (green_5bit_low << 3) | (green_5bit_low >> 2); + blue_low = (blue_5bit_low << 3) | (blue_5bit_low >> 2); + + red_high = (red_5bit_high << 3) | (red_5bit_high >> 2); + green_high = (green_5bit_high << 3) | (green_5bit_high >> 2); + blue_high = (blue_5bit_high << 3) | (blue_5bit_high >> 2); + + kr = (float)red_high - (float)red_low; + kg = (float)green_high - (float)green_low; + kb = (float)blue_high - (float)blue_low; + + // Note that dr, dg, and db are all negative. + dr = red_low - red_average; + dg = green_low - green_average; + db = blue_low - blue_average; + + // Use straight (nonperceptive) weights. + wR2 = (float) 1.0; + wG2 = (float) 1.0; + wB2 = (float) 1.0; + + lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + + float min_value = lowhightable[0]; + int min_index = 0; + + for(q = 1; q<8; q++) + { + if(lowhightable[q] < min_value) + { + min_value = lowhightable[q]; + min_index = q; + } + } + + float drh = red_high-red_average; + float dgh = green_high-green_average; + float dbh = blue_high-blue_average; + + low_color[0] = red_5bit_low; + low_color[1] = green_5bit_low; + low_color[2] = blue_5bit_low; + + high_color[0] = red_5bit_high; + high_color[1] = green_5bit_high; + high_color[2] = blue_5bit_high; + + switch(min_index) + { + case 0: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 1: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 2: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 3: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 4: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 5: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 6: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + case 7: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + } + + // Expand 5-bit encoded color to 8-bit color + avg_color[0] = (enc_color[0] << 3) | (enc_color[0] >> 2); + avg_color[1] = (enc_color[1] << 3) | (enc_color[1] >> 2); + avg_color[2] = (enc_color[2] << 3) | (enc_color[2] >> 2); + +} + +// The below code quantizes a float RGB value to RGB444. +// +// The format often allows a pixel to completely compensate an intensity error of the base +// color. Hence the closest RGB444 point may not be the best, and the code below uses +// this fact to find a better RGB444 color as the base color. +// +// (See the presentation http://www.jacobstrom.com/publications/PACKMAN.ppt for more info.) +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void quantize444ColorCombinedPerceptual(float *avg_col_in, int *enc_color, uint8 *avg_color) +{ + float dr, dg, db; + float kr, kg, kb; + float wR2, wG2, wB2; + uint8 low_color[3]; + uint8 high_color[3]; + float min_error=255*255*8*3; + float lowhightable[8]; + unsigned int best_table=0; + unsigned int best_index=0; + int q; + float kval = (float) (255.0/15.0); + + // These are the values that we want to have: + float red_average, green_average, blue_average; + + int red_4bit_low, green_4bit_low, blue_4bit_low; + int red_4bit_high, green_4bit_high, blue_4bit_high; + + // These are the values that we approximate with: + int red_low, green_low, blue_low; + int red_high, green_high, blue_high; + + red_average = avg_col_in[0]; + green_average = avg_col_in[1]; + blue_average = avg_col_in[2]; + + // Find the 5-bit reconstruction levels red_low, red_high + // so that red_average is in interval [red_low, red_high]. + // (The same with green and blue.) + + red_4bit_low = (int) (red_average/kval); + green_4bit_low = (int) (green_average/kval); + blue_4bit_low = (int) (blue_average/kval); + + red_4bit_high = CLAMP(0, red_4bit_low + 1, 15); + green_4bit_high = CLAMP(0, green_4bit_low + 1, 15); + blue_4bit_high = CLAMP(0, blue_4bit_low + 1, 15); + + red_low = (red_4bit_low << 4) | (red_4bit_low >> 0); + green_low = (green_4bit_low << 4) | (green_4bit_low >> 0); + blue_low = (blue_4bit_low << 4) | (blue_4bit_low >> 0); + + red_high = (red_4bit_high << 4) | (red_4bit_high >> 0); + green_high = (green_4bit_high << 4) | (green_4bit_high >> 0); + blue_high = (blue_4bit_high << 4) | (blue_4bit_high >> 0); + + low_color[0] = red_4bit_low; + low_color[1] = green_4bit_low; + low_color[2] = blue_4bit_low; + + high_color[0] = red_4bit_high; + high_color[1] = green_4bit_high; + high_color[2] = blue_4bit_high; + + kr = (float)red_high - (float)red_low; + kg = (float)green_high - (float)green_low; + kb = (float)blue_high- (float)blue_low; + + // Note that dr, dg, and db are all negative. + dr = red_low - red_average; + dg = green_low - green_average; + db = blue_low - blue_average; + + // Perceptual weights to use + wR2 = (float) PERCEPTUAL_WEIGHT_R_SQUARED; + wG2 = (float) PERCEPTUAL_WEIGHT_G_SQUARED; + wB2 = (float) PERCEPTUAL_WEIGHT_B_SQUARED; + + lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + + float min_value = lowhightable[0]; + int min_index = 0; + + for(q = 1; q<8; q++) + { + if(lowhightable[q] < min_value) + { + min_value = lowhightable[q]; + min_index = q; + } + } + + float drh = red_high-red_average; + float dgh = green_high-green_average; + float dbh = blue_high-blue_average; + + switch(min_index) + { + case 0: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 1: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 2: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 3: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 4: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 5: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 6: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + case 7: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + } + + // Expand encoded color to eight bits + avg_color[0] = (enc_color[0] << 4) | enc_color[0]; + avg_color[1] = (enc_color[1] << 4) | enc_color[1]; + avg_color[2] = (enc_color[2] << 4) | enc_color[2]; +} + +// The below code quantizes a float RGB value to RGB555. +// +// The format often allows a pixel to completely compensate an intensity error of the base +// color. Hence the closest RGB555 point may not be the best, and the code below uses +// this fact to find a better RGB555 color as the base color. +// +// (See the presentation http://www.jacobstrom.com/publications/PACKMAN.ppt for more info.) +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void quantize555ColorCombinedPerceptual(float *avg_col_in, int *enc_color, uint8 *avg_color) +{ + float dr, dg, db; + float kr, kg, kb; + float wR2, wG2, wB2; + uint8 low_color[3]; + uint8 high_color[3]; + float min_error=255*255*8*3; + float lowhightable[8]; + unsigned int best_table=0; + unsigned int best_index=0; + int q; + float kval = (float) (255.0/31.0); + + // These are the values that we want to have: + float red_average, green_average, blue_average; + + int red_5bit_low, green_5bit_low, blue_5bit_low; + int red_5bit_high, green_5bit_high, blue_5bit_high; + + // These are the values that we approximate with: + int red_low, green_low, blue_low; + int red_high, green_high, blue_high; + + red_average = avg_col_in[0]; + green_average = avg_col_in[1]; + blue_average = avg_col_in[2]; + + // Find the 5-bit reconstruction levels red_low, red_high + // so that red_average is in interval [red_low, red_high]. + // (The same with green and blue.) + + red_5bit_low = (int) (red_average/kval); + green_5bit_low = (int) (green_average/kval); + blue_5bit_low = (int) (blue_average/kval); + + red_5bit_high = CLAMP(0, red_5bit_low + 1, 31); + green_5bit_high = CLAMP(0, green_5bit_low + 1, 31); + blue_5bit_high = CLAMP(0, blue_5bit_low + 1, 31); + + red_low = (red_5bit_low << 3) | (red_5bit_low >> 2); + green_low = (green_5bit_low << 3) | (green_5bit_low >> 2); + blue_low = (blue_5bit_low << 3) | (blue_5bit_low >> 2); + + red_high = (red_5bit_high << 3) | (red_5bit_high >> 2); + green_high = (green_5bit_high << 3) | (green_5bit_high >> 2); + blue_high = (blue_5bit_high << 3) | (blue_5bit_high >> 2); + + low_color[0] = red_5bit_low; + low_color[1] = green_5bit_low; + low_color[2] = blue_5bit_low; + + high_color[0] = red_5bit_high; + high_color[1] = green_5bit_high; + high_color[2] = blue_5bit_high; + + kr = (float)red_high - (float)red_low; + kg = (float)green_high - (float)green_low; + kb = (float)blue_high - (float)blue_low; + + // Note that dr, dg, and db are all negative. + dr = red_low - red_average; + dg = green_low - green_average; + db = blue_low - blue_average; + + // Perceptual weights to use + wR2 = (float) PERCEPTUAL_WEIGHT_R_SQUARED; + wG2 = (float) PERCEPTUAL_WEIGHT_G_SQUARED; + wB2 = (float) PERCEPTUAL_WEIGHT_B_SQUARED; + + lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + + float min_value = lowhightable[0]; + int min_index = 0; + + for(q = 1; q<8; q++) + { + if(lowhightable[q] < min_value) + { + min_value = lowhightable[q]; + min_index = q; + } + } + + float drh = red_high-red_average; + float dgh = green_high-green_average; + float dbh = blue_high-blue_average; + + switch(min_index) + { + case 0: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 1: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 2: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 3: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 4: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 5: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 6: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + case 7: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + } + + // Expand 5-bit encoded color to 8-bit color + avg_color[0] = (enc_color[0] << 3) | (enc_color[0] >> 2); + avg_color[1] = (enc_color[1] << 3) | (enc_color[1] >> 2); + avg_color[2] = (enc_color[2] << 3) | (enc_color[2] >> 2); +} + +// Compresses the block using only the individual mode in ETC1/ETC2 using the average color as the base color. +// Uses a perceptual error metric. +// Uses fixed point arithmetics where 1000 equals 1.0 +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockOnlyIndividualAveragePerceptual1000(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, int *best_enc_color1, int*best_enc_color2, int &best_flip, unsigned int &best_err_upper, unsigned int &best_err_lower, unsigned int &best_err_left, unsigned int &best_err_right, int *best_color_upper, int *best_color_lower, int *best_color_left, int *best_color_right) +{ + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3]; + unsigned int best_table_indices1=0, best_table_indices2=0; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + unsigned int norm_err=0; + unsigned int flip_err=0; + unsigned int best_err; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + enc_color1[0] = int( JAS_ROUND(15.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(15.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(15.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(15.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(15.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(15.0*avg_color_float2[2]/255.0) ); + + diffbit = 0; + + avg_color_quant1[0] = enc_color1[0] << 4 | (enc_color1[0] ); + avg_color_quant1[1] = enc_color1[1] << 4 | (enc_color1[1] ); + avg_color_quant1[2] = enc_color1[2] << 4 | (enc_color1[2] ); + avg_color_quant2[0] = enc_color2[0] << 4 | (enc_color2[0] ); + avg_color_quant2[1] = enc_color2[1] << 4 | (enc_color2[1] ); + avg_color_quant2[2] = enc_color2[2] << 4 | (enc_color2[2] ); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + best_enc_color1[0] = enc_color1[0]; + best_enc_color1[1] = enc_color1[1]; + best_enc_color1[2] = enc_color1[2]; + best_enc_color2[0] = enc_color2[0]; + best_enc_color2[1] = enc_color2[1]; + best_enc_color2[2] = enc_color2[2]; + + best_color_left[0] = enc_color1[0]; + best_color_left[1] = enc_color1[1]; + best_color_left[2] = enc_color1[2]; + best_color_right[0] = enc_color2[0]; + best_color_right[1] = enc_color2[1]; + best_color_right[2] = enc_color2[2]; + + norm_err = 0; + + // left part of block + best_err_left = tryalltables_3bittable2x4percep1000(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + norm_err = best_err_left; + + // right part of block + best_err_right = tryalltables_3bittable2x4percep1000(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + norm_err += best_err_right; + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(15.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(15.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(15.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(15.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(15.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(15.0*avg_color_float2[2]/255.0) ); + + best_color_upper[0] = enc_color1[0]; + best_color_upper[1] = enc_color1[1]; + best_color_upper[2] = enc_color1[2]; + best_color_lower[0] = enc_color2[0]; + best_color_lower[1] = enc_color2[1]; + best_color_lower[2] = enc_color2[2]; + + diffbit = 0; + + avg_color_quant1[0] = enc_color1[0] << 4 | (enc_color1[0] ); + avg_color_quant1[1] = enc_color1[1] << 4 | (enc_color1[1] ); + avg_color_quant1[2] = enc_color1[2] << 4 | (enc_color1[2] ); + avg_color_quant2[0] = enc_color2[0] << 4 | (enc_color2[0] ); + avg_color_quant2[1] = enc_color2[1] << 4 | (enc_color2[1] ); + avg_color_quant2[2] = enc_color2[2] << 4 | (enc_color2[2] ); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 49); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + // upper part of block + best_err_upper = tryalltables_3bittable4x2percep1000(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + flip_err = best_err_upper; + // lower part of block + best_err_lower = tryalltables_3bittable4x2percep1000(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + flip_err += best_err_lower; + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + // Now lets see which is the best table to use. Only 8 tables are possible. + + if(norm_err <= flip_err) + { + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + best_err = norm_err; + best_flip = 0; + } + else + { + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + best_err = flip_err; + best_enc_color1[0] = enc_color1[0]; + best_enc_color1[1] = enc_color1[1]; + best_enc_color1[2] = enc_color1[2]; + best_enc_color2[0] = enc_color2[0]; + best_enc_color2[1] = enc_color2[1]; + best_enc_color2[2] = enc_color2[2]; + best_flip = 1; + } + return best_err; +} + +// Compresses the block using only the individual mode in ETC1/ETC2 using the average color as the base color. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int compressBlockOnlyIndividualAverage(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, int *best_enc_color1, int*best_enc_color2, int &best_flip, unsigned int &best_err_upper, unsigned int &best_err_lower, unsigned int &best_err_left, unsigned int &best_err_right, int *best_color_upper, int *best_color_lower, int *best_color_left, int *best_color_right) +{ + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3]; + int min_error=255*255*8*3; + unsigned int best_table_indices1=0, best_table_indices2=0; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + int best_err; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + enc_color1[0] = int( JAS_ROUND(15.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(15.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(15.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(15.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(15.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(15.0*avg_color_float2[2]/255.0) ); + + diffbit = 0; + + avg_color_quant1[0] = enc_color1[0] << 4 | (enc_color1[0] ); + avg_color_quant1[1] = enc_color1[1] << 4 | (enc_color1[1] ); + avg_color_quant1[2] = enc_color1[2] << 4 | (enc_color1[2] ); + avg_color_quant2[0] = enc_color2[0] << 4 | (enc_color2[0] ); + avg_color_quant2[1] = enc_color2[1] << 4 | (enc_color2[1] ); + avg_color_quant2[2] = enc_color2[2] << 4 | (enc_color2[2] ); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + best_enc_color1[0] = enc_color1[0]; + best_enc_color1[1] = enc_color1[1]; + best_enc_color1[2] = enc_color1[2]; + best_enc_color2[0] = enc_color2[0]; + best_enc_color2[1] = enc_color2[1]; + best_enc_color2[2] = enc_color2[2]; + best_color_left[0] = enc_color1[0]; + best_color_left[1] = enc_color1[1]; + best_color_left[2] = enc_color1[2]; + best_color_right[0] = enc_color2[0]; + best_color_right[1] = enc_color2[1]; + best_color_right[2] = enc_color2[2]; + + norm_err = 0; + + // left part of block + best_err_left = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + norm_err = best_err_left; + + // right part of block + best_err_right = tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + norm_err += best_err_right; + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(15.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(15.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(15.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(15.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(15.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(15.0*avg_color_float2[2]/255.0) ); + + best_color_upper[0] = enc_color1[0]; + best_color_upper[1] = enc_color1[1]; + best_color_upper[2] = enc_color1[2]; + best_color_lower[0] = enc_color2[0]; + best_color_lower[1] = enc_color2[1]; + best_color_lower[2] = enc_color2[2]; + + diffbit = 0; + + avg_color_quant1[0] = enc_color1[0] << 4 | (enc_color1[0] ); + avg_color_quant1[1] = enc_color1[1] << 4 | (enc_color1[1] ); + avg_color_quant1[2] = enc_color1[2] << 4 | (enc_color1[2] ); + avg_color_quant2[0] = enc_color2[0] << 4 | (enc_color2[0] ); + avg_color_quant2[1] = enc_color2[1] << 4 | (enc_color2[1] ); + avg_color_quant2[2] = enc_color2[2] << 4 | (enc_color2[2] ); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 49); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + // upper part of block + best_err_upper = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + flip_err = best_err_upper; + // lower part of block + best_err_lower = tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + flip_err += best_err_lower; + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + // Now lets see which is the best table to use. Only 8 tables are possible. + + if(norm_err <= flip_err) + { + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + best_err = norm_err; + best_flip = 0; + } + else + { + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + best_err = flip_err; + best_enc_color1[0] = enc_color1[0]; + best_enc_color1[1] = enc_color1[1]; + best_enc_color1[2] = enc_color1[2]; + best_enc_color2[0] = enc_color2[0]; + best_enc_color2[1] = enc_color2[1]; + best_enc_color2[2] = enc_color2[2]; + best_flip = 1; + } + return best_err; +} + +// Compresses the block using either the individual or differential mode in ETC1/ETC2 +// Uses the average color as the base color in each half-block. +// Tries both flipped and unflipped. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockDiffFlipAverage(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + int min_error=255*255*8*3; + unsigned int best_table_indices1=0, best_table_indices2=0; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + + // First try normal blocks 2x4: + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + float eps; + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + eps = (float) 0.0001; + + enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); + enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); + enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); + enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); + enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); + enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // Pack bits into the first word. + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // left part of block + norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + } + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + eps = (float) 0.0001; + + enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); + enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); + enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); + enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); + enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); + enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + + // Now lets see which is the best table to use. Only 8 tables are possible. + + if(norm_err <= flip_err) + { + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + } + else + { + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + } +} + +// Compresses the block using only the differential mode in ETC1/ETC2 +// Uses the average color as the base color in each half-block. +// If average colors are too different, use the average color of the entire block in both half-blocks. +// Tries both flipped and unflipped. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int compressBlockOnlyDiffFlipAverage(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, int *best_enc_color1, int*best_enc_color2, int &best_flip) +{ + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + int min_error=255*255*8*3; + unsigned int best_table_indices1=0, best_table_indices2=0; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + int best_err; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( !((diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3)) ) + { + // The colors are too different. Use the same color in both blocks. + enc_color1[0] = int( JAS_ROUND(31.0*((avg_color_float1[0]+avg_color_float2[0])/2.0)/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*((avg_color_float1[1]+avg_color_float2[1])/2.0)/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*((avg_color_float1[2]+avg_color_float2[2])/2.0)/255.0) ); + enc_color2[0] = enc_color1[0]; + enc_color2[1] = enc_color1[1]; + enc_color2[2] = enc_color1[2]; + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + } + + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + best_enc_color1[0] = enc_color1[0]; + best_enc_color1[1] = enc_color1[1]; + best_enc_color1[2] = enc_color1[2]; + best_enc_color2[0] = enc_color2[0]; + best_enc_color2[1] = enc_color2[1]; + best_enc_color2[2] = enc_color2[2]; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( !((diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3)) ) + { + // The colors are too different. Use the same color in both blocks. + enc_color1[0] = int( JAS_ROUND(31.0*((avg_color_float1[0]+avg_color_float2[0])/2.0)/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*((avg_color_float1[1]+avg_color_float2[1])/2.0)/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*((avg_color_float1[2]+avg_color_float2[2])/2.0)/255.0) ); + enc_color2[0] = enc_color1[0]; + enc_color2[1] = enc_color1[1]; + enc_color2[2] = enc_color1[2]; + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + } + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + // upper part of block + flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + // Now lets see which is the best table to use. Only 8 tables are possible. + + if(norm_err <= flip_err) + { + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + best_err = norm_err; + best_flip = 0; + } + else + { + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + best_err = flip_err; + best_enc_color1[0] = enc_color1[0]; + best_enc_color1[1] = enc_color1[1]; + best_enc_color1[2] = enc_color1[2]; + best_enc_color2[0] = enc_color2[0]; + best_enc_color2[1] = enc_color2[1]; + best_enc_color2[2] = enc_color2[2]; + best_flip = 1; + } + return best_err; +} + +// Compresses the block using only the differential mode in ETC1/ETC2 +// Uses the average color as the base color in each half-block. +// If average colors are too different, use the average color of the entire block in both half-blocks. +// Tries both flipped and unflipped. +// Uses fixed point arithmetics where 1000 represents 1.0. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockOnlyDiffFlipAveragePerceptual1000(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + unsigned int min_error=MAXERR1000; + unsigned int best_table_indices1=0, best_table_indices2=0; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( !((diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3)) ) + { + enc_color1[0] = (enc_color1[0] + enc_color2[0]) >> 1; + enc_color1[1] = (enc_color1[1] + enc_color2[1]) >> 1; + enc_color1[2] = (enc_color1[2] + enc_color2[2]) >> 1; + + enc_color2[0] = enc_color1[0]; + enc_color2[1] = enc_color1[1]; + enc_color2[2] = enc_color1[2]; + + } + + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4percep1000(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4percep1000(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + } + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( !((diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3)) ) + { + enc_color1[0] = (enc_color1[0] + enc_color2[0]) >> 1; + enc_color1[1] = (enc_color1[1] + enc_color2[1]) >> 1; + enc_color1[2] = (enc_color1[2] + enc_color2[2]) >> 1; + + enc_color2[0] = enc_color1[0]; + enc_color2[1] = enc_color1[1]; + enc_color2[2] = enc_color1[2]; + } + + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2percep1000(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2percep1000(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + unsigned int best_err; + + if(norm_err <= flip_err) + { + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + best_err = norm_err; + } + else + { + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + best_err = flip_err; + } + return best_err; +} + +// Compresses the block using both the individual and the differential mode in ETC1/ETC2 +// Uses the average color as the base color in each half-block. +// Uses a perceptual error metric. +// Tries both flipped and unflipped. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double compressBlockDiffFlipAveragePerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + int min_error=255*255*8*3; + unsigned int best_table_indices1=0, best_table_indices2=0; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + float eps; + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + eps = (float) 0.0001; + + enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); + enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); + enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); + enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); + enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); + enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // Pack bits into the first word. + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // left part of block + norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + } + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + eps = (float) 0.0001; + + enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); + enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); + enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); + enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); + enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); + enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + + // Now lets see which is the best table to use. Only 8 tables are possible. + + double best_err; + + if(norm_err <= flip_err) + { + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + best_err = norm_err; + } + else + { + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + best_err = flip_err; + } + return best_err; +} + +// This is our structure for matrix data +struct dMatrix +{ + int width; // The number of coloumns in the matrix + int height; // The number of rows in the matrix + double *data; // The matrix data in row order +}; + +// Multiplies two matrices +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +dMatrix *multiplyMatrices( dMatrix *Amat, dMatrix *Bmat) +{ + int xx,yy, q; + dMatrix *resmatrix; + + if(Amat->width != Bmat->height) + { + printf("Cannot multiply matrices -- dimensions do not agree.\n"); + exit(1); + } + + // Allocate space for result + resmatrix = (dMatrix*) malloc(sizeof(dMatrix)); + resmatrix->width = Bmat->width; + resmatrix->height = Amat->height; + resmatrix->data = (double*) malloc(sizeof(double)*(resmatrix->width)*(resmatrix->height)); + + for(yy = 0; yyheight; yy++) + for(xx = 0; xxwidth; xx++) + for(q=0, resmatrix->data[yy*resmatrix->width+xx] = 0.0; qwidth; q++) + resmatrix->data[yy*resmatrix->width+xx] += Amat->data[yy*Amat->width + q] * Bmat->data[q*Bmat->width+xx]; + + return(resmatrix); + +} + +// Transposes a matrix +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void transposeMatrix( dMatrix *mat) +{ + int xx, yy, zz; + double *temp; + int newwidth, newheight; + + temp = (double*) malloc (sizeof(double)*(mat->width)*(mat->height)); + + for(zz = 0; zz<((mat->width)*(mat->height)); zz++) + temp[zz] = mat->data[zz]; + + newwidth = mat->height; + newheight= mat->width; + + for(yy = 0; yydata[yy*newwidth+xx] = temp[xx*(mat->width)+yy]; + + mat->height = newheight; + mat->width = newwidth; + free(temp); +} + +// In the planar mode in ETC2, the block can be partitioned as follows: +// +// O A A A H +// B D1 D3 C3 +// B D2 C2 D5 +// B C1 D4 D6 +// V +// Here A-pixels, B-pixels and C-pixels only depend on two values. For instance, B-pixels only depend on O and V. +// This can be used to quickly rule out combinations of colors. +// Here we calculate the minimum error for the block if we know the red component for O and V. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcBBBred(uint8 *block, int colorO, int colorV) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error = 0; + + // Now first column: B B B + /* unroll loop for( yy=0; (yy<4) && (error <= best_error_sofar); yy++)*/ + { + error = error + square_table[(block[4*4 + 0] - clamp_table[ ((((colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*2 + 0] - clamp_table[ (((((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*3 + 0] - clamp_table[ (((3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + + return error; +} + +// Calculating the minimum error for the block if we know the red component for H and V. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcCCCred(uint8 *block, int colorH, int colorV) +{ + colorH = (colorH << 2) | (colorH >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error=0; + + error = error + square_table[(block[4*4*3 + 4 + 0] - clamp_table[ (((colorH + 3*colorV)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*2 + 4*2 + 0] - clamp_table[ (((2*colorH + 2*colorV)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4 + 4*3 + 0] - clamp_table[ (((3*colorH + colorV)+2)>>2) + 255])+255]; + + return error; +} + +// Calculating the minimum error for the block if we know the red component for O and H. +// Uses perceptual error metric. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcLowestPossibleRedOHperceptual(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorH = (colorH << 2) | (colorH >> 4); + + unsigned int error; + + error = square_table_percep_red[(block[0] - colorO) + 255]; + error = error + square_table_percep_red[(block[4] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table_percep_red[(block[4*2] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_red[(block[4*3] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcLowestPossibleRedOH(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorH = (colorH << 2) | (colorH >> 4); + + unsigned int error; + + error = square_table[(block[0] - colorO) + 255]; + error = error + square_table[(block[4] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table[(block[4*2] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*3] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H and V. +// Uses perceptual error metric. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcErrorPlanarOnlyRedPerceptual(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorH = (colorH << 2) | (colorH >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error; + + // The block can be partitioned into: O A A A + // B D1 D3 C3 + // B D2 C2 D5 + // B C1 D4 D6 + int xpart_times_4; + + // The first part: O A A A. It equals lowest_possible_error previously calculated. + // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. + error = lowest_possible_error + BBBvalue + CCCvalue; + + // The remaining pixels to cover are D1 through D6. + if(error <= best_error_sofar) + { + // Second column: D1 D2 but not C1 + xpart_times_4 = (colorH-colorO); + error = error + square_table_percep_red[(block[4*4 + 4 + 0] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_red[(block[4*4*2 + 4 + 0] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + // Third column: D3 notC2 D4 + xpart_times_4 = (colorH-colorO) << 1; + error = error + square_table_percep_red[(block[4*4 + 4*2 + 0] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table_percep_red[(block[4*4*3 + 4*2 + 0] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + // Forth column: notC3 D5 D6 + xpart_times_4 = 3*(colorH-colorO); + error = error + square_table_percep_red[(block[4*4*2 + 4*3 + 0] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_red[(block[4*4*3 + 4*3 + 0] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + } + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H and V. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcErrorPlanarOnlyRed(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorH = (colorH << 2) | (colorH >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error; + + // The block can be partitioned into: O A A A + // B D1 D3 C3 + // B D2 C2 D5 + // B C1 D4 D6 + int xpart_times_4; + + // The first part: O A A A. It equals lowest_possible_error previously calculated. + // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. + error = lowest_possible_error + BBBvalue + CCCvalue; + + // The remaining pixels to cover are D1 through D6. + if(error <= best_error_sofar) + { + // Second column: D1 D2 but not C1 + xpart_times_4 = (colorH-colorO); + error = error + square_table[(block[4*4 + 4 + 0] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*2 + 4 + 0] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + // Third column: D3 notC2 D4 + xpart_times_4 = (colorH-colorO) << 1; + error = error + square_table[(block[4*4 + 4*2 + 0] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table[(block[4*4*3 + 4*2 + 0] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + // Forth column: notC3 D5 D6 + xpart_times_4 = 3*(colorH-colorO); + error = error + square_table[(block[4*4*2 + 4*3 + 0] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*3 + 4*3 + 0] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + } + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H. +// Uses perceptual error metrics. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcLowestPossibleGreenOHperceptual(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) +{ + colorO = (colorO << 1) | (colorO >> 6); + colorH = (colorH << 1) | (colorH >> 6); + + unsigned int error; + + error = square_table_percep_green[(block[1] - colorO) + 255]; + error = error + square_table_percep_green[(block[4 + 1] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table_percep_green[(block[4*2 + 1] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_green[(block[4*3 + 1] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the red component for O and H. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcLowestPossibleGreenOH(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) +{ + colorO = (colorO << 1) | (colorO >> 6); + colorH = (colorH << 1) | (colorH >> 6); + + unsigned int error; + + error = square_table[(block[1] - colorO) + 255]; + error = error + square_table[(block[4 + 1] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table[(block[4*2 + 1] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*3 + 1] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the green component for O and V. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcBBBgreen(uint8 *block, int colorO, int colorV) +{ + colorO = (colorO << 1) | (colorO >> 6); + colorV = (colorV << 1) | (colorV >> 6); + + unsigned int error = 0; + + // Now first column: B B B + /* unroll loop for( yy=0; (yy<4) && (error <= best_error_sofar); yy++)*/ + { + error = error + square_table[(block[4*4 + 1] - clamp_table[ ((((colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*2 + 1] - clamp_table[ (((((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*3 + 1] - clamp_table[ (((3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + + return error; + +} + +// Calculating the minimum error for the block (in planar mode) if we know the green component for H and V. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcCCCgreen(uint8 *block, int colorH, int colorV) +{ + colorH = (colorH << 1) | (colorH >> 6); + colorV = (colorV << 1) | (colorV >> 6); + + unsigned int error=0; + + error = error + square_table[(block[4*4*3 + 4 + 1] - clamp_table[ (((colorH + 3*colorV)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*2 + 4*2 + 1] - clamp_table[ (((2*colorH + 2*colorV)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4 + 4*3 + 1] - clamp_table[ (((3*colorH + colorV)+2)>>2) + 255])+255]; + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the green component for H V and O. +// Uses perceptual error metric. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcErrorPlanarOnlyGreenPerceptual(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) +{ + colorO = (colorO << 1) | (colorO >> 6); + colorH = (colorH << 1) | (colorH >> 6); + colorV = (colorV << 1) | (colorV >> 6); + + unsigned int error; + + // The block can be partitioned into: O A A A + // B D1 D3 C3 + // B D2 C2 D5 + // B C1 D4 D6 + + int xpart_times_4; + + // The first part: O A A A. It equals lowest_possible_error previously calculated. + // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. + error = lowest_possible_error + BBBvalue + CCCvalue; + + // The remaining pixels to cover are D1 through D6. + if(error <= best_error_sofar) + { + // Second column: D1 D2 but not C1 + xpart_times_4 = (colorH-colorO); + error = error + square_table_percep_green[(block[4*4 + 4 + 1] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_green[(block[4*4*2 + 4 + 1] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + // Third column: D3 notC2 D4 + xpart_times_4 = (colorH-colorO) << 1; + error = error + square_table_percep_green[(block[4*4 + 4*2 + 1] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table_percep_green[(block[4*4*3 + 4*2 + 1] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + // Forth column: notC3 D5 D6 + xpart_times_4 = 3*(colorH-colorO); + error = error + square_table_percep_green[(block[4*4*2 + 4*3 + 1] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_green[(block[4*4*3 + 4*3 + 1] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + } + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the green component for H V and O. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcErrorPlanarOnlyGreen(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) +{ + colorO = (colorO << 1) | (colorO >> 6); + colorH = (colorH << 1) | (colorH >> 6); + colorV = (colorV << 1) | (colorV >> 6); + + unsigned int error; + + // The block can be partitioned into: O A A A + // B D1 D3 C3 + // B D2 C2 D5 + // B C1 D4 D6 + int xpart_times_4; + + // The first part: O A A A. It equals lowest_possible_error previously calculated. + // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. + error = lowest_possible_error + BBBvalue + CCCvalue; + + // The remaining pixels to cover are D1 through D6. + if(error <= best_error_sofar) + { + // Second column: D1 D2 but not C1 + xpart_times_4 = (colorH-colorO); + error = error + square_table[(block[4*4 + 4 + 1] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*2 + 4 + 1] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + // Third column: D3 notC2 D4 + xpart_times_4 = (colorH-colorO) << 1; + error = error + square_table[(block[4*4 + 4*2 + 1] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table[(block[4*4*3 + 4*2 + 1] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + // Forth column: notC3 D5 D6 + xpart_times_4 = 3*(colorH-colorO); + error = error + square_table[(block[4*4*2 + 4*3 + 1] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*3 + 4*3 + 1] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + } + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and V. +// Uses perceptual error metric. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcBBBbluePerceptual(uint8 *block, int colorO, int colorV) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error = 0; + + // Now first column: B B B + /* unroll loop for( yy=0; (yy<4) && (error <= best_error_sofar); yy++)*/ + { + error = error + square_table_percep_blue[(block[4*4 + 2] - clamp_table[ ((((colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_blue[(block[4*4*2 + 2] - clamp_table[ (((((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_blue[(block[4*4*3 + 2] - clamp_table[ (((3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and V. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcBBBblue(uint8 *block, int colorO, int colorV) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error = 0; + + // Now first column: B B B + /* unroll loop for( yy=0; (yy<4) && (error <= best_error_sofar); yy++)*/ + { + error = error + square_table[(block[4*4 + 2] - clamp_table[ ((((colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*2 + 2] - clamp_table[ (((((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*3 + 2] - clamp_table[ (((3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the blue component for H and V. +// Uses perceptual error metric. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcCCCbluePerceptual(uint8 *block, int colorH, int colorV) +{ + colorH = (colorH << 2) | (colorH >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error=0; + + error = error + square_table_percep_blue[(block[4*4*3 + 4 + 2] - clamp_table[ (((colorH + 3*colorV)+2)>>2) + 255])+255]; + error = error + square_table_percep_blue[(block[4*4*2 + 4*2 + 2] - clamp_table[ (((2*colorH + 2*colorV)+2)>>2) + 255])+255]; + error = error + square_table_percep_blue[(block[4*4 + 4*3 + 2] - clamp_table[ (((3*colorH + colorV)+2)>>2) + 255])+255]; + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and V. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcCCCblue(uint8 *block, int colorH, int colorV) +{ + colorH = (colorH << 2) | (colorH >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error=0; + + error = error + square_table[(block[4*4*3 + 4 + 2] - clamp_table[ (((colorH + 3*colorV)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*2 + 4*2 + 2] - clamp_table[ (((2*colorH + 2*colorV)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4 + 4*3 + 2] - clamp_table[ (((3*colorH + colorV)+2)>>2) + 255])+255]; + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and H. +// Uses perceptual error metric. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcLowestPossibleBlueOHperceptual(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorH = (colorH << 2) | (colorH >> 4); + + unsigned int error; + + error = square_table_percep_blue[(block[2] - colorO) + 255]; + error = error + square_table_percep_blue[(block[4+2] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table_percep_blue[(block[4*2+2] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_blue[(block[4*3+2] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the blue component for O and H. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcLowestPossibleBlueOH(uint8 *block, int colorO, int colorH, unsigned int best_error_sofar) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorH = (colorH << 2) | (colorH >> 4); + + unsigned int error; + + error = square_table[(block[2] - colorO) + 255]; + error = error + square_table[(block[4+2] - clamp_table[ ((( (colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table[(block[4*2+2] - clamp_table[ ((( ((colorH-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*3+2] - clamp_table[ ((( 3*(colorH-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the blue component for O, V and H. +// Uses perceptual error metric. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcErrorPlanarOnlyBluePerceptual(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorH = (colorH << 2) | (colorH >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error; + + // The block can be partitioned into: O A A A + // B D1 D3 C3 + // B D2 C2 D5 + // B C1 D4 D6 + int xpart_times_4; + + // The first part: O A A A. It equals lowest_possible_error previously calculated. + // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. + error = lowest_possible_error + BBBvalue + CCCvalue; + + // The remaining pixels to cover are D1 through D6. + if(error <= best_error_sofar) + { + // Second column: D1 D2 but not C1 + xpart_times_4 = (colorH-colorO); + error = error + square_table_percep_blue[(block[4*4 + 4 + 2] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_blue[(block[4*4*2 + 4 + 2] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + // Third column: D3 notC2 D4 + xpart_times_4 = (colorH-colorO) << 1; + error = error + square_table_percep_blue[(block[4*4 + 4*2 + 2] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table_percep_blue[(block[4*4*3 + 4*2 + 2] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + // Forth column: notC3 D5 D6 + xpart_times_4 = 3*(colorH-colorO); + error = error + square_table_percep_blue[(block[4*4*2 + 4*3 + 2] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table_percep_blue[(block[4*4*3 + 4*3 + 2] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + } + + return error; +} + +// Calculating the minimum error for the block (in planar mode) if we know the blue component for O, V and H. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calcErrorPlanarOnlyBlue(uint8 *block, int colorO, int colorH, int colorV, unsigned int lowest_possible_error, unsigned int BBBvalue, unsigned int CCCvalue, unsigned int best_error_sofar) +{ + colorO = (colorO << 2) | (colorO >> 4); + colorH = (colorH << 2) | (colorH >> 4); + colorV = (colorV << 2) | (colorV >> 4); + + unsigned int error; + + // The block can be partitioned into: O A A A + // B D1 D3 C3 + // B D2 C2 D5 + // B C1 D4 D6 + int xpart_times_4; + + // The first part: O A A A. It equals lowest_possible_error previously calculated. + // lowest_possible_error is OAAA, BBBvalue is BBB and CCCvalue is C1C2C3. + error = lowest_possible_error + BBBvalue + CCCvalue; + + // The remaining pixels to cover are D1 through D6. + if(error <= best_error_sofar) + { + // Second column: D1 D2 but not C1 + xpart_times_4 = (colorH-colorO); + error = error + square_table[(block[4*4 + 4 + 2] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*2 + 4 + 2] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + // Third column: D3 notC2 D4 + xpart_times_4 = (colorH-colorO) << 1; + error = error + square_table[(block[4*4 + 4*2 + 2] - clamp_table[ (((xpart_times_4 + (colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + if(error <= best_error_sofar) + { + error = error + square_table[(block[4*4*3 + 4*2 + 2] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + // Forth column: notC3 D5 D6 + xpart_times_4 = 3*(colorH-colorO); + error = error + square_table[(block[4*4*2 + 4*3 + 2] - clamp_table[ (((xpart_times_4 + ((colorV-colorO)<<1) + 4*colorO)+2)>>2) + 255])+255]; + error = error + square_table[(block[4*4*3 + 4*3 + 2] - clamp_table[ (((xpart_times_4 + 3*(colorV-colorO) + 4*colorO)+2)>>2) + 255])+255]; + } + } + + return error; +} + + + +// This function uses least squares in order to determine the best values of the plane. +// This is close to optimal, but not quite, due to nonlinearities in the expantion from 6 and 7 bits to 8, and +// in the clamping to a number between 0 and the maximum. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockPlanar57(uint8 *img, int width,int height,int startx,int starty, unsigned int &compressed57_1, unsigned int &compressed57_2) +{ + // Use least squares to find the solution with the smallest error. + // That is, find the vector x so that |Ax-b|^2 is minimized, where + // x = [Ro Rr Rv]'; + // A = [1 3/4 2/4 1/4 3/4 2/4 1/4 0 2/4 1/4 0 -1/4 1/4 0 -1/4 -2/4 ; + // 0 1/4 2/4 3/4 0 1/4 2/4 3/4 0 1/4 2/4 3/4 0 1/4 2/4 3/4 ; + // 0 0 0 0 1/4 1/4 1/4 1/4 2/4 2/4 2/4 2/4; 3/4 3/4 3/4 3/4]'; + // b = [r11 r12 r13 r14 r21 r22 r23 r24 r31 r32 r33 r34 r41 r42 r43 r44]; + // + // That is, find solution x = inv(A' * A) * A' * b + // = C * A' * b; + // C is always the same, so we have calculated it off-line here. + // = C * D + int xx,yy, cc; + double coeffsA[48]= { 1.00, 0.00, 0.00, + 0.75, 0.25, 0.00, + 0.50, 0.50, 0.00, + 0.25, 0.75, 0.00, + 0.75, 0.00, 0.25, + 0.50, 0.25, 0.25, + 0.25, 0.50, 0.25, + 0.00, 0.75, 0.25, + 0.50, 0.00, 0.50, + 0.25, 0.25, 0.50, + 0.00, 0.50, 0.50, + -0.25, 0.75, 0.50, + 0.25, 0.00, 0.75, + 0.00, 0.25, 0.75, + -0.25, 0.50, 0.75, + -0.50, 0.75, 0.75}; + + double coeffsC[9] = {0.2875, -0.0125, -0.0125, -0.0125, 0.4875, -0.3125, -0.0125, -0.3125, 0.4875}; + double colorO[3], colorH[3], colorV[3]; + uint8 colorO8[3], colorH8[3], colorV8[3]; + + dMatrix *D_matrix; + dMatrix *x_vector; + + dMatrix A_matrix; A_matrix.width = 3; A_matrix.height = 16; + A_matrix.data = coeffsA; + dMatrix C_matrix; C_matrix.width = 3; C_matrix.height = 3; + C_matrix.data = coeffsC; + dMatrix b_vector; b_vector.width = 1; b_vector.height = 16; + b_vector.data = (double*) malloc(sizeof(double)*b_vector.width*b_vector.height); + transposeMatrix(&A_matrix); + + // Red component + + // Load color data into vector b: + for(cc = 0, yy = 0; yy<4; yy++) + for(xx = 0; xx<4; xx++) + b_vector.data[cc++] = img[3*width*(starty+yy) + 3*(startx+xx) + 0]; + + D_matrix = multiplyMatrices(&A_matrix, &b_vector); + x_vector = multiplyMatrices(&C_matrix, D_matrix); + + colorO[0] = CLAMP(0.0, x_vector->data[0], 255.0); + colorH[0] = CLAMP(0.0, x_vector->data[1], 255.0); + colorV[0] = CLAMP(0.0, x_vector->data[2], 255.0); + + free(D_matrix->data); free(D_matrix); + free(x_vector->data); free(x_vector); + + // Green component + + // Load color data into vector b: + for(cc = 0, yy = 0; yy<4; yy++) + for(xx = 0; xx<4; xx++) + b_vector.data[cc++] = img[3*width*(starty+yy) + 3*(startx+xx) + 1]; + + D_matrix = multiplyMatrices(&A_matrix, &b_vector); + x_vector = multiplyMatrices(&C_matrix, D_matrix); + + colorO[1] = CLAMP(0.0, x_vector->data[0], 255.0); + colorH[1] = CLAMP(0.0, x_vector->data[1], 255.0); + colorV[1] = CLAMP(0.0, x_vector->data[2], 255.0); + + free(D_matrix->data); free(D_matrix); + free(x_vector->data); free(x_vector); + + // Blue component + + // Load color data into vector b: + for(cc = 0, yy = 0; yy<4; yy++) + for(xx = 0; xx<4; xx++) + b_vector.data[cc++] = img[3*width*(starty+yy) + 3*(startx+xx) + 2]; + + D_matrix = multiplyMatrices(&A_matrix, &b_vector); + x_vector = multiplyMatrices(&C_matrix, D_matrix); + + colorO[2] = CLAMP(0.0, x_vector->data[0], 255.0); + colorH[2] = CLAMP(0.0, x_vector->data[1], 255.0); + colorV[2] = CLAMP(0.0, x_vector->data[2], 255.0); + + free(D_matrix->data); free(D_matrix); + free(x_vector->data); free(x_vector); + + // Quantize to 6 bits + double D = 255*(1.0/((1<<6)-1.0) ); + colorO8[0] = JAS_ROUND((1.0*colorO[0])/D); + colorO8[2] = JAS_ROUND((1.0*colorO[2])/D); + colorH8[0] = JAS_ROUND((1.0*colorH[0])/D); + colorH8[2] = JAS_ROUND((1.0*colorH[2])/D); + colorV8[0] = JAS_ROUND((1.0*colorV[0])/D); + colorV8[2] = JAS_ROUND((1.0*colorV[2])/D); + + // Quantize to 7 bits + D = 255*(1.0/((1<<7)-1.0) ); + colorO8[1] = JAS_ROUND((1.0*colorO[1])/D); + colorH8[1] = JAS_ROUND((1.0*colorH[1])/D); + colorV8[1] = JAS_ROUND((1.0*colorV[1])/D); + + // Pack bits in 57 bits + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ------------------------------------------------------------------------------------------------ + // | R0 | G0 | B0 | RH | GH | + // ------------------------------------------------------------------------------------------------ + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // ------------------------------------------------------------------------------------------------ + // | BH | RV | GV | BV | not used | + // ------------------------------------------------------------------------------------------------ + + compressed57_1 = 0; + compressed57_2 = 0; + PUTBITSHIGH( compressed57_1, colorO8[0], 6, 63); + PUTBITSHIGH( compressed57_1, colorO8[1], 7, 57); + PUTBITSHIGH( compressed57_1, colorO8[2], 6, 50); + PUTBITSHIGH( compressed57_1, colorH8[0], 6, 44); + PUTBITSHIGH( compressed57_1, colorH8[1], 7, 38); + PUTBITS( compressed57_2, colorH8[2], 6, 31); + PUTBITS( compressed57_2, colorV8[0], 6, 25); + PUTBITS( compressed57_2, colorV8[1], 7, 19); + PUTBITS( compressed57_2, colorV8[2], 6, 12); +} + +// During search it is not convenient to store the bits the way they are stored in the +// file format. Hence, after search, it is converted to this format. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void stuff57bits(unsigned int planar57_word1, unsigned int planar57_word2, unsigned int &planar_word1, unsigned int &planar_word2) +{ + // Put bits in twotimer configuration for 57 bits (red and green dont overflow, green does) + // + // Go from this bit layout: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // |R0 |G01G02 |B01B02 ;B03 |RH1 |RH2|GH | + // ----------------------------------------------------------------------------------------------- + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // ----------------------------------------------------------------------------------------------- + // |BH |RV |GV |BV | not used | + // ----------------------------------------------------------------------------------------------- + // + // To this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ------------------------------------------------------------------------------------------------ + // |//|R0 |G01|/|G02 |B01|/ // //|B02 |//|B03 |RH1 |df|RH2| + // ------------------------------------------------------------------------------------------------ + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // ----------------------------------------------------------------------------------------------- + // |GH |BH |RV |GV |BV | + // ----------------------------------------------------------------------------------------------- + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + uint8 RO, GO1, GO2, BO1, BO2, BO3, RH1, RH2, GH, BH, RV, GV, BV; + uint8 bit, a, b, c, d, bits; + + RO = GETBITSHIGH( planar57_word1, 6, 63); + GO1= GETBITSHIGH( planar57_word1, 1, 57); + GO2= GETBITSHIGH( planar57_word1, 6, 56); + BO1= GETBITSHIGH( planar57_word1, 1, 50); + BO2= GETBITSHIGH( planar57_word1, 2, 49); + BO3= GETBITSHIGH( planar57_word1, 3, 47); + RH1= GETBITSHIGH( planar57_word1, 5, 44); + RH2= GETBITSHIGH( planar57_word1, 1, 39); + GH = GETBITSHIGH( planar57_word1, 7, 38); + BH = GETBITS( planar57_word2, 6, 31); + RV = GETBITS( planar57_word2, 6, 25); + GV = GETBITS( planar57_word2, 7, 19); + BV = GETBITS( planar57_word2, 6, 12); + + planar_word1 = 0; planar_word2 = 0; + PUTBITSHIGH( planar_word1, RO, 6, 62); + PUTBITSHIGH( planar_word1, GO1, 1, 56); + PUTBITSHIGH( planar_word1, GO2, 6, 54); + PUTBITSHIGH( planar_word1, BO1, 1, 48); + PUTBITSHIGH( planar_word1, BO2, 2, 44); + PUTBITSHIGH( planar_word1, BO3, 3, 41); + PUTBITSHIGH( planar_word1, RH1, 5, 38); + PUTBITSHIGH( planar_word1, RH2, 1, 32); + PUTBITS( planar_word2, GH, 7, 31); + PUTBITS( planar_word2, BH, 6, 24); + PUTBITS( planar_word2, RV, 6, 18); + PUTBITS( planar_word2, GV, 7, 12); + PUTBITS( planar_word2, BV, 6, 5); + + // Make sure that red does not overflow: + bit = GETBITSHIGH( planar_word1, 1, 62); + PUTBITSHIGH( planar_word1, !bit, 1, 63); + + // Make sure that green does not overflow: + bit = GETBITSHIGH( planar_word1, 1, 54); + PUTBITSHIGH( planar_word1, !bit, 1, 55); + + // Make sure that blue overflows: + a = GETBITSHIGH( planar_word1, 1, 44); + b = GETBITSHIGH( planar_word1, 1, 43); + c = GETBITSHIGH( planar_word1, 1, 41); + d = GETBITSHIGH( planar_word1, 1, 40); + // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 + // The following logical expression checks for the presence of any of those: + bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); + bits = 0xf*bit; + PUTBITSHIGH( planar_word1, bits, 3, 47); + PUTBITSHIGH( planar_word1, !bit, 1, 42); + + // Set diffbit + PUTBITSHIGH( planar_word1, 1, 1, 33); +} + +// During search it is not convenient to store the bits the way they are stored in the +// file format. Hence, after search, it is converted to this format. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void stuff58bits(unsigned int thumbH58_word1, unsigned int thumbH58_word2, unsigned int &thumbH_word1, unsigned int &thumbH_word2) +{ + // Put bits in twotimer configuration for 58 (red doesn't overflow, green does) + // + // Go from this bit layout: + // + // + // |63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| + // |-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |---------------------------------------index bits----------------------------------------------| + // + // To this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // |//|R0 |G0 |// // //|G0|B0|//|B0b |R1 |G1 |B0 |d2|df|d1| + // ----------------------------------------------------------------------------------------------- + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |---------------------------------------index bits----------------------------------------------| + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt| + // ----------------------------------------------------------------------------------------------- + // + // + // Thus, what we are really doing is going from this bit layout: + // + // + // |63 62 61 60 59 58|57 56 55 54 53 52 51|50 49|48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33|32 | + // |-------empty-----|part0---------------|part1|part2------------------------------------------|part3| + // + // To this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------| + // |//|part0 |// // //|part1|//|part2 |df|part3| + // --------------------------------------------------------------------------------------------------| + + unsigned int part0, part1, part2, part3; + uint8 bit, a, b, c, d, bits; + + // move parts + part0 = GETBITSHIGH( thumbH58_word1, 7, 57); + part1 = GETBITSHIGH( thumbH58_word1, 2, 50); + part2 = GETBITSHIGH( thumbH58_word1,16, 48); + part3 = GETBITSHIGH( thumbH58_word1, 1, 32); + thumbH_word1 = 0; + PUTBITSHIGH( thumbH_word1, part0, 7, 62); + PUTBITSHIGH( thumbH_word1, part1, 2, 52); + PUTBITSHIGH( thumbH_word1, part2, 16, 49); + PUTBITSHIGH( thumbH_word1, part3, 1, 32); + + // Make sure that red does not overflow: + bit = GETBITSHIGH( thumbH_word1, 1, 62); + PUTBITSHIGH( thumbH_word1, !bit, 1, 63); + + // Make sure that green overflows: + a = GETBITSHIGH( thumbH_word1, 1, 52); + b = GETBITSHIGH( thumbH_word1, 1, 51); + c = GETBITSHIGH( thumbH_word1, 1, 49); + d = GETBITSHIGH( thumbH_word1, 1, 48); + // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 + // The following logical expression checks for the presence of any of those: + bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); + bits = 0xf*bit; + PUTBITSHIGH( thumbH_word1, bits, 3, 55); + PUTBITSHIGH( thumbH_word1, !bit, 1, 50); + + // Set diffbit + PUTBITSHIGH( thumbH_word1, 1, 1, 33); + thumbH_word2 = thumbH58_word2; + +} + +// copy of above, but diffbit is 0 +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void stuff58bitsDiffFalse(unsigned int thumbH58_word1, unsigned int thumbH58_word2, unsigned int &thumbH_word1, unsigned int &thumbH_word2) +{ + unsigned int part0, part1, part2, part3; + uint8 bit, a, b, c, d, bits; + + // move parts + part0 = GETBITSHIGH( thumbH58_word1, 7, 57); + part1 = GETBITSHIGH( thumbH58_word1, 2, 50); + part2 = GETBITSHIGH( thumbH58_word1,16, 48); + part3 = GETBITSHIGH( thumbH58_word1, 1, 32); + thumbH_word1 = 0; + PUTBITSHIGH( thumbH_word1, part0, 7, 62); + PUTBITSHIGH( thumbH_word1, part1, 2, 52); + PUTBITSHIGH( thumbH_word1, part2, 16, 49); + PUTBITSHIGH( thumbH_word1, part3, 1, 32); + + // Make sure that red does not overflow: + bit = GETBITSHIGH( thumbH_word1, 1, 62); + PUTBITSHIGH( thumbH_word1, !bit, 1, 63); + + // Make sure that green overflows: + a = GETBITSHIGH( thumbH_word1, 1, 52); + b = GETBITSHIGH( thumbH_word1, 1, 51); + c = GETBITSHIGH( thumbH_word1, 1, 49); + d = GETBITSHIGH( thumbH_word1, 1, 48); + // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 + // The following logical expression checks for the presence of any of those: + bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); + bits = 0xf*bit; + PUTBITSHIGH( thumbH_word1, bits, 3, 55); + PUTBITSHIGH( thumbH_word1, !bit, 1, 50); + + // Set diffbit + PUTBITSHIGH( thumbH_word1, 0, 1, 33); + thumbH_word2 = thumbH58_word2; + +} + +// During search it is not convenient to store the bits the way they are stored in the +// file format. Hence, after search, it is converted to this format. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void stuff59bits(unsigned int thumbT59_word1, unsigned int thumbT59_word2, unsigned int &thumbT_word1, unsigned int &thumbT_word2) +{ + // Put bits in twotimer configuration for 59 (red overflows) + // + // Go from this bit layout: + // + // |63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| + // |----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |----------------------------------------index bits---------------------------------------------| + // + // + // To this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // |// // //|R0a |//|R0b |G0 |B0 |R1 |G1 |B1 |da |df|db| + // ----------------------------------------------------------------------------------------------- + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |----------------------------------------index bits---------------------------------------------| + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt| + // ------------------------------------------------------------------------------------------------ + + uint8 R0a; + uint8 bit, a, b, c, d, bits; + + R0a = GETBITSHIGH( thumbT59_word1, 2, 58); + + // Fix middle part + thumbT_word1 = thumbT59_word1 << 1; + // Fix R0a (top two bits of R0) + PUTBITSHIGH( thumbT_word1, R0a, 2, 60); + // Fix db (lowest bit of d) + PUTBITSHIGH( thumbT_word1, thumbT59_word1, 1, 32); + // + // Make sure that red overflows: + a = GETBITSHIGH( thumbT_word1, 1, 60); + b = GETBITSHIGH( thumbT_word1, 1, 59); + c = GETBITSHIGH( thumbT_word1, 1, 57); + d = GETBITSHIGH( thumbT_word1, 1, 56); + // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 + // The following logical expression checks for the presence of any of those: + bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); + bits = 0xf*bit; + PUTBITSHIGH( thumbT_word1, bits, 3, 63); + PUTBITSHIGH( thumbT_word1, !bit, 1, 58); + + // Set diffbit + PUTBITSHIGH( thumbT_word1, 1, 1, 33); + thumbT_word2 = thumbT59_word2; +} + + +// Decompress the planar mode and calculate the error per component compared to original image. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void decompressBlockPlanar57errorPerComponent(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img,int width,int height,int startx,int starty, uint8 *srcimg, unsigned int &error_red, unsigned int &error_green, unsigned int &error_blue) +{ + uint8 colorO[3], colorH[3], colorV[3]; + + colorO[0] = GETBITSHIGH( compressed57_1, 6, 63); + colorO[1] = GETBITSHIGH( compressed57_1, 7, 57); + colorO[2] = GETBITSHIGH( compressed57_1, 6, 50); + colorH[0] = GETBITSHIGH( compressed57_1, 6, 44); + colorH[1] = GETBITSHIGH( compressed57_1, 7, 38); + colorH[2] = GETBITS( compressed57_2, 6, 31); + colorV[0] = GETBITS( compressed57_2, 6, 25); + colorV[1] = GETBITS( compressed57_2, 7, 19); + colorV[2] = GETBITS( compressed57_2, 6, 12); + + colorO[0] = (colorO[0] << 2) | (colorO[0] >> 4); + colorO[1] = (colorO[1] << 1) | (colorO[1] >> 6); + colorO[2] = (colorO[2] << 2) | (colorO[2] >> 4); + + colorH[0] = (colorH[0] << 2) | (colorH[0] >> 4); + colorH[1] = (colorH[1] << 1) | (colorH[1] >> 6); + colorH[2] = (colorH[2] << 2) | (colorH[2] >> 4); + + colorV[0] = (colorV[0] << 2) | (colorV[0] >> 4); + colorV[1] = (colorV[1] << 1) | (colorV[1] >> 6); + colorV[2] = (colorV[2] << 2) | (colorV[2] >> 4); + + int xx, yy; + + for( xx=0; xx<4; xx++) + { + for( yy=0; yy<4; yy++) + { + img[3*width*(starty+yy) + 3*(startx+xx) + 0] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[0]-colorO[0])/4.0 + yy*(colorV[0]-colorO[0])/4.0 + colorO[0])), 255); + img[3*width*(starty+yy) + 3*(startx+xx) + 1] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[1]-colorO[1])/4.0 + yy*(colorV[1]-colorO[1])/4.0 + colorO[1])), 255); + img[3*width*(starty+yy) + 3*(startx+xx) + 2] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[2]-colorO[2])/4.0 + yy*(colorV[2]-colorO[2])/4.0 + colorO[2])), 255); + } + } + + error_red = 0; + error_green= 0; + error_blue = 0; + for( xx=0; xx<4; xx++) + { + for( yy=0; yy<4; yy++) + { + error_red = error_red + SQUARE(srcimg[3*width*(starty+yy) + 3*(startx+xx) + 0] - img[3*width*(starty+yy) + 3*(startx+xx) + 0]); + error_green = error_green + SQUARE(srcimg[3*width*(starty+yy) + 3*(startx+xx) + 1] - img[3*width*(starty+yy) + 3*(startx+xx) + 1]); + error_blue = error_blue + SQUARE(srcimg[3*width*(starty+yy) + 3*(startx+xx) + 2] - img[3*width*(starty+yy) + 3*(startx+xx) + 2]); + + } + } +} + +// Compress using both individual and differential mode in ETC1/ETC2 using combined color +// quantization. Both flip modes are tried. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockDiffFlipCombined(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + int min_error=255*255*8*3; + unsigned int best_table_indices1=0, best_table_indices2=0; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + float eps; + + uint8 dummy[3]; + + quantize555ColorCombined(avg_color_float1, enc_color1, dummy); + quantize555ColorCombined(avg_color_float2, enc_color2, dummy); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + eps = (float) 0.0001; + + uint8 dummy[3]; + quantize444ColorCombined(avg_color_float1, enc_color1, dummy); + quantize444ColorCombined(avg_color_float2, enc_color2, dummy); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + + // Pack bits into the first word. + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // left part of block + norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + } + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + quantize555ColorCombined(avg_color_float1, enc_color1, dummy); + quantize555ColorCombined(avg_color_float2, enc_color2, dummy); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + eps = (float) 0.0001; + + uint8 dummy[3]; + quantize444ColorCombined(avg_color_float1, enc_color1, dummy); + quantize444ColorCombined(avg_color_float2, enc_color2, dummy); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + + // Now lets see which is the best table to use. Only 8 tables are possible. + + if(norm_err <= flip_err) + { + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + } + else + { + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + } +} + +// Calculation of the two block colors using the LBG-algorithm +// The following method scales down the intensity, since this can be compensated for anyway by both the H and T mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void computeColorLBGHalfIntensityFast(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) +{ + uint8 block_mask[4][4]; + + // reset rand so that we get predictable output per block + srand(10000); + //LBG-algorithm + double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; + double error_a, error_b; + int number_of_iterations = 10; + double t_color[2][3]; + double original_colors[4][4][3]; + double current_colors[2][3]; + double best_colors[2][3]; + double max_v[3]; + double min_v[3]; + int x,y,i; + double red, green, blue; + bool continue_seeding; + int maximum_number_of_seedings = 10; + int seeding; + bool continue_iterate; + + max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; + min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; + + // resolve trainingdata + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (x = 0; x < BLOCKWIDTH; ++x) + { + red = img[3*((starty+y)*width+startx+x)+R]; + green = img[3*((starty+y)*width+startx+x)+G]; + blue = img[3*((starty+y)*width+startx+x)+B]; + + // Use qrs representation instead of rgb + // qrs = Q * rgb where Q = [a a a ; b -b 0 ; c c -2c]; a = 1/sqrt(3), b= 1/sqrt(2), c = 1/sqrt(6); + // rgb = inv(Q)*qrs = Q' * qrs where ' denotes transpose. + // The q variable holds intensity. r and s hold chrominance. + // q = [0, sqrt(3)*255], r = [-255/sqrt(2), 255/sqrt(2)], s = [-2*255/sqrt(6), 2*255/sqrt(6)]; + // + // The LGB algorithm will only act on the r and s variables and not on q. + // + original_colors[x][y][R] = (1.0/sqrt(1.0*3))*red + (1.0/sqrt(1.0*3))*green + (1.0/sqrt(1.0*3))*blue; + original_colors[x][y][G] = (1.0/sqrt(1.0*2))*red - (1.0/sqrt(1.0*2))*green; + original_colors[x][y][B] = (1.0/sqrt(1.0*6))*red + (1.0/sqrt(1.0*6))*green - (2.0/sqrt(1.0*6))*blue; + + // find max + if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; + // find min + if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; + } + } + + D = 512*512*3*16.0; + bestD = 512*512*3*16.0; + + continue_seeding = true; + + // loop seeds + for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) + { + // hopefully we will not need more seedings: + continue_seeding = false; + + // calculate seeds + for (uint8 s = 0; s < 2; ++s) + { + for (uint8 c = 0; c < 3; ++c) + { + current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; + } + } + + // divide into two quantization sets and calculate distortion + + continue_iterate = true; + for(i = 0; (i < number_of_iterations) && continue_iterate; i++) + { + oldD = D; + D = 0; + int n = 0; + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + error_a = 0.5*SQUARE(original_colors[x][y][R] - current_colors[0][R]) + + SQUARE(original_colors[x][y][G] - current_colors[0][G]) + + SQUARE(original_colors[x][y][B] - current_colors[0][B]); + error_b = 0.5*SQUARE(original_colors[x][y][R] - current_colors[1][R]) + + SQUARE(original_colors[x][y][G] - current_colors[1][G]) + + SQUARE(original_colors[x][y][B] - current_colors[1][B]); + if (error_a < error_b) + { + block_mask[x][y] = 0; + D += error_a; + ++n; + } + else + { + block_mask[x][y] = 1; + D += error_b; + } + } + } + + // compare with old distortion + if (D == 0) + { + // Perfect score -- we dont need to go further iterations. + continue_iterate = false; + continue_seeding = false; + } + if (D == oldD) + { + // Same score as last round -- no need to go for further iterations. + continue_iterate = false; + continue_seeding = false; + } + if (D < bestD) + { + bestD = D; + for(uint8 s = 0; s < 2; ++s) + { + for(uint8 c = 0; c < 3; ++c) + { + best_colors[s][c] = current_colors[s][c]; + } + } + } + if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) + { + // All colors end up in the same voroni region. We need to reseed. + continue_iterate = false; + continue_seeding = true; + } + else + { + // Calculate new reconstruction points using the centroids + + // Find new construction values from average + t_color[0][R] = 0; + t_color[0][G] = 0; + t_color[0][B] = 0; + t_color[1][R] = 0; + t_color[1][G] = 0; + t_color[1][B] = 0; + + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + // use dummy value for q-parameter + t_color[block_mask[x][y]][R] += original_colors[x][y][R]; + t_color[block_mask[x][y]][G] += original_colors[x][y][G]; + t_color[block_mask[x][y]][B] += original_colors[x][y][B]; + } + } + current_colors[0][R] = t_color[0][R] / n; + current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][G] = t_color[0][G] / n; + current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][B] = t_color[0][B] / n; + current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); + } + } + } + + for(x=0;x<2;x++) + { + double qq, rr, ss; + + qq = best_colors[x][0]; + rr = best_colors[x][1]; + ss = best_colors[x][2]; + + current_colors[x][0] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); + current_colors[x][1] = CLAMP(0, (1.0/sqrt(1.0*3))*qq - (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); + current_colors[x][2] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (0.0 )*rr - (2.0/sqrt(1.0*6))*ss, 255); + } + + for(x=0;x<2;x++) + for(y=0;y<3;y++) + LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); +} + +// Calculation of the two block colors using the LBG-algorithm +// The following method scales down the intensity, since this can be compensated for anyway by both the H and T mode. +// Faster version +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void computeColorLBGNotIntensityFast(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) +{ + uint8 block_mask[4][4]; + + // reset rand so that we get predictable output per block + srand(10000); + //LBG-algorithm + double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; + double error_a, error_b; + int number_of_iterations = 10; + double t_color[2][3]; + double original_colors[4][4][3]; + double current_colors[2][3]; + double best_colors[2][3]; + double max_v[3]; + double min_v[3]; + int x,y,i; + double red, green, blue; + bool continue_seeding; + int maximum_number_of_seedings = 10; + int seeding; + bool continue_iterate; + + max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; + min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; + + // resolve trainingdata + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (x = 0; x < BLOCKWIDTH; ++x) + { + red = img[3*((starty+y)*width+startx+x)+R]; + green = img[3*((starty+y)*width+startx+x)+G]; + blue = img[3*((starty+y)*width+startx+x)+B]; + + // Use qrs representation instead of rgb + // qrs = Q * rgb where Q = [a a a ; b -b 0 ; c c -2c]; a = 1/sqrt(1.0*3), b= 1/sqrt(1.0*2), c = 1/sqrt(1.0*6); + // rgb = inv(Q)*qrs = Q' * qrs where ' denotes transpose. + // The q variable holds intensity. r and s hold chrominance. + // q = [0, sqrt(1.0*3)*255], r = [-255/sqrt(1.0*2), 255/sqrt(1.0*2)], s = [-2*255/sqrt(1.0*6), 2*255/sqrt(1.0*6)]; + // + // The LGB algorithm will only act on the r and s variables and not on q. + // + original_colors[x][y][R] = (1.0/sqrt(1.0*3))*red + (1.0/sqrt(1.0*3))*green + (1.0/sqrt(1.0*3))*blue; + original_colors[x][y][G] = (1.0/sqrt(1.0*2))*red - (1.0/sqrt(1.0*2))*green; + original_colors[x][y][B] = (1.0/sqrt(1.0*6))*red + (1.0/sqrt(1.0*6))*green - (2.0/sqrt(1.0*6))*blue; + + // find max + if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; + // find min + if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; + } + } + + D = 512*512*3*16.0; + bestD = 512*512*3*16.0; + + continue_seeding = true; + + // loop seeds + for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) + { + // hopefully we will not need more seedings: + continue_seeding = false; + + // calculate seeds + for (uint8 s = 0; s < 2; ++s) + { + for (uint8 c = 0; c < 3; ++c) + { + current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; + } + } + // divide into two quantization sets and calculate distortion + + continue_iterate = true; + for(i = 0; (i < number_of_iterations) && continue_iterate; i++) + { + oldD = D; + D = 0; + int n = 0; + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + error_a = 0.0*SQUARE(original_colors[x][y][R] - current_colors[0][R]) + + SQUARE(original_colors[x][y][G] - current_colors[0][G]) + + SQUARE(original_colors[x][y][B] - current_colors[0][B]); + error_b = 0.0*SQUARE(original_colors[x][y][R] - current_colors[1][R]) + + SQUARE(original_colors[x][y][G] - current_colors[1][G]) + + SQUARE(original_colors[x][y][B] - current_colors[1][B]); + if (error_a < error_b) + { + block_mask[x][y] = 0; + D += error_a; + ++n; + } + else + { + block_mask[x][y] = 1; + D += error_b; + } + } + } + + // compare with old distortion + if (D == 0) + { + // Perfect score -- we dont need to go further iterations. + continue_iterate = false; + continue_seeding = false; + } + if (D == oldD) + { + // Same score as last round -- no need to go for further iterations. + continue_iterate = false; + continue_seeding = false; + } + if (D < bestD) + { + bestD = D; + for(uint8 s = 0; s < 2; ++s) + { + for(uint8 c = 0; c < 3; ++c) + { + best_colors[s][c] = current_colors[s][c]; + } + } + } + if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) + { + // All colors end up in the same voroni region. We need to reseed. + continue_iterate = false; + continue_seeding = true; + } + else + { + // Calculate new reconstruction points using the centroids + + // Find new construction values from average + t_color[0][R] = 0; + t_color[0][G] = 0; + t_color[0][B] = 0; + t_color[1][R] = 0; + t_color[1][G] = 0; + t_color[1][B] = 0; + + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + // use dummy value for q-parameter + t_color[block_mask[x][y]][R] += original_colors[x][y][R]; + t_color[block_mask[x][y]][G] += original_colors[x][y][G]; + t_color[block_mask[x][y]][B] += original_colors[x][y][B]; + } + } + current_colors[0][R] = t_color[0][R] / n; + current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][G] = t_color[0][G] / n; + current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][B] = t_color[0][B] / n; + current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); + } + } + } + + for(x=0;x<2;x++) + { + double qq, rr, ss; + + qq = best_colors[x][0]; + rr = best_colors[x][1]; + ss = best_colors[x][2]; + + current_colors[x][0] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); + current_colors[x][1] = CLAMP(0, (1.0/sqrt(1.0*3))*qq - (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); + current_colors[x][2] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (0.0 )*rr - (2.0/sqrt(1.0*6))*ss, 255); + } + + for(x=0;x<2;x++) + for(y=0;y<3;y++) + LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); +} + +// Calculation of the two block colors using the LBG-algorithm +// The following method completely ignores the intensity, since this can be compensated for anyway by both the H and T mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void computeColorLBGNotIntensity(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) +{ + uint8 block_mask[4][4]; + + // reset rand so that we get predictable output per block + srand(10000); + //LBG-algorithm + double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; + double error_a, error_b; + int number_of_iterations = 10; + double t_color[2][3]; + double original_colors[4][4][3]; + double current_colors[2][3]; + double best_colors[2][3]; + double max_v[3]; + double min_v[3]; + int x,y,i; + double red, green, blue; + bool continue_seeding; + int maximum_number_of_seedings = 10; + int seeding; + bool continue_iterate; + + max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; + min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; + + // resolve trainingdata + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (x = 0; x < BLOCKWIDTH; ++x) + { + red = img[3*((starty+y)*width+startx+x)+R]; + green = img[3*((starty+y)*width+startx+x)+G]; + blue = img[3*((starty+y)*width+startx+x)+B]; + + // Use qrs representation instead of rgb + // qrs = Q * rgb where Q = [a a a ; b -b 0 ; c c -2c]; a = 1/sqrt(1.0*3), b= 1/sqrt(1.0*2), c = 1/sqrt(1.0*6); + // rgb = inv(Q)*qrs = Q' * qrs where ' denotes transpose. + // The q variable holds intensity. r and s hold chrominance. + // q = [0, sqrt(1.0*3)*255], r = [-255/sqrt(1.0*2), 255/sqrt(1.0*2)], s = [-2*255/sqrt(1.0*6), 2*255/sqrt(1.0*6)]; + // + // The LGB algorithm will only act on the r and s variables and not on q. + // + original_colors[x][y][R] = (1.0/sqrt(1.0*3))*red + (1.0/sqrt(1.0*3))*green + (1.0/sqrt(1.0*3))*blue; + original_colors[x][y][G] = (1.0/sqrt(1.0*2))*red - (1.0/sqrt(1.0*2))*green; + original_colors[x][y][B] = (1.0/sqrt(1.0*6))*red + (1.0/sqrt(1.0*6))*green - (2.0/sqrt(1.0*6))*blue; + + // find max + if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; + // find min + if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; + } + } + + D = 512*512*3*16.0; + bestD = 512*512*3*16.0; + + continue_seeding = true; + + // loop seeds + for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) + { + // hopefully we will not need more seedings: + continue_seeding = false; + + // calculate seeds + for (uint8 s = 0; s < 2; ++s) + { + for (uint8 c = 0; c < 3; ++c) + { + current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; + } + } + + // divide into two quantization sets and calculate distortion + + continue_iterate = true; + for(i = 0; (i < number_of_iterations) && continue_iterate; i++) + { + oldD = D; + D = 0; + int n = 0; + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + error_a = 0.0*SQUARE(original_colors[x][y][R] - current_colors[0][R]) + + SQUARE(original_colors[x][y][G] - current_colors[0][G]) + + SQUARE(original_colors[x][y][B] - current_colors[0][B]); + error_b = 0.0*SQUARE(original_colors[x][y][R] - current_colors[1][R]) + + SQUARE(original_colors[x][y][G] - current_colors[1][G]) + + SQUARE(original_colors[x][y][B] - current_colors[1][B]); + if (error_a < error_b) + { + block_mask[x][y] = 0; + D += error_a; + ++n; + } + else + { + block_mask[x][y] = 1; + D += error_b; + } + } + } + + // compare with old distortion + if (D == 0) + { + // Perfect score -- we dont need to go further iterations. + continue_iterate = false; + continue_seeding = false; + } + if (D == oldD) + { + // Same score as last round -- no need to go for further iterations. + continue_iterate = false; + continue_seeding = true; + } + if (D < bestD) + { + bestD = D; + for(uint8 s = 0; s < 2; ++s) + { + for(uint8 c = 0; c < 3; ++c) + { + best_colors[s][c] = current_colors[s][c]; + } + } + } + if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) + { + // All colors end up in the same voroni region. We need to reseed. + continue_iterate = false; + continue_seeding = true; + } + else + { + // Calculate new reconstruction points using the centroids + + // Find new construction values from average + t_color[0][R] = 0; + t_color[0][G] = 0; + t_color[0][B] = 0; + t_color[1][R] = 0; + t_color[1][G] = 0; + t_color[1][B] = 0; + + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + // use dummy value for q-parameter + t_color[block_mask[x][y]][R] += original_colors[x][y][R]; + t_color[block_mask[x][y]][G] += original_colors[x][y][G]; + t_color[block_mask[x][y]][B] += original_colors[x][y][B]; + } + } + current_colors[0][R] = t_color[0][R] / n; + current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][G] = t_color[0][G] / n; + current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][B] = t_color[0][B] / n; + current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); + } + } + } + + for(x=0;x<2;x++) + { + double qq, rr, ss; + + qq = best_colors[x][0]; + rr = best_colors[x][1]; + ss = best_colors[x][2]; + + current_colors[x][0] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); + current_colors[x][1] = CLAMP(0, (1.0/sqrt(1.0*3))*qq - (1.0/sqrt(1.0*2))*rr + (1.0/sqrt(1.0*6))*ss, 255); + current_colors[x][2] = CLAMP(0, (1.0/sqrt(1.0*3))*qq + (0.0 )*rr - (2.0/sqrt(1.0*6))*ss, 255); + } + + for(x=0;x<2;x++) + for(y=0;y<3;y++) + LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); +} + +// Calculation of the two block colors using the LBG-algorithm +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void computeColorLBG(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) +{ + uint8 block_mask[4][4]; + + // reset rand so that we get predictable output per block + srand(10000); + //LBG-algorithm + double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; + double error_a, error_b; + int number_of_iterations = 10; + double t_color[2][3]; + double original_colors[4][4][3]; + double current_colors[2][3]; + double best_colors[2][3]; + double max_v[3]; + double min_v[3]; + int x,y,i; + double red, green, blue; + bool continue_seeding; + int maximum_number_of_seedings = 10; + int seeding; + bool continue_iterate; + + max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; + min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; + + // resolve trainingdata + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (x = 0; x < BLOCKWIDTH; ++x) + { + red = img[3*((starty+y)*width+startx+x)+R]; + green = img[3*((starty+y)*width+startx+x)+G]; + blue = img[3*((starty+y)*width+startx+x)+B]; + + original_colors[x][y][R] = red; + original_colors[x][y][G] = green; + original_colors[x][y][B] = blue; + + // find max + if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; + // find min + if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; + } + } + + D = 512*512*3*16.0; + bestD = 512*512*3*16.0; + + continue_seeding = true; + + // loop seeds + for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) + { + // hopefully we will not need more seedings: + continue_seeding = false; + + // calculate seeds + for (uint8 s = 0; s < 2; ++s) + { + for (uint8 c = 0; c < 3; ++c) + { + current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; + } + } + + // divide into two quantization sets and calculate distortion + + continue_iterate = true; + for(i = 0; (i < number_of_iterations) && continue_iterate; i++) + { + oldD = D; + D = 0; + int n = 0; + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + error_a = SQUARE(original_colors[x][y][R] - JAS_ROUND(current_colors[0][R])) + + SQUARE(original_colors[x][y][G] - JAS_ROUND(current_colors[0][G])) + + SQUARE(original_colors[x][y][B] - JAS_ROUND(current_colors[0][B])); + error_b = SQUARE(original_colors[x][y][R] - JAS_ROUND(current_colors[1][R])) + + SQUARE(original_colors[x][y][G] - JAS_ROUND(current_colors[1][G])) + + SQUARE(original_colors[x][y][B] - JAS_ROUND(current_colors[1][B])); + if (error_a < error_b) + { + block_mask[x][y] = 0; + D += error_a; + ++n; + } + else + { + block_mask[x][y] = 1; + D += error_b; + } + } + } + + // compare with old distortion + if (D == 0) + { + // Perfect score -- we dont need to go further iterations. + continue_iterate = false; + continue_seeding = false; + } + if (D == oldD) + { + // Same score as last round -- no need to go for further iterations. + continue_iterate = false; + continue_seeding = true; + } + if (D < bestD) + { + bestD = D; + for(uint8 s = 0; s < 2; ++s) + { + for(uint8 c = 0; c < 3; ++c) + { + best_colors[s][c] = current_colors[s][c]; + } + } + } + if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) + { + // All colors end up in the same voroni region. We need to reseed. + continue_iterate = false; + continue_seeding = true; + } + else + { + // Calculate new reconstruction points using the centroids + + // Find new construction values from average + t_color[0][R] = 0; + t_color[0][G] = 0; + t_color[0][B] = 0; + t_color[1][R] = 0; + t_color[1][G] = 0; + t_color[1][B] = 0; + + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + // use dummy value for q-parameter + t_color[block_mask[x][y]][R] += original_colors[x][y][R]; + t_color[block_mask[x][y]][G] += original_colors[x][y][G]; + t_color[block_mask[x][y]][B] += original_colors[x][y][B]; + } + } + current_colors[0][R] = t_color[0][R] / n; + current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][G] = t_color[0][G] / n; + current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][B] = t_color[0][B] / n; + current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); + } + } + } + + // Set the best colors as the final block colors + for(int s = 0; s < 2; ++s) + { + for(uint8 c = 0; c < 3; ++c) + { + current_colors[s][c] = best_colors[s][c]; + } + } + + for(x=0;x<2;x++) + for(y=0;y<3;y++) + LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); +} + +// Calculation of the two block colors using the LBG-algorithm +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void computeColorLBGfast(uint8 *img,int width,int startx,int starty, uint8 (LBG_colors)[2][3]) +{ + uint8 block_mask[4][4]; + + // reset rand so that we get predictable output per block + srand(10000); + //LBG-algorithm + double D = 0, oldD, bestD = MAXIMUM_ERROR, eps = 0.0000000001; + double error_a, error_b; + int number_of_iterations = 10; + double t_color[2][3]; + uint8 original_colors[4][4][3]; + double current_colors[2][3]; + double best_colors[2][3]; + double max_v[3]; + double min_v[3]; + int x,y,i; + bool continue_seeding; + int maximum_number_of_seedings = 10; + int seeding; + bool continue_iterate; + + max_v[R] = -512.0; max_v[G] = -512.0; max_v[B] = -512.0; + min_v[R] = 512.0; min_v[G] = 512.0; min_v[B] = 512.0; + + // resolve trainingdata + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (x = 0; x < BLOCKWIDTH; ++x) + { + original_colors[x][y][R] = img[3*((starty+y)*width+startx+x)+R]; + original_colors[x][y][G] = img[3*((starty+y)*width+startx+x)+G]; + original_colors[x][y][B] = img[3*((starty+y)*width+startx+x)+B]; + + // find max + if (original_colors[x][y][R] > max_v[R]) max_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] > max_v[G]) max_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] > max_v[B]) max_v[B] = original_colors[x][y][B]; + // find min + if (original_colors[x][y][R] < min_v[R]) min_v[R] = original_colors[x][y][R]; + if (original_colors[x][y][G] < min_v[G]) min_v[G] = original_colors[x][y][G]; + if (original_colors[x][y][B] < min_v[B]) min_v[B] = original_colors[x][y][B]; + } + } + + D = 512*512*3*16.0; + bestD = 512*512*3*16.0; + + continue_seeding = true; + + // loop seeds + for (seeding = 0; (seeding < maximum_number_of_seedings) && continue_seeding; seeding++) + { + // hopefully we will not need more seedings: + continue_seeding = false; + + // calculate seeds + for (uint8 s = 0; s < 2; ++s) + { + for (uint8 c = 0; c < 3; ++c) + { + current_colors[s][c] = double((double(rand())/RAND_MAX)*(max_v[c]-min_v[c])) + min_v[c]; + } + } + + // divide into two quantization sets and calculate distortion + continue_iterate = true; + for(i = 0; (i < number_of_iterations) && continue_iterate; i++) + { + oldD = D; + D = 0; + int n = 0; + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + error_a = SQUARE(original_colors[x][y][R] - JAS_ROUND(current_colors[0][R])) + + SQUARE(original_colors[x][y][G] - JAS_ROUND(current_colors[0][G])) + + SQUARE(original_colors[x][y][B] - JAS_ROUND(current_colors[0][B])); + error_b = SQUARE(original_colors[x][y][R] - JAS_ROUND(current_colors[1][R])) + + SQUARE(original_colors[x][y][G] - JAS_ROUND(current_colors[1][G])) + + SQUARE(original_colors[x][y][B] - JAS_ROUND(current_colors[1][B])); + if (error_a < error_b) + { + block_mask[x][y] = 0; + D += error_a; + ++n; + } + else + { + block_mask[x][y] = 1; + D += error_b; + } + } + } + + // compare with old distortion + if (D == 0) + { + // Perfect score -- we dont need to go further iterations. + continue_iterate = false; + continue_seeding = false; + } + if (D == oldD) + { + // Same score as last round -- no need to go for further iterations. + continue_iterate = false; + continue_seeding = false; + } + if (D < bestD) + { + bestD = D; + for(uint8 s = 0; s < 2; ++s) + { + for(uint8 c = 0; c < 3; ++c) + { + best_colors[s][c] = current_colors[s][c]; + } + } + } + if (n == 0 || n == BLOCKWIDTH*BLOCKHEIGHT) + { + // All colors end up in the same voroni region. We need to reseed. + continue_iterate = false; + continue_seeding = true; + } + else + { + // Calculate new reconstruction points using the centroids + + // Find new construction values from average + t_color[0][R] = 0; + t_color[0][G] = 0; + t_color[0][B] = 0; + t_color[1][R] = 0; + t_color[1][G] = 0; + t_color[1][B] = 0; + + for (y = 0; y < BLOCKHEIGHT; ++y) + { + for (int x = 0; x < BLOCKWIDTH; ++x) + { + // use dummy value for q-parameter + t_color[block_mask[x][y]][R] += original_colors[x][y][R]; + t_color[block_mask[x][y]][G] += original_colors[x][y][G]; + t_color[block_mask[x][y]][B] += original_colors[x][y][B]; + } + } + current_colors[0][R] = t_color[0][R] / n; + current_colors[1][R] = t_color[1][R] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][G] = t_color[0][G] / n; + current_colors[1][G] = t_color[1][G] / (BLOCKWIDTH*BLOCKHEIGHT - n); + current_colors[0][B] = t_color[0][B] / n; + current_colors[1][B] = t_color[1][B] / (BLOCKWIDTH*BLOCKHEIGHT - n); + } + } + } + + // Set the best colors as the final block colors + for(int s = 0; s < 2; ++s) + { + for(uint8 c = 0; c < 3; ++c) + { + current_colors[s][c] = best_colors[s][c]; + } + } + + for(x=0;x<2;x++) + for(y=0;y<3;y++) + LBG_colors[x][y] = JAS_ROUND(current_colors[x][y]); +} + +// Each color component is compressed to fit in its specified number of bits +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressColor(int R_B, int G_B, int B_B, uint8 (current_color)[2][3], uint8 (quantized_color)[2][3]) +{ + // + // The color is calculated as: + // + // c = (c + (2^(8-b))/2) / (255 / (2^b - 1)) where b is the number of bits + // to code color c with + // For instance, if b = 3: + // + // c = (c + 16) / (255 / 7) = 7 * (c + 16) / 255 + // + + quantized_color[0][R] = CLAMP(0,(BINPOW(R_B)-1) * (current_color[0][R] + BINPOW(8-R_B-1)) / 255,255); + quantized_color[0][G] = CLAMP(0,(BINPOW(G_B)-1) * (current_color[0][G] + BINPOW(8-G_B-1)) / 255,255); + quantized_color[0][B] = CLAMP(0,(BINPOW(B_B)-1) * (current_color[0][B] + BINPOW(8-B_B-1)) / 255,255); + + quantized_color[1][R] = CLAMP(0,(BINPOW(R_B)-1) * (current_color[1][R] + BINPOW(8-R_B-1)) / 255,255); + quantized_color[1][G] = CLAMP(0,(BINPOW(G_B)-1) * (current_color[1][G] + BINPOW(8-G_B-1)) / 255,255); + quantized_color[1][B] = CLAMP(0,(BINPOW(B_B)-1) * (current_color[1][B] + BINPOW(8-B_B-1)) / 255,255); +} + +// Swapping two RGB-colors +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void swapColors(uint8 (colors)[2][3]) +{ + uint8 temp = colors[0][R]; + colors[0][R] = colors[1][R]; + colors[1][R] = temp; + + temp = colors[0][G]; + colors[0][G] = colors[1][G]; + colors[1][G] = temp; + + temp = colors[0][B]; + colors[0][B] = colors[1][B]; + colors[1][B] = temp; +} + + +// Calculate the paint colors from the block colors +// using a distance d and one of the H- or T-patterns. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. + +// Calculate the error for the block at position (startx,starty) +// The parameters needed for reconstruction are calculated as well +// +// Please note that the function can change the order between the two colors in colorsRGB444 +// +// In the 59T bit mode, we only have pattern T. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateError59Tperceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) +{ + + unsigned int block_error = 0, + best_block_error = MAXERR1000, + pixel_error, + best_pixel_error; + int diff[3]; + uint8 best_sw; + unsigned int pixel_colors; + uint8 colors[2][3]; + uint8 possible_colors[4][3]; + + // First use the colors as they are, then swap them + for (uint8 sw = 0; sw <2; ++sw) + { + if (sw == 1) + { + swapColors(colorsRGB444); + } + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) + { + calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXERR1000; + pixel_colors <<=2; // Make room for next value + + // Loop possible block colors + for (uint8 c = 0; c < 4; ++c) + { + + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); + + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]) + + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*SQUARE(diff[B]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + pixel_colors ^= (pixel_colors & 3); // Reset the two first bits + pixel_colors |= c; + } + } + block_error += best_pixel_error; + } + } + if (block_error < best_block_error) + { + best_block_error = block_error; + distance = d; + pixel_indices = pixel_colors; + best_sw = sw; + } + } + + if (sw == 1 && best_sw == 0) + { + swapColors(colorsRGB444); + } + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + } + return best_block_error; +} + +// Calculate the error for the block at position (startx,starty) +// The parameters needed for reconstruction is calculated as well +// +// Please note that the function can change the order between the two colors in colorsRGB444 +// +// In the 59T bit mode, we only have pattern T. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calculateError59T(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) +{ + double block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + int diff[3]; + uint8 best_sw; + unsigned int pixel_colors; + uint8 colors[2][3]; + uint8 possible_colors[4][3]; + + // First use the colors as they are, then swap them + for (uint8 sw = 0; sw <2; ++sw) + { + if (sw == 1) + { + swapColors(colorsRGB444); + } + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) + { + calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXIMUM_ERROR; + pixel_colors <<=2; // Make room for next value + + // Loop possible block colors + for (uint8 c = 0; c < 4; ++c) + { + + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); + + pixel_error = weight[R]*SQUARE(diff[R]) + + weight[G]*SQUARE(diff[G]) + + weight[B]*SQUARE(diff[B]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + pixel_colors ^= (pixel_colors & 3); // Reset the two first bits + pixel_colors |= c; + } + } + block_error += best_pixel_error; + } + } + if (block_error < best_block_error) + { + best_block_error = block_error; + distance = d; + pixel_indices = pixel_colors; + best_sw = sw; + } + } + + if (sw == 1 && best_sw == 0) + { + swapColors(colorsRGB444); + } + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + } + return best_block_error; +} + +// Calculate the error for the block at position (startx,starty) +// The parameters needed for reconstruction is calculated as well +// +// In the 59T bit mode, we only have pattern T. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateError59TnoSwapPerceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) +{ + + unsigned int block_error = 0, + best_block_error = MAXERR1000, + pixel_error, + best_pixel_error; + int diff[3]; + unsigned int pixel_colors; + uint8 colors[2][3]; + uint8 possible_colors[4][3]; + int thebestintheworld; + + // First use the colors as they are, then swap them + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) + { + calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXERR1000; + pixel_colors <<=2; // Make room for next value + + // Loop possible block colors + for (uint8 c = 0; c < 4; ++c) + { + + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); + + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]) + + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*SQUARE(diff[B]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + pixel_colors ^= (pixel_colors & 3); // Reset the two first bits + pixel_colors |= c; + thebestintheworld = c; + } + } + block_error += best_pixel_error; + } + } + if (block_error < best_block_error) + { + best_block_error = block_error; + distance = d; + pixel_indices = pixel_colors; + } + } + + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + return best_block_error; +} + +// Calculate the error for the block at position (startx,starty) +// The parameters needed for reconstruction is calculated as well +// +// In the 59T bit mode, we only have pattern T. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calculateError59TnoSwap(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) +{ + double block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + int diff[3]; + unsigned int pixel_colors; + uint8 colors[2][3]; + uint8 possible_colors[4][3]; + int thebestintheworld; + + // First use the colors as they are, then swap them + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) + { + calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXIMUM_ERROR; + pixel_colors <<=2; // Make room for next value + + // Loop possible block colors + for (uint8 c = 0; c < 4; ++c) + { + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); + + pixel_error = weight[R]*SQUARE(diff[R]) + + weight[G]*SQUARE(diff[G]) + + weight[B]*SQUARE(diff[B]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + pixel_colors ^= (pixel_colors & 3); // Reset the two first bits + pixel_colors |= c; + thebestintheworld = c; + } + } + block_error += best_pixel_error; + } + } + if (block_error < best_block_error) + { + best_block_error = block_error; + distance = d; + pixel_indices = pixel_colors; + } + } + + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + return best_block_error; +} + +// Put the compress params into the compression block +// +// +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void packBlock59T(uint8 (colors)[2][3], uint8 d, unsigned int pixel_indices, unsigned int &compressed1, unsigned int &compressed2) +{ + + compressed1 = 0; + + PUTBITSHIGH( compressed1, colors[0][R], 4, 58); + PUTBITSHIGH( compressed1, colors[0][G], 4, 54); + PUTBITSHIGH( compressed1, colors[0][B], 4, 50); + PUTBITSHIGH( compressed1, colors[1][R], 4, 46); + PUTBITSHIGH( compressed1, colors[1][G], 4, 42); + PUTBITSHIGH( compressed1, colors[1][B], 4, 38); + PUTBITSHIGH( compressed1, d, TABLE_BITS_59T, 34); + pixel_indices=indexConversion(pixel_indices); + compressed2 = 0; + PUTBITS( compressed2, pixel_indices, 32, 31); +} + +// Copy colors from source to dest +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void copyColors(uint8 (source)[2][3], uint8 (dest)[2][3]) +{ + int x,y; + + for (x=0; x<2; x++) + for (y=0; y<3; y++) + dest[x][y] = source[x][y]; +} + +// The below code should compress the block to 59 bits. +// +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockTHUMB59TFastestOnlyColorPerceptual1000(uint8 *img,int width,int height,int startx,int starty, int (best_colorsRGB444_packed)[2]) +{ + unsigned int best_error = MAXERR1000; + unsigned int best_pixel_indices; + uint8 best_distance; + + unsigned int error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm + computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); + compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); + + // Determine the parameters for the lowest error + error_no_i = calculateError59Tperceptual1000(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + + best_colorsRGB444_packed[0] = (colorsRGB444_no_i[0][0] << 8) + (colorsRGB444_no_i[0][1] << 4) + (colorsRGB444_no_i[0][2] << 0); + best_colorsRGB444_packed[1] = (colorsRGB444_no_i[1][0] << 8) + (colorsRGB444_no_i[1][1] << 4) + (colorsRGB444_no_i[1][2] << 0); + + return best_error; +} + + +// The below code should compress the block to 59 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double compressBlockTHUMB59TFastestOnlyColor(uint8 *img,int width,int height,int startx,int starty, int (best_colorsRGB444_packed)[2]) +{ + double best_error = MAXIMUM_ERROR; + unsigned int best_pixel_indices; + uint8 best_distance; + + double error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm + computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); + compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); + + // Determine the parameters for the lowest error + error_no_i = calculateError59T(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + + best_colorsRGB444_packed[0] = (colorsRGB444_no_i[0][0] << 8) + (colorsRGB444_no_i[0][1] << 4) + (colorsRGB444_no_i[0][2] << 0); + best_colorsRGB444_packed[1] = (colorsRGB444_no_i[1][0] << 8) + (colorsRGB444_no_i[1][1] << 4) + (colorsRGB444_no_i[1][2] << 0); + + return best_error; +} + +// The below code should compress the block to 59 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double compressBlockTHUMB59TFastestPerceptual1000(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + double best_error = MAXIMUM_ERROR; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + double error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm + computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); + compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); + + // Determine the parameters for the lowest error + error_no_i = calculateError59Tperceptual1000(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + copyColors(colorsRGB444_no_i, best_colorsRGB444); + + // Put the compress params into the compression block + packBlock59T(best_colorsRGB444, best_distance, best_pixel_indices, compressed1, compressed2); + + return best_error; +} + +// The below code should compress the block to 59 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double compressBlockTHUMB59TFastest(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + double best_error = MAXIMUM_ERROR; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + double error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm + computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); + compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); + + // Determine the parameters for the lowest error + error_no_i = calculateError59T(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + copyColors(colorsRGB444_no_i, best_colorsRGB444); + + // Put the compress params into the compression block + packBlock59T(best_colorsRGB444, best_distance, best_pixel_indices, compressed1, compressed2); + + return best_error; +} + +// The below code should compress the block to 59 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double compressBlockTHUMB59TFast(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + double best_error = MAXIMUM_ERROR; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + double error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + + double error_half_i; + uint8 colorsRGB444_half_i[2][3]; + unsigned int pixel_indices_half_i; + uint8 distance_half_i; + + double error; + uint8 colorsRGB444[2][3]; + unsigned int pixel_indices; + uint8 distance; + + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm + computeColorLBGNotIntensityFast(img,width,startx,starty, colors); + compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); + // Determine the parameters for the lowest error + error_no_i = calculateError59T(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + // Calculate average color using the LBG-algorithm + computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); + compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_half_i); + // Determine the parameters for the lowest error + error_half_i = calculateError59T(img, width, startx, starty, colorsRGB444_half_i, distance_half_i, pixel_indices_half_i); + + // Calculate average color using the LBG-algorithm + computeColorLBGfast(img,width,startx,starty, colors); + compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444); + // Determine the parameters for the lowest error + error = calculateError59T(img, width, startx, starty, colorsRGB444, distance, pixel_indices); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + copyColors(colorsRGB444_no_i, best_colorsRGB444); + + if(error_half_i < best_error) + { + best_error = error_half_i; + best_distance = distance_half_i; + best_pixel_indices = pixel_indices_half_i; + copyColors (colorsRGB444_half_i, best_colorsRGB444); + } + if(error < best_error) + { + best_error = error; + best_distance = distance; + best_pixel_indices = pixel_indices; + copyColors (colorsRGB444, best_colorsRGB444); + } + + // Put the compress params into the compression block + packBlock59T(best_colorsRGB444, best_distance, best_pixel_indices, compressed1, compressed2); + + return best_error; +} + +// Calculate the error for the block at position (startx,starty) +// The parameters needed for reconstruction is calculated as well +// +// In the 58H bit mode, we only have pattern H. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateErrorAndCompress58Hperceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) +{ + unsigned int block_error = 0, + best_block_error = MAXERR1000, + pixel_error, + best_pixel_error; + int diff[3]; + unsigned int pixel_colors; + uint8 possible_colors[4][3]; + uint8 colors[2][3]; + + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) + { + calculatePaintColors58H(d, PATTERN_H, colors, possible_colors); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXERR1000; + pixel_colors <<=2; // Make room for next value + + // Loop possible block colors + for (uint8 c = 0; c < 4; ++c) + { + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); + + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]) + + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*SQUARE(diff[B]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + pixel_colors ^= (pixel_colors & 3); // Reset the two first bits + pixel_colors |= c; + } + } + block_error += best_pixel_error; + } + } + + if (block_error < best_block_error) + { + best_block_error = block_error; + distance = d; + pixel_indices = pixel_colors; + } + } + return best_block_error; +} + +// The H-mode but with punchthrough alpha +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calculateErrorAndCompress58HAlpha(uint8* srcimg, uint8* alphaimg,int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) +{ + double block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + int diff[3]; + unsigned int pixel_colors; + uint8 possible_colors[4][3]; + uint8 colors[2][3]; + int alphaindex; + int colorsRGB444_packed[2]; + colorsRGB444_packed[0] = (colorsRGB444[0][R] << 8) + (colorsRGB444[0][G] << 4) + colorsRGB444[0][B]; + colorsRGB444_packed[1] = (colorsRGB444[1][R] << 8) + (colorsRGB444[1][G] << 4) + colorsRGB444[1][B]; + + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) + { + alphaindex=2; + if( (colorsRGB444_packed[0] >= colorsRGB444_packed[1]) ^ ((d & 1)==1) ) + { + //we're going to have to swap the colors to be able to choose this distance.. that means + //that the indices will be swapped as well, so C1 will be the one with alpha instead of C3.. + alphaindex=0; + } + + calculatePaintColors58H(d, PATTERN_H, colors, possible_colors); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + int alpha=0; + if(alphaimg[((starty+y)*width+startx+x)]>0) + alpha=1; + if(alphaimg[((starty+y)*width+startx+x)]>0&&alphaimg[((starty+y)*width+startx+x)]<255) + printf("INVALID ALPHA DATA!!\n"); + best_pixel_error = MAXIMUM_ERROR; + pixel_colors <<=2; // Make room for next value + + // Loop possible block colors + for (uint8 c = 0; c < 4; ++c) + { + if(c==alphaindex&&alpha) + { + pixel_error=0; + } + else if(c==alphaindex||alpha) + { + pixel_error=MAXIMUM_ERROR; + } + else + { + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); + + pixel_error = weight[R]*SQUARE(diff[R]) + + weight[G]*SQUARE(diff[G]) + + weight[B]*SQUARE(diff[B]); + } + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + pixel_colors ^= (pixel_colors & 3); // Reset the two first bits + pixel_colors |= c; + } + } + block_error += best_pixel_error; + } + } + if (block_error < best_block_error) + { + best_block_error = block_error; + distance = d; + pixel_indices = pixel_colors; + } + } + return best_block_error; +} + +// Calculate the error for the block at position (startx,starty) +// The parameters needed for reconstruction is calculated as well +// +// In the 58H bit mode, we only have pattern H. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calculateErrorAndCompress58H(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) +{ + double block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + int diff[3]; + unsigned int pixel_colors; + uint8 possible_colors[4][3]; + uint8 colors[2][3]; + + + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) + { + calculatePaintColors58H(d, PATTERN_H, colors, possible_colors); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXIMUM_ERROR; + pixel_colors <<=2; // Make room for next value + + // Loop possible block colors + for (uint8 c = 0; c < 4; ++c) + { + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); + + pixel_error = weight[R]*SQUARE(diff[R]) + + weight[G]*SQUARE(diff[G]) + + weight[B]*SQUARE(diff[B]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + pixel_colors ^= (pixel_colors & 3); // Reset the two first bits + pixel_colors |= c; + } + } + block_error += best_pixel_error; + } + } + + if (block_error < best_block_error) + { + best_block_error = block_error; + distance = d; + pixel_indices = pixel_colors; + } + } + + return best_block_error; +} + +// Makes sure that col0 < col1; +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void sortColorsRGB444(uint8 (colorsRGB444)[2][3]) +{ + unsigned int col0, col1, tcol; + + // sort colors + col0 = 16*16*colorsRGB444[0][R] + 16*colorsRGB444[0][G] + colorsRGB444[0][B]; + col1 = 16*16*colorsRGB444[1][R] + 16*colorsRGB444[1][G] + colorsRGB444[1][B]; + + // After this, col0 should be smaller than col1 (col0 < col1) + if( col0 > col1) + { + tcol = col0; + col0 = col1; + col1 = tcol; + } + else + { + if(col0 == col1) + { + // Both colors are the same. That is useless. If they are both black, + // col1 can just as well be (0,0,1). Else, col0 can be col1 - 1. + if(col0 == 0) + col1 = col0+1; + else + col0 = col1-1; + } + } + + colorsRGB444[0][R] = GETBITS(col0, 4, 11); + colorsRGB444[0][G] = GETBITS(col0, 4, 7); + colorsRGB444[0][B] = GETBITS(col0, 4, 3); + colorsRGB444[1][R] = GETBITS(col1, 4, 11); + colorsRGB444[1][G] = GETBITS(col1, 4, 7); + colorsRGB444[1][B] = GETBITS(col1, 4, 3); +} + +// The below code should compress the block to 58 bits. +// The bit layout is thought to be: +// +//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| +//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. +// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. +// Else, it is assumed to be 1. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockTHUMB58HFastestPerceptual1000(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int best_error = MAXERR1000; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + unsigned int error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm but discarding the intensity in the error function + computeColorLBGHalfIntensityFast(img, width, startx, starty, colors); + compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_no_i); + sortColorsRGB444(colorsRGB444_no_i); + + error_no_i = calculateErrorAndCompress58Hperceptual1000(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + copyColors(colorsRGB444_no_i, best_colorsRGB444); + + // | col0 >= col1 col0 < col1 + //------------------------------------------------------ + // (dist & 1) = 1 | no need to swap | need to swap + // |-----------------+---------------- + // (dist & 1) = 0 | need to swap | no need to swap + // + // This can be done with an xor test. + + int best_colorsRGB444_packed[2]; + best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; + best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; + if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) + { + swapColors(best_colorsRGB444); + + // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 + best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); + } + + // Put the compress params into the compression block + + compressed1 = 0; + + PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); + PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); + + compressed2 = 0; + best_pixel_indices=indexConversion(best_pixel_indices); + PUTBITS( compressed2, best_pixel_indices, 32, 31); + + return best_error; +} + +// The below code should compress the block to 58 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// The bit layout is thought to be: +// +//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| +//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. +// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. +// Else, it is assumed to be 1. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double compressBlockTHUMB58HFastest(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + double best_error = MAXIMUM_ERROR; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + double error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm but discarding the intensity in the error function + computeColorLBGHalfIntensityFast(img, width, startx, starty, colors); + compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_no_i); + sortColorsRGB444(colorsRGB444_no_i); + + error_no_i = calculateErrorAndCompress58H(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + copyColors(colorsRGB444_no_i, best_colorsRGB444); + + // | col0 >= col1 col0 < col1 + //------------------------------------------------------ + // (dist & 1) = 1 | no need to swap | need to swap + // |-----------------+---------------- + // (dist & 1) = 0 | need to swap | no need to swap + // + // This can be done with an xor test. + + int best_colorsRGB444_packed[2]; + best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; + best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; + if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) + { + swapColors(best_colorsRGB444); + + // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 + best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); + } + + // Put the compress params into the compression block + + compressed1 = 0; + + PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); + PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); + best_pixel_indices=indexConversion(best_pixel_indices); + compressed2 = 0; + PUTBITS( compressed2, best_pixel_indices, 32, 31); + + return best_error; +} + +//same as above, but with 1-bit alpha +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double compressBlockTHUMB58HAlpha(uint8 *img, uint8* alphaimg, int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + double best_error = MAXIMUM_ERROR; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + double error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm but discarding the intensity in the error function + computeColorLBGHalfIntensityFast(img, width, startx, starty, colors); + compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_no_i); + sortColorsRGB444(colorsRGB444_no_i); + + error_no_i = calculateErrorAndCompress58HAlpha(img, alphaimg,width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + copyColors(colorsRGB444_no_i, best_colorsRGB444); + + // | col0 >= col1 col0 < col1 + //------------------------------------------------------ + // (dist & 1) = 1 | no need to swap | need to swap + // |-----------------+---------------- + // (dist & 1) = 0 | need to swap | no need to swap + // + // This can be done with an xor test. + + int best_colorsRGB444_packed[2]; + best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; + best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; + if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) + { + swapColors(best_colorsRGB444); + + // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 + best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); + } + + // Put the compress params into the compression block + + compressed1 = 0; + + PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); + PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); + best_pixel_indices=indexConversion(best_pixel_indices); + compressed2 = 0; + PUTBITS( compressed2, best_pixel_indices, 32, 31); + + return best_error; +} + +// The below code should compress the block to 58 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// The bit layout is thought to be: +// +//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| +//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. +// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. +// Else, it is assumed to be 1. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double compressBlockTHUMB58HFast(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + double best_error = MAXIMUM_ERROR; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + double error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + + double error_half_i; + uint8 colorsRGB444_half_i[2][3]; + unsigned int pixel_indices_half_i; + uint8 distance_half_i; + + double error; + uint8 colorsRGB444[2][3]; + unsigned int pixel_indices; + uint8 distance; + + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm but discarding the intensity in the error function + computeColorLBGNotIntensity(img, width, startx, starty, colors); + compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_no_i); + sortColorsRGB444(colorsRGB444_no_i); + error_no_i = calculateErrorAndCompress58H(img, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + // Calculate average color using the LBG-algorithm but halfing the influence of the intensity in the error function + computeColorLBGNotIntensity(img, width, startx, starty, colors); + compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444_half_i); + sortColorsRGB444(colorsRGB444_half_i); + error_half_i = calculateErrorAndCompress58H(img, width, startx, starty, colorsRGB444_half_i, distance_half_i, pixel_indices_half_i); + + // Calculate average color using the LBG-algorithm + computeColorLBG(img, width, startx, starty, colors); + compressColor(R_BITS58H, G_BITS58H, B_BITS58H, colors, colorsRGB444); + sortColorsRGB444(colorsRGB444); + error = calculateErrorAndCompress58H(img, width, startx, starty, colorsRGB444, distance, pixel_indices); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + copyColors(colorsRGB444_no_i, best_colorsRGB444); + + if(error_half_i < best_error) + { + best_error = error_half_i; + best_distance = distance_half_i; + best_pixel_indices = pixel_indices_half_i; + copyColors(colorsRGB444_half_i, best_colorsRGB444); + } + + if(error < best_error) + { + best_error = error; + best_distance = distance; + best_pixel_indices = pixel_indices; + copyColors(colorsRGB444, best_colorsRGB444); + } + + // | col0 >= col1 col0 < col1 + //------------------------------------------------------ + // (dist & 1) = 1 | no need to swap | need to swap + // |-----------------+---------------- + // (dist & 1) = 0 | need to swap | no need to swap + // + // This can be done with an xor test. + + int best_colorsRGB444_packed[2]; + best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; + best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; + if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) + { + swapColors(best_colorsRGB444); + + // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 + best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); + } + + // Put the compress params into the compression block + compressed1 = 0; + + PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); + PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); + best_pixel_indices=indexConversion(best_pixel_indices); + compressed2 = 0; + PUTBITS( compressed2, best_pixel_indices, 32, 31); + + return best_error; +} + +// Compress block testing both individual and differential mode. +// Perceptual error metric. +// Combined quantization for colors. +// Both flipped and unflipped tested. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockDiffFlipCombinedPerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + int min_error=255*255*8*3; + unsigned int best_table_indices1=0, best_table_indices2=0; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + float eps; + + uint8 dummy[3]; + + quantize555ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); + quantize555ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + eps = (float) 0.0001; + + quantize444ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); + quantize444ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // Pack bits into the first word. + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // left part of block + norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + } + + // Now try flipped blocks 4x2: + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + quantize555ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); + quantize555ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + eps = (float) 0.0001; + + quantize444ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); + quantize444ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + // Pack bits into the first word. + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + + // Now lets see which is the best table to use. Only 8 tables are possible. + if(norm_err <= flip_err) + { + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + } + else + { + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + } +} + +// Calculate the error of a block +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calcBlockErrorRGB(uint8 *img, uint8 *imgdec, int width, int height, int startx, int starty) +{ + int xx,yy; + double err; + + err = 0; + + for(xx = startx; xx< startx+4; xx++) + { + for(yy = starty; yy3) + diff[c]=3; + enc_color2[c]=enc_color1[c]+diff[c]; + } + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + // see regular compressblockdiffflipfast for details + + compressed1_temp = 0; + PUTBITSHIGH( compressed1_temp, !isTransparent, 1, 33); + PUTBITSHIGH( compressed1_temp, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_temp, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_temp, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_temp, diff[0], 3, 58); + PUTBITSHIGH( compressed1_temp, diff[1], 3, 50); + PUTBITSHIGH( compressed1_temp, diff[2], 3, 42); + + temp_err = 0; + + int besterror[2]; + besterror[0]=255*255*3*16; + besterror[1]=255*255*3*16; + int besttable[2]; + int best_indices_LSB[16]; + int best_indices_MSB[16]; + //for each table, we're going to compute the indices required to get minimum error in each half. + //then we'll check if this was the best table for either half, and set besterror/besttable accordingly. + for(int table=0; table<8; table++) + { + int taberror[2];//count will be sort of an index of each pixel within a half, determining where the index will be placed in the bitstream. + + int pixel_indices_LSB[16],pixel_indices_MSB[16]; + + for(int i=0; i<2; i++) + { + taberror[i]=0; + } + for(int x=0; x<4; x++) + { + for(int y=0; y<4; y++) + { + int index = x+startx+(y+starty)*width; + uint8 basecol[3]; + bool transparentPixel=alphaimg[index]<128; + //determine which half of the block this pixel is in, based on the flipbit. + int half=0; + if( (flipbit==0&&x<2) || (flipbit&&y<2) ) + { + basecol[0]=avg_color_quant1[0]; + basecol[1]=avg_color_quant1[1]; + basecol[2]=avg_color_quant1[2]; + } + else + { + half=1; + basecol[0]=avg_color_quant2[0]; + basecol[1]=avg_color_quant2[1]; + basecol[2]=avg_color_quant2[2]; + } + int besterri=255*255*3*2; + int besti=0; + int erri; + for(int i=0; i<4; i++) + { + if(i==1&&isTransparent) + continue; + erri=0; + for(int c=0; c<3; c++) + { + int col=CLAMP(0,((int)basecol[c])+compressParams[table*2][i],255); + if(i==2&&isTransparent) + { + col=(int)basecol[c]; + } + int errcol=col-((int)(img[index*3+c])); + erri=erri+(errcol*errcol); + } + if(erri> 1); + pixel_indices_LSB[x*4+y]=(pixel_index & 1); + } + } + for(int half=0; half<2; half++) + { + if(taberror[half] 128) +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calcBlockErrorRGBA(uint8 *img, uint8 *imgdec, uint8* alpha, int width, int height, int startx, int starty) +{ + int xx,yy; + double err; + + err = 0; + + for(xx = startx; xx< startx+4; xx++) + { + for(yy = starty; yy128) + { + err += SQUARE(1.0*RED(img,width,xx,yy) - 1.0*RED(imgdec, width, xx,yy)); + err += SQUARE(1.0*GREEN(img,width,xx,yy)- 1.0*GREEN(imgdec, width, xx,yy)); + err += SQUARE(1.0*BLUE(img,width,xx,yy) - 1.0*BLUE(imgdec, width, xx,yy)); + } + } + } + return err; +} + +//calculates the error for a block using the given colors, and the paremeters required to obtain the error. This version uses 1-bit punch-through alpha. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calculateError59TAlpha(uint8* srcimg, uint8* alpha,int width, int startx, int starty, uint8 (colorsRGB444)[2][3], uint8 &distance, unsigned int &pixel_indices) +{ + + double block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + int diff[3]; + uint8 best_sw; + unsigned int pixel_colors; + uint8 colors[2][3]; + uint8 possible_colors[4][3]; + + // First use the colors as they are, then swap them + for (uint8 sw = 0; sw <2; ++sw) + { + if (sw == 1) + { + swapColors(colorsRGB444); + } + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_59T); ++d) + { + calculatePaintColors59T(d,PATTERN_T, colors, possible_colors); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXIMUM_ERROR; + pixel_colors <<=2; // Make room for next value + + // Loop possible block colors + if(alpha[x+startx+(y+starty)*width]==0) + { + best_pixel_error=0; + pixel_colors ^= (pixel_colors & 3); // Reset the two first bits + pixel_colors |= 2; //insert the index for this pixel, two meaning transparent. + } + else + { + for (uint8 c = 0; c < 4; ++c) + { + + if(c==2) + continue; //don't use this, because we don't have alpha here and index 2 means transparent. + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + diff[B] = srcimg[3*((starty+y)*width+startx+x)+B] - CLAMP(0,possible_colors[c][B],255); + + pixel_error = weight[R]*SQUARE(diff[R]) + + weight[G]*SQUARE(diff[G]) + + weight[B]*SQUARE(diff[B]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + pixel_colors ^= (pixel_colors & 3); // Reset the two first bits + pixel_colors |= c; //insert the index for this pixel + } + } + } + block_error += best_pixel_error; + } + } + if (block_error < best_block_error) + { + best_block_error = block_error; + distance = d; + pixel_indices = pixel_colors; + best_sw = sw; + } + } + + if (sw == 1 && best_sw == 0) + { + swapColors(colorsRGB444); + } + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + } + return best_block_error; +} + +// same as fastest t-mode compressor above, but here one of the colors (the central one in the T) is used to also signal that the pixel is transparent. +// the only difference is that calculateError has been swapped out to one that considers alpha. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double compressBlockTHUMB59TAlpha(uint8 *img, uint8* alpha, int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + double best_error = MAXIMUM_ERROR; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + double error_no_i; + uint8 colorsRGB444_no_i[2][3]; + unsigned int pixel_indices_no_i; + uint8 distance_no_i; + + uint8 colors[2][3]; + + // Calculate average color using the LBG-algorithm + computeColorLBGHalfIntensityFast(img,width,startx,starty, colors); + compressColor(R_BITS59T, G_BITS59T, B_BITS59T, colors, colorsRGB444_no_i); + + // Determine the parameters for the lowest error + error_no_i = calculateError59TAlpha(img, alpha, width, startx, starty, colorsRGB444_no_i, distance_no_i, pixel_indices_no_i); + + best_error = error_no_i; + best_distance = distance_no_i; + best_pixel_indices = pixel_indices_no_i; + copyColors(colorsRGB444_no_i, best_colorsRGB444); + + // Put the compress params into the compression block + packBlock59T(best_colorsRGB444, best_distance, best_pixel_indices, compressed1, compressed2); + + return best_error; +} + +// Put bits in order for the format. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void stuff59bitsDiffFalse(unsigned int thumbT59_word1, unsigned int thumbT59_word2, unsigned int &thumbT_word1, unsigned int &thumbT_word2) +{ + // Put bits in twotimer configuration for 59 (red overflows) + // + // Go from this bit layout: + // + // |63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| + // |----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |----------------------------------------index bits---------------------------------------------| + // + // + // To this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // |// // //|R0a |//|R0b |G0 |B0 |R1 |G1 |B1 |da |df|db| + // ----------------------------------------------------------------------------------------------- + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |----------------------------------------index bits---------------------------------------------| + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt| + // ------------------------------------------------------------------------------------------------ + + uint8 R0a; + uint8 bit, a, b, c, d, bits; + + R0a = GETBITSHIGH( thumbT59_word1, 2, 58); + + // Fix middle part + thumbT_word1 = thumbT59_word1 << 1; + // Fix R0a (top two bits of R0) + PUTBITSHIGH( thumbT_word1, R0a, 2, 60); + // Fix db (lowest bit of d) + PUTBITSHIGH( thumbT_word1, thumbT59_word1, 1, 32); + // + // Make sure that red overflows: + a = GETBITSHIGH( thumbT_word1, 1, 60); + b = GETBITSHIGH( thumbT_word1, 1, 59); + c = GETBITSHIGH( thumbT_word1, 1, 57); + d = GETBITSHIGH( thumbT_word1, 1, 56); + // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111 + // The following logical expression checks for the presence of any of those: + bit = (a & c) | (!a & b & c & d) | (a & b & !c & d); + bits = 0xf*bit; + PUTBITSHIGH( thumbT_word1, bits, 3, 63); + PUTBITSHIGH( thumbT_word1, !bit, 1, 58); + + // Set diffbit + PUTBITSHIGH( thumbT_word1, 0, 1, 33); + thumbT_word2 = thumbT59_word2; +} + +// Tests if there is at least one pixel in the image which would get alpha = 0 in punchtrough mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +bool hasAlpha(uint8* alphaimg, int ix, int iy, int width) +{ + for(int x=ix; x> 8) & 0xff; + bytes[1] = (block >> 0) & 0xff; + + fwrite(&bytes[0],1,1,f); + fwrite(&bytes[1],1,1,f); +} + + +// Write a word in big endian style +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void write_big_endian_4byte_word(unsigned int *blockadr, FILE *f) +{ + uint8 bytes[4]; + unsigned int block; + + block = blockadr[0]; + + bytes[0] = (block >> 24) & 0xff; + bytes[1] = (block >> 16) & 0xff; + bytes[2] = (block >> 8) & 0xff; + bytes[3] = (block >> 0) & 0xff; + + fwrite(&bytes[0],1,1,f); + fwrite(&bytes[1],1,1,f); + fwrite(&bytes[2],1,1,f); + fwrite(&bytes[3],1,1,f); +} + +extern int alphaTable[256][8]; +extern int alphaBase[16][4]; + +// valtab holds precalculated data used for compressing using EAC2. +// Note that valtab is constructed using get16bits11bits, which means +// that it already is expanded to 16 bits. +// Note also that it its contents will depend on the value of formatSigned. +int *valtab; + +void setupAlphaTableAndValtab() +{ + setupAlphaTable(); + + //fix precomputation table..! + valtab = new int[1024*512]; + int16 val16; + int count=0; + for(int base=0; base<256; base++) + { + for(int tab=0; tab<16; tab++) + { + for(int mul=0; mul<16; mul++) + { + for(int index=0; index<8; index++) + { + if(formatSigned) + { + val16=get16bits11signed(base,tab,mul,index); + valtab[count] = val16 + 256*128; + } + else + valtab[count]=get16bits11bits(base,tab,mul,index); + count++; + } + } + } + } +} + +// Reads alpha data +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void readAlpha(uint8* &data, int &width, int &height, int &extendedwidth, int &extendedheight) +{ + //width and height are already known..? + uint8* tempdata; + int wantedBitDepth; + if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS||format==ETC2PACKAGE_RGBA1_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) + { + wantedBitDepth=8; + } + else if(format==ETC2PACKAGE_R_NO_MIPMAPS) + { + wantedBitDepth=16; + } + else + { + printf("invalid format for alpha reading!\n"); + exit(1); + } + fReadPGM("alpha.pgm",width,height,tempdata,wantedBitDepth); + extendedwidth=4*((width+3)/4); + extendedheight=4*((height+3)/4); + + if(width==extendedwidth&&height==extendedheight) + { + data=tempdata; + } + else + { + data = (uint8*)malloc(extendedwidth*extendedheight*wantedBitDepth/8); + uint8 last=0; + uint8 lastlast=0; + for(int x=0; xmaxdist) + maxdist=abs(alpha-data[ix+x+(iy+y)*width]); //maximum distance from average + } + } + int approxPos = (maxdist*255)/160-4; //experimentally derived formula for calculating approximate table position given a max distance from average + if(approxPos>255) + approxPos=255; + int startTable=approxPos-15; //first table to be tested + if(startTable<0) + startTable=0; + int endTable=clamp(approxPos+15); //last table to be tested + + int bestsum=1000000000; + int besttable=-3; + int bestalpha=128; + int prevalpha=alpha; + + //main loop: determine best base alpha value and offset table to use for compression + //try some different alpha tables. + for(int table = startTable; table0; table++) + { + int tablealpha=prevalpha; + int tablebestsum=1000000000; + //test some different alpha values, trying to find the best one for the given table. + for(int alphascale=16; alphascale>0; alphascale/=4) + { + int startalpha; + int endalpha; + if(alphascale==16) + { + startalpha = clamp(tablealpha-alphascale*4); + endalpha = clamp(tablealpha+alphascale*4); + } + else + { + startalpha = clamp(tablealpha-alphascale*2); + endalpha = clamp(tablealpha+alphascale*2); + } + for(alpha=startalpha; alpha<=endalpha; alpha+=alphascale) + { + int sum=0; + int val,diff,bestdiff=10000000,index; + for(int x=0; x<4; x++) + { + for(int y=0; y<4; y++) + { + //compute best offset here, add square difference to sum.. + val=data[ix+x+(iy+y)*width]; + bestdiff=1000000000; + //the values are always ordered from small to large, with the first 4 being negative and the last 4 positive + //search is therefore made in the order 0-1-2-3 or 7-6-5-4, stopping when error increases compared to the previous entry tested. + if(val>alpha) + { + for(index=7; index>3; index--) + { + diff=clamp_table[alpha+(int)(alphaTable[table][index])+255]-val; + diff*=diff; + if(diff<=bestdiff) + { + bestdiff=diff; + } + else + break; + } + } + else + { + for(index=0; index<4; index++) + { + diff=clamp_table[alpha+(int)(alphaTable[table][index])+255]-val; + diff*=diff; + if(diffbestsum) + { + x=9999; //just to make it large and get out of the x<4 loop + break; + } + } + } + if(sum7) + { + bit=0; + byte++; + } + } + } + } +} + +// Helper function for the below function +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int getPremulIndex(int base, int tab, int mul, int index) +{ + return (base<<11)+(tab<<7)+(mul<<3)+index; +} + +// Calculates the error used in compressBlockAlpha16() +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calcError(uint8* data, int ix, int iy, int width, int height, int base, int tab, int mul, double prevbest) +{ + int offset = getPremulIndex(base,tab,mul,0); + double error=0; + for (int y=0; y<4; y++) + { + for(int x=0; x<4; x++) + { + double besthere = (1<<20); + besthere*=besthere; + uint8 byte1 = data[2*(x+ix+(y+iy)*width)]; + uint8 byte2 = data[2*(x+ix+(y+iy)*width)+1]; + int alpha = (byte1<<8)+byte2; + for(int index=0; index<8; index++) + { + double indexError; + indexError = alpha-valtab[offset+index]; + indexError*=indexError; + if(indexError=prevbest) + return prevbest+(1<<30); + } + } + return error; +} + +// compressBlockAlpha16 +// +// Compresses a block using the 11-bit EAC formats. +// Depends on the global variable formatSigned. +// +// COMPRESSED_R11_EAC (if formatSigned = 0) +// This is an 11-bit unsigned format. Since we do not have a good 11-bit file format, we use 16-bit pgm instead. +// Here we assume that, in the input 16-bit pgm file, 0 represents 0.0 and 65535 represents 1.0. The function compressBlockAlpha16 +// will find the compressed block which best matches the data. In detail, it will find the compressed block, which +// if decompressed, will generate an 11-bit block that after bit replication to 16-bits will generate the closest +// block to the original 16-bit pgm block. +// +// COMPRESSED_SIGNED_R11_EAC (if formatSigned = 1) +// This is an 11-bit signed format. Since we do not have any signed file formats, we use unsigned 16-bit pgm instead. +// Hence we assume that, in the input 16-bit pgm file, 1 represents -1.0, 32768 represents 0.0 and 65535 represents 1.0. +// The function compresseBlockAlpha16 will find the compressed block, which if decompressed, will generate a signed +// 11-bit block that after bit replication to 16-bits and conversion to unsigned (1 equals -1.0, 32768 equals 0.0 and +// 65535 equals 1.0) will generate the closest block to the original 16-bit pgm block. +// +// COMPRESSED_RG11_EAC is compressed by calling the function twice, dito for COMPRESSED_SIGNED_RG11_EAC. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockAlpha16(uint8* data, int ix, int iy, int width, int height, uint8* returnData) +{ + unsigned int bestbase, besttable, bestmul; + double besterror; + besterror=1<<20; + besterror*=besterror; + for(int base=0; base<256; base++) + { + for(int table=0; table<16; table++) + { + for(int mul=0; mul<16; mul++) + { + double e = calcError(data, ix, iy, width, height,base,table,mul,besterror); + if(e7) + { + bit=0; + byte++; + } + } + } + } +} + +// Exhaustive compression of alpha compression in a GL_COMPRESSED_RGB8_ETC2 block +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockAlphaSlow(uint8* data, int ix, int iy, int width, int height, uint8* returnData) +{ + //determine the best table and base alpha value for this block using MSE + int alphasum=0; + int maxdist=-2; + for(int x=0; x<4; x++) + { + for(int y=0; y<4; y++) + { + alphasum+=data[ix+x+(iy+y)*width]; + } + } + int alpha = (int)( ((float)alphasum)/16.0f+0.5f); //average pixel value, used as guess for base value. + + int bestsum=1000000000; + int besttable=-3; + int bestalpha=128; + int prevalpha=alpha; + + //main loop: determine best base alpha value and offset table to use for compression + //try some different alpha tables. + for(int table = 0; table<256&&bestsum>0; table++) + { + int tablealpha=prevalpha; + int tablebestsum=1000000000; + //test some different alpha values, trying to find the best one for the given table. + for(int alphascale=32; alphascale>0; alphascale/=8) + { + + int startalpha = clamp(tablealpha-alphascale*4); + int endalpha = clamp(tablealpha+alphascale*4); + + for(alpha=startalpha; alpha<=endalpha; alpha+=alphascale) { + int sum=0; + int val,diff,bestdiff=10000000,index; + for(int x=0; x<4; x++) + { + for(int y=0; y<4; y++) + { + //compute best offset here, add square difference to sum.. + val=data[ix+x+(iy+y)*width]; + bestdiff=1000000000; + //the values are always ordered from small to large, with the first 4 being negative and the last 4 positive + //search is therefore made in the order 0-1-2-3 or 7-6-5-4, stopping when error increases compared to the previous entry tested. + if(val>alpha) + { + for(index=7; index>3; index--) + { + diff=clamp_table[alpha+(alphaTable[table][index])+255]-val; + diff*=diff; + if(diff<=bestdiff) + { + bestdiff=diff; + } + else + break; + } + } + else + { + for(index=0; index<5; index++) + { + diff=clamp_table[alpha+(alphaTable[table][index])+255]-val; + diff*=diff; + if(difftablebestsum) + { + x=9999; //just to make it large and get out of the x<4 loop + break; + } + } + } + if(sum7) + { + bit=0; + byte++; + } + } + } + } +} + +// Calculate weighted PSNR +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calculateWeightedPSNR(uint8 *lossyimg, uint8 *origimg, int width, int height, double w1, double w2, double w3) +{ + // Note: This calculation of PSNR uses the formula + // + // PSNR = 10 * log_10 ( 255^2 / wMSE ) + // + // where the wMSE is calculated as + // + // 1/(N*M) * sum ( ( w1*(R' - R)^2 + w2*(G' - G)^2 + w3*(B' - B)^2) ) + // + // typical weights are 0.299, 0.587, 0.114 for perceptually weighted PSNR and + // 1.0/3.0, 1.0/3.0, 1.0/3.0 for nonweighted PSNR + + int x,y; + double wMSE; + double PSNR; + double err; + wMSE = 0; + + for(y=0;y.\n",srcfile); + exit(1); + } + height=active_height; + width=active_width; + fclose(f); +} + +// Writes output file +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void writeOutputFile(char *dstfile, uint8* img, uint8* alphaimg, int width, int height) +{ + char str[300]; + + if(format!=ETC2PACKAGE_R_NO_MIPMAPS&&format!=ETC2PACKAGE_RG_NO_MIPMAPS) + { + fWritePPM("tmp.ppm",width,height,img,8,false); + printf("Saved file tmp.ppm \n\n"); + } + else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) + { + fWritePPM("tmp.ppm",width,height,img,16,false); + } + if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS||format==ETC2PACKAGE_RGBA1_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) + fWritePGM("alphaout.pgm",width,height,alphaimg,false,8); + if(format==ETC2PACKAGE_R_NO_MIPMAPS) + fWritePGM("alphaout.pgm",width,height,alphaimg,false,16); + + // Delete destination file if it exists + if(fileExist(dstfile)) + { + sprintf(str, "del %s\n",dstfile); + system(str); + } + + int q = find_pos_of_extension(dstfile); + if(!strcmp(&dstfile[q],".ppm")&&format!=ETC2PACKAGE_R_NO_MIPMAPS) + { + // Already a .ppm file. Just rename. + sprintf(str,"move tmp.ppm %s\n",dstfile); + printf("Renaming destination file to %s\n",dstfile); + } + else + { + // Converting from .ppm to other file format + // + // Use your favorite command line image converter program, + // for instance Image Magick. Just make sure the syntax can + // be written as below: + // + // C:\magick convert source.ppm dest.jpg + // + if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS||format==ETC2PACKAGE_RGBA1_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) + { + // Somewhere after version 6.7.1-2 of ImageMagick the following command gives the wrong result due to a bug. + // sprintf(str,"composite -compose CopyOpacity alphaout.pgm tmp.ppm %s\n",dstfile); + // Instead we read the file and write a tga. + + printf("Converting destination file from .ppm/.pgm to %s with alpha\n",dstfile); + int rw, rh; + unsigned char *pixelsRGB; + unsigned char *pixelsA; + fReadPPM("tmp.ppm", rw, rh, pixelsRGB, 8); + fReadPGM("alphaout.pgm", rw, rh, pixelsA, 8); + fWriteTGAfromRGBandA(dstfile, rw, rh, pixelsRGB, pixelsA, true); + free(pixelsRGB); + free(pixelsA); + sprintf(str,""); // Nothing to execute. + } + else if(format==ETC2PACKAGE_R_NO_MIPMAPS) + { + sprintf(str,"magick convert alphaout.pgm %s\n",dstfile); + printf("Converting destination file from .pgm to %s\n",dstfile); + } + else + { + sprintf(str,"magick convert tmp.ppm %s\n",dstfile); + printf("Converting destination file from .ppm to %s\n",dstfile); + } + } + // Execute system call + system(str); + + free(img); + if(alphaimg!=NULL) + free(alphaimg); +} + +// Calculates the PSNR between two files +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calculatePSNRfile(char *srcfile, uint8 *origimg, uint8* origalpha) +{ + uint8 *alphaimg, *img; + int active_width, active_height; + uncompressFile(srcfile,img,alphaimg,active_width,active_height); + + // calculate Mean Square Error (MSE) + double MSER=0,MSEG=0,MSEB=0,MSEA, PSNRR,PSNRG,PSNRA; + double MSE; + double wMSE; + double PSNR=0; + double wPSNR; + double err; + MSE = 0; + MSEA=0; + wMSE = 0; + int width=((active_width+3)/4)*4; + int height=((active_height+3)/4)*4; + int numpixels = 0; + for(int y=0;y 0) + { + err = img[y*active_width*3+x*3+0] - origimg[y*width*3+x*3+0]; + MSE += ((err * err)/3.0); + wMSE += PERCEPTUAL_WEIGHT_R_SQUARED * (err*err); + err = img[y*active_width*3+x*3+1] - origimg[y*width*3+x*3+1]; + MSE += ((err * err)/3.0); + wMSE += PERCEPTUAL_WEIGHT_G_SQUARED * (err*err); + err = img[y*active_width*3+x*3+2] - origimg[y*width*3+x*3+2]; + MSE += ((err * err)/3.0); + wMSE += PERCEPTUAL_WEIGHT_B_SQUARED * (err*err); + numpixels++; + } + } + else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) + { + int rorig = (origimg[6*(y*width+x)+0]<<8)+origimg[6*(y*width+x)+1]; + int rnew = ( img[6*(y*active_width+x)+0]<<8)+ img[6*(y*active_width+x)+1]; + int gorig = (origimg[6*(y*width+x)+2]<<8)+origimg[6*(y*width+x)+3]; + int gnew = ( img[6*(y*active_width+x)+2]<<8)+ img[6*(y*active_width+x)+3]; + err=rorig-rnew; + MSER+=(err*err); + err=gorig-gnew; + MSEG+=(err*err); + } + else if(format==ETC2PACKAGE_R_NO_MIPMAPS) + { + int aorig = (((int)origalpha[2*(y*width+x)+0])<<8)+origalpha[2*(y*width+x)+1]; + int anew = (((int)alphaimg[2*(y*active_width+x)+0])<<8)+alphaimg[2*(y*active_width+x)+1]; + err=aorig-anew; + MSEA+=(err*err); + } + } + } + if(format == ETC2PACKAGE_RGBA1_NO_MIPMAPS || format == ETC2PACKAGE_sRGBA1_NO_MIPMAPS) + { + MSE = MSE / (1.0 * numpixels); + wMSE = wMSE / (1.0 * numpixels); + PSNR = 10*log((1.0*255*255)/MSE)/log(10.0); + wPSNR = 10*log((1.0*255*255)/wMSE)/log(10.0); + printf("PSNR only calculated on pixels where compressed alpha > 0\n"); + printf("color PSNR: %lf\nweighted PSNR: %lf\n",PSNR,wPSNR); + } + else if(format!=ETC2PACKAGE_R_NO_MIPMAPS&&format!=ETC2PACKAGE_RG_NO_MIPMAPS) + { + MSE = MSE / (active_width * active_height); + wMSE = wMSE / (active_width * active_height); + PSNR = 10*log((1.0*255*255)/MSE)/log(10.0); + wPSNR = 10*log((1.0*255*255)/wMSE)/log(10.0); + if(format == ETC2PACKAGE_RGBA_NO_MIPMAPS || format == ETC2PACKAGE_sRGBA_NO_MIPMAPS) + printf("PSNR only calculated on RGB, not on alpha\n"); + printf("color PSNR: %lf\nweighted PSNR: %lf\n",PSNR,wPSNR); + } + else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) + { + MSER = MSER / (active_width * active_height); + MSEG = MSEG / (active_width * active_height); + PSNRR = 10*log((1.0*65535*65535)/MSER)/log(10.0); + PSNRG = 10*log((1.0*65535*65535)/MSEG)/log(10.0); + printf("red PSNR: %lf\ngreen PSNR: %lf\n",PSNRR,PSNRG); + } + else if(format==ETC2PACKAGE_R_NO_MIPMAPS) + { + MSEA = MSEA / (active_width * active_height); + PSNRA = 10*log((1.0*65535.0*65535.0)/MSEA)/log(10.0); + printf("PSNR: %lf\n",PSNRA); + } + free(img); + return PSNR; +} + +//// Exhaustive code starts here. + +#if EXHAUSTIVE_CODE_ACTIVE +// Precomutes a table that is used when compressing a block exhaustively +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +inline unsigned int precompute_3bittable_all_subblocksRG_withtest_perceptual1000(uint8 *block,uint8 *avg_color, unsigned int *precalc_err_UL_R, unsigned int *precalc_err_UR_R, unsigned int *precalc_err_LL_R, unsigned int *precalc_err_LR_R,unsigned int *precalc_err_UL_RG, unsigned int *precalc_err_UR_RG, unsigned int *precalc_err_LL_RG, unsigned int *precalc_err_LR_RG, unsigned int best_err) +{ + int table; + int index; + int orig[3],approx[3][4]; + int x; + int intensity_modifier; + const int *table_indices; + + int good_enough_to_test; + unsigned int err[4]; + unsigned int err_this_table_upper; + unsigned int err_this_table_lower; + unsigned int err_this_table_left; + unsigned int err_this_table_right; + + // If the error in the red and green component is already larger than best_err for all 8 tables in + // all of upper, lower, left and right, this combination of red and green will never be used in + // the optimal color configuration. Therefore we can avoid testing all the blue colors for this + // combination. + good_enough_to_test = false; + + for(table=0;table<8;table++) // try all the 8 tables. + { + table_indices = &compressParamsFast[table*4]; + + intensity_modifier = table_indices[0]; + approx[1][0]=CLAMP(0, avg_color[1]+intensity_modifier,255); + intensity_modifier = table_indices[1]; + approx[1][1]=CLAMP(0, avg_color[1]+intensity_modifier,255); + intensity_modifier = table_indices[2]; + approx[1][2]=CLAMP(0, avg_color[1]+intensity_modifier,255); + intensity_modifier = table_indices[3]; + approx[1][3]=CLAMP(0, avg_color[1]+intensity_modifier,255); + + err_this_table_upper = 0; + err_this_table_lower = 0; + err_this_table_left = 0; + err_this_table_right = 0; + for(x=0; x<4; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + for(index=0;index<4;index++) + { + err[index] = precalc_err_UL_R[table*4*4+x*4+index] + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 * SQUARE(approx[1][index]-orig[1]); + precalc_err_UL_RG[table*4*4+x*4+index] = err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_upper+=err[0]; + err_this_table_left+=err[0]; + } + for(x=4; x<8; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + for(index=0;index<4;index++) + { + err[index] = precalc_err_UR_R[table*4*4+(x-4)*4+index] + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 * SQUARE(approx[1][index]-orig[1]); + precalc_err_UR_RG[table*4*4+(x-4)*4+index] = err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_upper+=err[0]; + err_this_table_right+=err[0]; + } + for(x=8; x<12; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + + for(index=0;index<4;index++) + { + err[index] = precalc_err_LL_R[table*4*4+(x-8)*4+index] + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 * SQUARE(approx[1][index]-orig[1]); + precalc_err_LL_RG[table*4*4+(x-8)*4+index] = err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_lower+=err[0]; + err_this_table_left+=err[0]; + } + for(x=12; x<16; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + + for(index=0;index<4;index++) + { + err[index] = precalc_err_LR_R[table*4*4+(x-12)*4+index] + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000 * SQUARE(approx[1][index]-orig[1]); + precalc_err_LR_RG[table*4*4+(x-12)*4+index] = err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_lower+=err[0]; + err_this_table_right+=err[0]; + } + if(err_this_table_upper < best_err) + good_enough_to_test = true; + if(err_this_table_lower < best_err) + good_enough_to_test = true; + if(err_this_table_left < best_err) + good_enough_to_test = true; + if(err_this_table_right < best_err) + good_enough_to_test = true; + } + return good_enough_to_test; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precomutes a table that is used when compressing a block exhaustively +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +inline int precompute_3bittable_all_subblocksRG_withtest(uint8 *block,uint8 *avg_color, unsigned int *precalc_err_UL_R, unsigned int *precalc_err_UR_R, unsigned int *precalc_err_LL_R, unsigned int *precalc_err_LR_R,unsigned int *precalc_err_UL_RG, unsigned int *precalc_err_UR_RG, unsigned int *precalc_err_LL_RG, unsigned int *precalc_err_LR_RG, unsigned int best_err) +{ + int table; + int index; + int orig[3],approx[3][4]; + int x; + int intensity_modifier; + const int *table_indices; + + int good_enough_to_test; + unsigned int err[4]; + unsigned int err_this_table_upper; + unsigned int err_this_table_lower; + unsigned int err_this_table_left; + unsigned int err_this_table_right; + + // If the error in the red and green component is already larger than best_err for all 8 tables in + // all of upper, lower, left and right, this combination of red and green will never be used in + // the optimal color configuration. Therefore we can avoid testing all the blue colors for this + // combination. + good_enough_to_test = false; + + for(table=0;table<8;table++) // try all the 8 tables. + { + table_indices = &compressParamsFast[table*4]; + + intensity_modifier = table_indices[0]; + approx[1][0]=CLAMP(0, avg_color[1]+intensity_modifier,255); + intensity_modifier = table_indices[1]; + approx[1][1]=CLAMP(0, avg_color[1]+intensity_modifier,255); + intensity_modifier = table_indices[2]; + approx[1][2]=CLAMP(0, avg_color[1]+intensity_modifier,255); + intensity_modifier = table_indices[3]; + approx[1][3]=CLAMP(0, avg_color[1]+intensity_modifier,255); + + err_this_table_upper = 0; + err_this_table_lower = 0; + err_this_table_left = 0; + err_this_table_right = 0; + for(x=0; x<4; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + for(index=0;index<4;index++) + { + err[index] = precalc_err_UL_R[table*4*4+x*4+index]+SQUARE(approx[1][index]-orig[1]); + precalc_err_UL_RG[table*4*4+x*4+index] = err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_upper+=err[0]; + err_this_table_left+=err[0]; + } + for(x=4; x<8; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + for(index=0;index<4;index++) + { + err[index] = precalc_err_UR_R[table*4*4+(x-4)*4+index]+SQUARE(approx[1][index]-orig[1]); + precalc_err_UR_RG[table*4*4+(x-4)*4+index] = err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_upper+=err[0]; + err_this_table_right+=err[0]; + } + for(x=8; x<12; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + + for(index=0;index<4;index++) + { + err[index] = precalc_err_LL_R[table*4*4+(x-8)*4+index]+SQUARE(approx[1][index]-orig[1]); + precalc_err_LL_RG[table*4*4+(x-8)*4+index] = err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_lower+=err[0]; + err_this_table_left+=err[0]; + } + for(x=12; x<16; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + + for(index=0;index<4;index++) + { + err[index] = precalc_err_LR_R[table*4*4+(x-12)*4+index]+SQUARE(approx[1][index]-orig[1]); + precalc_err_LR_RG[table*4*4+(x-12)*4+index] = err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_lower+=err[0]; + err_this_table_right+=err[0]; + } + if(err_this_table_upper < best_err) + good_enough_to_test = true; + if(err_this_table_lower < best_err) + good_enough_to_test = true; + if(err_this_table_left < best_err) + good_enough_to_test = true; + if(err_this_table_right < best_err) + good_enough_to_test = true; + } + return good_enough_to_test; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precomutes a table that is used when compressing a block exhaustively +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +inline unsigned int precompute_3bittable_all_subblocksR_with_test_perceptual1000(uint8 *block,uint8 *avg_color, unsigned int *precalc_err_UL_R, unsigned int *precalc_err_UR_R, unsigned int *precalc_err_LL_R, unsigned int *precalc_err_LR_R, unsigned int best_err) +{ + int table; + int index; + int orig[3],approx[3][4]; + int x; + int intensity_modifier; + const int *table_indices; + + unsigned int err[4]; + unsigned int err_this_table_upper; + unsigned int err_this_table_lower; + unsigned int err_this_table_left; + unsigned int err_this_table_right; + + int good_enough_to_test; + + good_enough_to_test = false; + + for(table=0;table<8;table++) // try all the 8 tables. + { + err_this_table_upper = 0; + err_this_table_lower = 0; + err_this_table_left = 0; + err_this_table_right = 0; + + table_indices = &compressParamsFast[table*4]; + + intensity_modifier = table_indices[0]; + approx[0][0]=CLAMP(0, avg_color[0]+intensity_modifier,255); + intensity_modifier = table_indices[1]; + approx[0][1]=CLAMP(0, avg_color[0]+intensity_modifier,255); + intensity_modifier = table_indices[2]; + approx[0][2]=CLAMP(0, avg_color[0]+intensity_modifier,255); + intensity_modifier = table_indices[3]; + approx[0][3]=CLAMP(0, avg_color[0]+intensity_modifier,255); + + for(x=0; x<4; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + for(index=0;index<4;index++) + { + err[index]=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(approx[0][index]-orig[0]); + precalc_err_UL_R[table*4*4+x*4+index]=err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_upper+=err[0]; + err_this_table_left+=err[0]; + } + for(x=4; x<8; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + for(index=0;index<4;index++) + { + err[index]=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(approx[0][index]-orig[0]); + precalc_err_UR_R[table*4*4+(x-4)*4+index]=err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_upper+=err[0]; + err_this_table_right+=err[0]; + } + for(x=8; x<12; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + + for(index=0;index<4;index++) + { + err[index]=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(approx[0][index]-orig[0]); + precalc_err_LL_R[table*4*4+(x-8)*4+index]=err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_lower+=err[0]; + err_this_table_left+=err[0]; + + } + for(x=12; x<16; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + + for(index=0;index<4;index++) + { + err[index]=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(approx[0][index]-orig[0]); + precalc_err_LR_R[table*4*4+(x-12)*4+index]=err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_lower+=err[0]; + err_this_table_right+=err[0]; + } + if(err_this_table_upper < best_err) + good_enough_to_test = true; + if(err_this_table_lower < best_err) + good_enough_to_test = true; + if(err_this_table_left < best_err) + good_enough_to_test = true; + if(err_this_table_right < best_err) + good_enough_to_test = true; + } + return good_enough_to_test; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precomutes a table that is used when compressing a block exhaustively +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +inline int precompute_3bittable_all_subblocksR_with_test(uint8 *block,uint8 *avg_color, unsigned int *precalc_err_UL_R, unsigned int *precalc_err_UR_R, unsigned int *precalc_err_LL_R, unsigned int *precalc_err_LR_R, unsigned int best_err) +{ + int table; + int index; + int orig[3],approx[3][4]; + int x; + int intensity_modifier; + const int *table_indices; + + unsigned int err[4]; + unsigned int err_this_table_upper; + unsigned int err_this_table_lower; + unsigned int err_this_table_left; + unsigned int err_this_table_right; + + int good_enough_to_test; + + good_enough_to_test = false; + + for(table=0;table<8;table++) // try all the 8 tables. + { + err_this_table_upper = 0; + err_this_table_lower = 0; + err_this_table_left = 0; + err_this_table_right = 0; + + table_indices = &compressParamsFast[table*4]; + + intensity_modifier = table_indices[0]; + approx[0][0]=CLAMP(0, avg_color[0]+intensity_modifier,255); + intensity_modifier = table_indices[1]; + approx[0][1]=CLAMP(0, avg_color[0]+intensity_modifier,255); + intensity_modifier = table_indices[2]; + approx[0][2]=CLAMP(0, avg_color[0]+intensity_modifier,255); + intensity_modifier = table_indices[3]; + approx[0][3]=CLAMP(0, avg_color[0]+intensity_modifier,255); + + for(x=0; x<4; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + for(index=0;index<4;index++) + { + err[index]=SQUARE(approx[0][index]-orig[0]); + precalc_err_UL_R[table*4*4+x*4+index]=err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_upper+=err[0]; + err_this_table_left+=err[0]; + } + for(x=4; x<8; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + for(index=0;index<4;index++) + { + err[index]=SQUARE(approx[0][index]-orig[0]); + precalc_err_UR_R[table*4*4+(x-4)*4+index]=err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_upper+=err[0]; + err_this_table_right+=err[0]; + } + for(x=8; x<12; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + + for(index=0;index<4;index++) + { + err[index]=SQUARE(approx[0][index]-orig[0]); + precalc_err_LL_R[table*4*4+(x-8)*4+index]=err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_lower+=err[0]; + err_this_table_left+=err[0]; + + } + for(x=12; x<16; x++) + { + orig[0]=block[x*4]; + orig[1]=block[x*4+1]; + orig[2]=block[x*4+2]; + + for(index=0;index<4;index++) + { + err[index]=SQUARE(approx[0][index]-orig[0]); + precalc_err_LR_R[table*4*4+(x-12)*4+index]=err[index]; + } + if(err[0] > err[1]) + err[0] = err[1]; + if(err[2] > err[3]) + err[2] = err[3]; + if(err[0] > err[2]) + err[0] = err[2]; + err_this_table_lower+=err[0]; + err_this_table_right+=err[0]; + } + if(err_this_table_upper < best_err) + good_enough_to_test = true; + if(err_this_table_lower < best_err) + good_enough_to_test = true; + if(err_this_table_left < best_err) + good_enough_to_test = true; + if(err_this_table_right < best_err) + good_enough_to_test = true; + } + return good_enough_to_test; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Tries all index-tables, used when compressing a block exhaustively +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +inline void tryalltables_3bittable_all_subblocks_using_precalc(uint8 *block_2x2,uint8 *color_quant1, unsigned int *precalc_err_UL_RG, unsigned int *precalc_err_UR_RG, unsigned int *precalc_err_LL_RG, unsigned int *precalc_err_LR_RG, unsigned int &err_upper, unsigned int &err_lower, unsigned int &err_left, unsigned int &err_right, unsigned int best_err) +{ + unsigned int err_this_table_upper; + unsigned int err_this_table_lower; + unsigned int err_this_table_left; + unsigned int err_this_table_right; + int orig[3],approx[4]; + int err[4]; + err_upper = 3*255*255*16; + err_lower = 3*255*255*16; + err_left = 3*255*255*16; + err_right = 3*255*255*16; + +#define ONE_PIXEL_UL(table_nbr,xx)\ + orig[0]=block_2x2[xx*4];\ + orig[1]=block_2x2[xx*4+1];\ + orig[2]=block_2x2[xx*4+2];\ + /* unrolled loop for(index=0;index<4;index++)*/\ + err[0]=precalc_err_UL_RG[table_nbr*4*4+xx*4+0] + square_table[approx[0]-orig[2]];\ + err[1]=precalc_err_UL_RG[table_nbr*4*4+xx*4+1] + square_table[approx[1]-orig[2]];\ + err[2]=precalc_err_UL_RG[table_nbr*4*4+xx*4+2] + square_table[approx[2]-orig[2]];\ + err[3]=precalc_err_UL_RG[table_nbr*4*4+xx*4+3] + square_table[approx[3]-orig[2]];\ + /* end unrolled loop*/\ + if(err[0] > err[1])\ + err[0] = err[1];\ + if(err[2] > err[3])\ + err[2] = err[3];\ + if(err[0] > err[2])\ + err[0] = err[2];\ + err_this_table_upper+=err[0];\ + err_this_table_left+=err[0];\ + +#define ONE_PIXEL_UR(table_nbr,xx)\ + orig[0]=block_2x2[xx*4];\ + orig[1]=block_2x2[xx*4+1];\ + orig[2]=block_2x2[xx*4+2];\ + /* unrolled loop for(index=0;index<4;index++)*/\ + err[0]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+0] + square_table[approx[0]-orig[2]];\ + err[1]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+1] + square_table[approx[1]-orig[2]];\ + err[2]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+2] + square_table[approx[2]-orig[2]];\ + err[3]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+3] + square_table[approx[3]-orig[2]];\ + /* end unrolled loop */\ + if(err[0] > err[1])\ + err[0] = err[1];\ + if(err[2] > err[3])\ + err[2] = err[3];\ + if(err[0] > err[2])\ + err[0] = err[2];\ + err_this_table_upper+=err[0];\ + err_this_table_right+=err[0]; + +#define ONE_PIXEL_LL(table_nbr,xx)\ + orig[0]=block_2x2[xx*4];\ + orig[1]=block_2x2[xx*4+1];\ + orig[2]=block_2x2[xx*4+2];\ + /* unrolled loop for(index=0;index<4;index++)*/\ + err[0]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+0] + square_table[approx[0]-orig[2]];\ + err[1]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+1] + square_table[approx[1]-orig[2]];\ + err[2]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+2] + square_table[approx[2]-orig[2]];\ + err[3]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+3] + square_table[approx[3]-orig[2]];\ + /* end unrolled loop*/\ + if(err[0] > err[1])\ + err[0] = err[1];\ + if(err[2] > err[3])\ + err[2] = err[3];\ + if(err[0] > err[2])\ + err[0] = err[2];\ + err_this_table_lower+=err[0];\ + err_this_table_left+=err[0];\ + +#define ONE_PIXEL_LR(table_nbr,xx)\ + orig[0]=block_2x2[xx*4];\ + orig[1]=block_2x2[xx*4+1];\ + orig[2]=block_2x2[xx*4+2];\ + /* unrolled loop for(index=0;index<4;index++)*/\ + err[0]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+0] + square_table[approx[0]-orig[2]];\ + err[1]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+1] + square_table[approx[1]-orig[2]];\ + err[2]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+2] + square_table[approx[2]-orig[2]];\ + err[3]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+3] + square_table[approx[3]-orig[2]];\ + /* end unrolled loop*/\ + if(err[0] > err[1])\ + err[0] = err[1];\ + if(err[2] > err[3])\ + err[2] = err[3];\ + if(err[0] > err[2])\ + err[0] = err[2];\ + err_this_table_lower+=err[0];\ + err_this_table_right+=err[0];\ + +#define ONE_TABLE_3(table_nbr)\ + err_this_table_upper = 0;\ + err_this_table_lower = 0;\ + err_this_table_left = 0;\ + err_this_table_right = 0;\ + approx[0]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+0]+255];\ + approx[1]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+1]+255];\ + approx[2]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+2]+255];\ + approx[3]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+3]+255];\ + /* unroll loop for(xx=0; xx<4; xx++) */\ + ONE_PIXEL_UL(table_nbr,0)\ + ONE_PIXEL_UL(table_nbr,1)\ + ONE_PIXEL_UL(table_nbr,2)\ + ONE_PIXEL_UL(table_nbr,3)\ + /* end unroll loop */\ + /* unroll loop for(xx=4; xx<8; xx++) */\ + ONE_PIXEL_LR(table_nbr,12)\ + ONE_PIXEL_LR(table_nbr,13)\ + ONE_PIXEL_LR(table_nbr,14)\ + ONE_PIXEL_LR(table_nbr,15)\ + /* end unroll loop */\ + /* If error in the top left 2x2 pixel area is already larger than the best error, and */\ + /* The same is true for the bottom right 2x2 pixel area, this combination of table and color */\ + /* can never be part of an optimal solution and therefore we do not need to test the other */\ + /* two 2x2 pixel areas */\ + if((err_this_table_upper err[1])\ + err[0] = err[1];\ + if(err[2] > err[3])\ + err[2] = err[3];\ + if(err[0] > err[2])\ + err[0] = err[2];\ + err_this_table_upper+=err[0];\ + err_this_table_left+=err[0];\ + +#define ONE_PIXEL_UR_PERCEP(table_nbr,xx)\ + orig[0]=block_2x2[xx*4];\ + orig[1]=block_2x2[xx*4+1];\ + orig[2]=block_2x2[xx*4+2];\ + /* unrolled loop for(index=0;index<4;index++)*/\ + err[0]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+0] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[0]-orig[2]];\ + err[1]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+1] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[1]-orig[2]];\ + err[2]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+2] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[2]-orig[2]];\ + err[3]=precalc_err_UR_RG[table_nbr*4*4+(xx-4)*4+3] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[3]-orig[2]];\ + /* end unrolled loop */\ + if(err[0] > err[1])\ + err[0] = err[1];\ + if(err[2] > err[3])\ + err[2] = err[3];\ + if(err[0] > err[2])\ + err[0] = err[2];\ + err_this_table_upper+=err[0];\ + err_this_table_right+=err[0]; + +#define ONE_PIXEL_LL_PERCEP(table_nbr,xx)\ + orig[0]=block_2x2[xx*4];\ + orig[1]=block_2x2[xx*4+1];\ + orig[2]=block_2x2[xx*4+2];\ + /* unrolled loop for(index=0;index<4;index++)*/\ + err[0]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+0] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[0]-orig[2]];\ + err[1]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+1] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[1]-orig[2]];\ + err[2]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+2] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[2]-orig[2]];\ + err[3]=precalc_err_LL_RG[table_nbr*4*4+(xx-8)*4+3] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[3]-orig[2]];\ + /* end unrolled loop*/\ + if(err[0] > err[1])\ + err[0] = err[1];\ + if(err[2] > err[3])\ + err[2] = err[3];\ + if(err[0] > err[2])\ + err[0] = err[2];\ + err_this_table_lower+=err[0];\ + err_this_table_left+=err[0];\ + +#define ONE_PIXEL_LR_PERCEP(table_nbr,xx)\ + orig[0]=block_2x2[xx*4];\ + orig[1]=block_2x2[xx*4+1];\ + orig[2]=block_2x2[xx*4+2];\ + /* unrolled loop for(index=0;index<4;index++)*/\ + err[0]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+0] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[0]-orig[2]];\ + err[1]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+1] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[1]-orig[2]];\ + err[2]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+2] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[2]-orig[2]];\ + err[3]=precalc_err_LR_RG[table_nbr*4*4+(xx-12)*4+3] + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[approx[3]-orig[2]];\ + /* end unrolled loop*/\ + if(err[0] > err[1])\ + err[0] = err[1];\ + if(err[2] > err[3])\ + err[2] = err[3];\ + if(err[0] > err[2])\ + err[0] = err[2];\ + err_this_table_lower+=err[0];\ + err_this_table_right+=err[0];\ + +#define ONE_TABLE_3_PERCEP(table_nbr)\ + err_this_table_upper = 0;\ + err_this_table_lower = 0;\ + err_this_table_left = 0;\ + err_this_table_right = 0;\ + approx[0]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+0]+255];\ + approx[1]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+1]+255];\ + approx[2]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+2]+255];\ + approx[3]=clamp_table_plus_255[color_quant1[2]+compressParamsFast[table_nbr*4+3]+255];\ + /* unroll loop for(xx=0; xx<4; xx++) */\ + ONE_PIXEL_UL_PERCEP(table_nbr,0)\ + ONE_PIXEL_UL_PERCEP(table_nbr,1)\ + ONE_PIXEL_UL_PERCEP(table_nbr,2)\ + ONE_PIXEL_UL_PERCEP(table_nbr,3)\ + /* end unroll loop */\ + /* unroll loop for(xx=4; xx<8; xx++) */\ + ONE_PIXEL_LR_PERCEP(table_nbr,12)\ + ONE_PIXEL_LR_PERCEP(table_nbr,13)\ + ONE_PIXEL_LR_PERCEP(table_nbr,14)\ + ONE_PIXEL_LR_PERCEP(table_nbr,15)\ + /* end unroll loop */\ + /* If error in the top left 2x2 pixel area is already larger than the best error, and */\ + /* The same is true for the bottom right 2x2 pixel area, this combination of table and color */\ + /* can never be part of an optimal solution and therefore we do not need to test the other */\ + /* two 2x2 pixel areas */\ + if((err_this_table_upper> 5; + bytediff[1] = bytediff[1] >> 5; + bytediff[2] = bytediff[2] >> 5; + best_enc_color2[0]= best_enc_color1[0] + bytediff[0]; + best_enc_color2[1]= best_enc_color1[1] + bytediff[1]; + best_enc_color2[2]= best_enc_color1[2] + bytediff[2]; + + // allocate memory for errors: + err_upper = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); + if(!err_upper){printf("Out of memory allocating \n");exit(1);} + err_lower = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); + if(!err_lower){printf("Out of memory allocating \n");exit(1);} + err_left = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); + if(!err_left){printf("Out of memory allocating \n");exit(1);} + err_right = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); + if(!err_right){printf("Out of memory allocating \n");exit(1);} + + int q; + // Calculate all errors + for(enc_color1[0]=0; enc_color1[0]<32; enc_color1[0]++) + { + color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + if(precompute_3bittable_all_subblocksR_with_test_perceptual1000(block_2x2, color_quant1, precalc_err_UL_R, precalc_err_UR_R, precalc_err_LL_R, precalc_err_LR_R, best_error_so_far)) + { + for(enc_color1[1]=0; enc_color1[1]<32; enc_color1[1]++) + { + color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + if(precompute_3bittable_all_subblocksRG_withtest_perceptual1000(block_2x2, color_quant1, precalc_err_UL_R, precalc_err_UR_R, precalc_err_LL_R, precalc_err_LR_R, precalc_err_UL_RG, precalc_err_UR_RG, precalc_err_LL_RG, precalc_err_LR_RG, best_error_so_far)) + { + for(enc_color1[2]=0; enc_color1[2]<32; enc_color1[2]++) + { + color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + tryalltables_3bittable_all_subblocks_using_precalc_perceptual1000(block_2x2, color_quant1, precalc_err_UL_RG, precalc_err_UR_RG, precalc_err_LL_RG, precalc_err_LR_RG, err_upper[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_lower[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_left[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_right[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], best_error_so_far); + } + } + else + { + for(q=0;q<32;q++) + { + err_upper[32*32*enc_color1[0]+32*enc_color1[1]+q] = MAXERR1000; + err_lower[32*32*enc_color1[0]+32*enc_color1[1]+q] = MAXERR1000; + err_left[32*32*enc_color1[0]+32*enc_color1[1]+q] = MAXERR1000; + err_right[32*32*enc_color1[0]+32*enc_color1[1]+q] = MAXERR1000; + } + } + } + } + else + { + for(q=0;q<32*32;q++) + { + err_upper[32*32*enc_color1[0]+q] = MAXERR1000; + err_lower[32*32*enc_color1[0]+q] = MAXERR1000; + err_left[32*32*enc_color1[0]+q] = MAXERR1000; + err_right[32*32*enc_color1[0]+q] = MAXERR1000; + } + } + } + for(enc_color1[0]=0; enc_color1[0]<32; enc_color1[0]++) + { + for(enc_color1[1]=0; enc_color1[1]<32; enc_color1[1]++) + { + for(enc_color1[2]=0; enc_color1[2]<4; enc_color1[2]++) + { + error_lying = err_upper[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]]; + error_standing = err_left[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]]; + if(error_lying < best_error_so_far || error_standing < best_error_so_far) + { + for(enc_color2[0]=JAS_MAX(0,enc_color1[0]-4); enc_color2[0]> 2); + color_quant1[1] = best_enc_color1[1] << 3 | (best_enc_color1[1] >> 2); + color_quant1[2] = best_enc_color1[2] << 3 | (best_enc_color1[2] >> 2); + if(best_flip == 0) + tryalltables_3bittable2x4percep1000(img,width,height,startx,starty,color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + else + tryalltables_3bittable4x2percep1000(img,width,height,startx,starty,color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + color_quant2[0] = best_enc_color2[0] << 3 | (best_enc_color2[0] >> 2); + color_quant2[1] = best_enc_color2[1] << 3 | (best_enc_color2[1] >> 2); + color_quant2[2] = best_enc_color2[2] << 3 | (best_enc_color2[2] >> 2); + if(best_flip == 0) + tryalltables_3bittable2x4percep1000(img,width,height,startx+2,starty,color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + else + tryalltables_3bittable4x2percep1000(img,width,height,startx,starty+2,color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + diff[0] = best_enc_color2[0]-best_enc_color1[0]; + diff[1] = best_enc_color2[1]-best_enc_color1[1]; + diff[2] = best_enc_color2[2]-best_enc_color1[2]; + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + diffbit = 1; + compressed1 = 0; + PUTBITSHIGH( compressed1, diffbit, 1, 33); + PUTBITSHIGH( compressed1, best_enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1, best_enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1, best_enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1, diff[0], 3, 58); + PUTBITSHIGH( compressed1, diff[1], 3, 50); + PUTBITSHIGH( compressed1, diff[2], 3, 42); + PUTBITSHIGH( compressed1, best_table1, 3, 39); + PUTBITSHIGH( compressed1, best_table2, 3, 36); + PUTBITSHIGH( compressed1, best_flip, 1, 32); + + if(best_flip == 0) + { + compressed2 = 0; + PUTBITS( compressed2, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2, (best_pixel_indices2_LSB ), 8, 15); + } + else + { + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + compressed2 = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + return best_error_using_diff_mode; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Compresses the differential mode exhaustively. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockDifferentialExhaustive(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int previous_best_err) +{ + unsigned int best_err_norm_diff = 255*255*16*3; + unsigned int best_err_norm_444 = 255*255*16*3; + unsigned int best_err_flip_diff = 255*255*16*3; + unsigned int best_err_flip_444 = 255*255*16*3; + uint8 color_quant1[3], color_quant2[3]; + + int enc_color1[3], enc_color2[3], diff[3]; + int best_enc_color1[3], best_enc_color2[3]; + + int min_error=255*255*8*3; + unsigned int best_pixel_indices1_MSB=0; + unsigned int best_pixel_indices1_LSB=0; + unsigned int best_pixel_indices2_MSB=0; + unsigned int best_pixel_indices2_LSB=0; + unsigned int pixel_indices1_MSB=0; + unsigned int pixel_indices1_LSB=0; + unsigned int pixel_indices2_MSB=0; + + unsigned int *err_upper, *err_lower; + unsigned int *err_left, *err_right; + + unsigned int pixel_indices2_LSB=0; + + unsigned int table1=0, table2=0; + unsigned int best_table1=0, best_table2=0; + + unsigned int precalc_err_UL_R[8*4*4]; + unsigned int precalc_err_UR_R[8*4*4]; + unsigned int precalc_err_LL_R[8*4*4]; + unsigned int precalc_err_LR_R[8*4*4]; + + unsigned int precalc_err_UL_RG[8*4*4]; + unsigned int precalc_err_UR_RG[8*4*4]; + unsigned int precalc_err_LL_RG[8*4*4]; + unsigned int precalc_err_LR_RG[8*4*4]; + + int diffbit; + uint8 block_2x2[4*4*4]; + + unsigned int error, error_lying, error_standing, best_err, total_best_err; + unsigned int *err_lower_adr; + int best_flip; + unsigned int *err_right_adr; + + int xx,yy,count = 0; + + // Reshuffle pixels so that the top left 2x2 pixels arrive first, then the top right 2x2 pixels etc. Also put use 4 bytes per pixel to make it 32-word aligned. + for(xx = 0; xx<2; xx++) + { + for(yy=0; yy<2; yy++) + { + block_2x2[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block_2x2[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block_2x2[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block_2x2[(count)*4+3] = 0; + count++; + } + } + for(xx = 2; xx<4; xx++) + { + for(yy=0; yy<2; yy++) + { + block_2x2[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block_2x2[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block_2x2[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block_2x2[(count)*4+3] = 0; + count++; + } + } + for(xx = 0; xx<2; xx++) + { + for(yy=2; yy<4; yy++) + { + block_2x2[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block_2x2[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block_2x2[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block_2x2[(count)*4+3] = 0; + count++; + } + } + for(xx = 2; xx<4; xx++) + { + for(yy=2; yy<4; yy++) + { + block_2x2[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block_2x2[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block_2x2[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block_2x2[(count)*4+3] = 0; + count++; + } + } + + + unsigned int test1, test2; + best_err = (unsigned int)compressBlockOnlyDiffFlipAverage(img, width, height, startx, starty, test1, test2, best_enc_color1, best_enc_color2, best_flip); + if(previous_best_err < best_err) + total_best_err = previous_best_err; + else + total_best_err = best_err; + + // allocate memory for errors: + err_upper = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); + if(!err_upper){printf("Out of memory allocating \n");exit(1);} + err_lower = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); + if(!err_lower){printf("Out of memory allocating \n");exit(1);} + err_left = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); + if(!err_left){printf("Out of memory allocating \n");exit(1);} + err_right = (unsigned int*) malloc(32*32*32*sizeof(unsigned int)); + if(!err_right){printf("Out of memory allocating \n");exit(1);} + + int q; + // Calculate all errors + for(enc_color1[0]=0; enc_color1[0]<32; enc_color1[0]++) + { + color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + if(precompute_3bittable_all_subblocksR_with_test(block_2x2, color_quant1, precalc_err_UL_R, precalc_err_UR_R, precalc_err_LL_R, precalc_err_LR_R, total_best_err)) + { + for(enc_color1[1]=0; enc_color1[1]<32; enc_color1[1]++) + { + color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + if(precompute_3bittable_all_subblocksRG_withtest(block_2x2, color_quant1, precalc_err_UL_R, precalc_err_UR_R, precalc_err_LL_R, precalc_err_LR_R, precalc_err_UL_RG, precalc_err_UR_RG, precalc_err_LL_RG, precalc_err_LR_RG, total_best_err)) + { + for(enc_color1[2]=0; enc_color1[2]<32; enc_color1[2]++) + { + color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + tryalltables_3bittable_all_subblocks_using_precalc(block_2x2, color_quant1, precalc_err_UL_RG, precalc_err_UR_RG, precalc_err_LL_RG, precalc_err_LR_RG, err_upper[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_lower[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_left[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], err_right[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]], total_best_err); + } + } + else + { + for(q=0;q<32;q++) + { + err_upper[32*32*enc_color1[0]+32*enc_color1[1]+q] = 255*255*16*3; + err_lower[32*32*enc_color1[0]+32*enc_color1[1]+q] = 255*255*16*3; + err_left[32*32*enc_color1[0]+32*enc_color1[1]+q] = 255*255*16*3; + err_right[32*32*enc_color1[0]+32*enc_color1[1]+q] = 255*255*16*3; + } + } + } + } + else + { + for(q=0;q<32*32;q++) + { + err_upper[32*32*enc_color1[0]+q] = 255*255*16*3; + err_lower[32*32*enc_color1[0]+q] = 255*255*16*3; + err_left[32*32*enc_color1[0]+q] = 255*255*16*3; + err_right[32*32*enc_color1[0]+q] = 255*255*16*3; + } + } + } + + for(enc_color1[0]=0; enc_color1[0]<32; enc_color1[0]++) + { + for(enc_color1[1]=0; enc_color1[1]<32; enc_color1[1]++) + { + for(enc_color1[2]=0; enc_color1[2]<4; enc_color1[2]++) + { + error_lying = err_upper[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]]; + error_standing = err_left[32*32*enc_color1[0]+32*enc_color1[1]+enc_color1[2]]; + if(error_lying < total_best_err || error_standing < total_best_err) + { + for(enc_color2[0]=JAS_MAX(0,enc_color1[0]-4); enc_color2[0]> 2); + color_quant1[1] = best_enc_color1[1] << 3 | (best_enc_color1[1] >> 2); + color_quant1[2] = best_enc_color1[2] << 3 | (best_enc_color1[2] >> 2); + if(best_flip == 0) + tryalltables_3bittable2x4(img,width,height,startx,starty,color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + else + tryalltables_3bittable4x2(img,width,height,startx,starty,color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + color_quant2[0] = best_enc_color2[0] << 3 | (best_enc_color2[0] >> 2); + color_quant2[1] = best_enc_color2[1] << 3 | (best_enc_color2[1] >> 2); + color_quant2[2] = best_enc_color2[2] << 3 | (best_enc_color2[2] >> 2); + if(best_flip == 0) + tryalltables_3bittable2x4(img,width,height,startx+2,starty,color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + else + tryalltables_3bittable4x2(img,width,height,startx,starty+2,color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + diff[0] = best_enc_color2[0]-best_enc_color1[0]; + diff[1] = best_enc_color2[1]-best_enc_color1[1]; + diff[2] = best_enc_color2[2]-best_enc_color1[2]; + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + diffbit = 1; + compressed1 = 0; + PUTBITSHIGH( compressed1, diffbit, 1, 33); + PUTBITSHIGH( compressed1, best_enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1, best_enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1, best_enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1, diff[0], 3, 58); + PUTBITSHIGH( compressed1, diff[1], 3, 50); + PUTBITSHIGH( compressed1, diff[2], 3, 42); + PUTBITSHIGH( compressed1, best_table1, 3, 39); + PUTBITSHIGH( compressed1, best_table2, 3, 36); + PUTBITSHIGH( compressed1, best_flip, 1, 32); + + if(best_flip == 0) + { + compressed2 = 0; + PUTBITS( compressed2, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2, (best_pixel_indices2_LSB ), 8, 15); + } + else + { + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + compressed2 = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + } + return best_err; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// This function uses real exhaustive search for the planar mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockPlanar57ExhaustivePerceptual(uint8 *img, int width,int height,int startx,int starty, unsigned int &compressed57_1, unsigned int &compressed57_2, unsigned int best_error_sofar, unsigned int best_error_planar_red, unsigned int best_error_planar_green, unsigned int best_error_planar_blue) +{ + int colorO_enc[3], colorH_enc[3], colorV_enc[3]; + int best_colorO_enc[3], best_colorH_enc[3], best_colorV_enc[3]; + + unsigned int error; + unsigned int best_error; + unsigned int lowest_possible_error; + unsigned int best_error_red_sofar; + unsigned int best_error_green_sofar; + unsigned int best_error_blue_sofar; + unsigned int BBBtable[128*128]; + unsigned int CCCtable[128*128]; + + uint8 block[4*4*4]; + + // Use 4 bytes per pixel to make it 32-word aligned. + int count = 0; + int xx, yy; + for(yy=0; yy<4; yy++) + { + for(xx = 0; xx<4; xx++) + { + block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block[(count)*4+3] = 0; + count++; + } + } + + // The task is to calculate the sum of the error over the entire area of the block. + // + // The block can be partitioned into: O A A A + // B D D C + // B D C D + // B C D D + // where the error in + // O only depends on colorO + // A only depends on colorO and colorH + // B only depends on colorO and colorV + // C only depends on colorH and colorV + // D depends on all three (colorO, colorH and colorV) + // + // Note that B can be precalculated for all combinations of colorO and colorV + // and the precalculated values can be used instead of calculating it in the inner loop. + // The same applies to C. + // + // In the code below, the squared error over O A A A is calculated and stored in lowest_possible_error + + // Precalc BBB errors + for(colorO_enc[0] = 0; colorO_enc[0]<64; colorO_enc[0]++) + { + for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) + { + BBBtable[colorO_enc[0]*64+colorV_enc[0]] = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*calcBBBred(block, colorO_enc[0], colorV_enc[0]); + } + } + // Precalc CCC errors + for(colorH_enc[0] = 0; colorH_enc[0]<64; colorH_enc[0]++) + { + for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) + { + CCCtable[colorH_enc[0]*64+colorV_enc[0]] = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*calcCCCred(block, colorH_enc[0], colorV_enc[0]); + } + } + best_error = MAXERR1000; + + best_error_red_sofar = JAS_MIN(best_error_planar_red, best_error_sofar); + for(colorO_enc[0] = 0; colorO_enc[0]<64; colorO_enc[0]++) + { + for(colorH_enc[0] = 0; colorH_enc[0]<64; colorH_enc[0]++) + { + lowest_possible_error = calcLowestPossibleRedOHperceptual(block, colorO_enc[0], colorH_enc[0], best_error_red_sofar); + if(lowest_possible_error <= best_error_red_sofar) + { + for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) + { + error = calcErrorPlanarOnlyRedPerceptual(block, colorO_enc[0], colorH_enc[0], colorV_enc[0], lowest_possible_error, BBBtable[colorO_enc[0]*64+colorV_enc[0]], CCCtable[colorH_enc[0]*64+colorV_enc[0]], best_error_red_sofar); + if(error < best_error) + { + best_error = error; + best_colorO_enc[0] = colorO_enc[0]; + best_colorH_enc[0] = colorH_enc[0]; + best_colorV_enc[0] = colorV_enc[0]; + } + } + } + } + } + + if(best_error < best_error_planar_red) + best_error_planar_red = best_error; + + if(best_error_planar_red > best_error_sofar) + { + // The red component in itself is already bigger than the previously best value ---- we can give up. + // use the dummy color black for all colors and report that the errors for the different color components are infinite + best_error_planar_green = MAXERR1000; + best_error_planar_blue = MAXERR1000; + compressed57_1 = 0; + compressed57_2 = 0; + return; + } + + // The task is to calculate the sum of the error over the entire area of the block. + // + // The block can be partitioned into: O A A A + // B D D C + // B D C D + // B C D D + // where the error in + // O only depends on colorO + // A only depends on colorO and colorH + // B only depends on colorO and colorV + // C only depends on colorH and colorV + // D depends on all three (colorO, colorH and colorV) + // + // Note that B can be precalculated for all combinations of colorO and colorV + // and the precalculated values can be used instead of calculating it in the inner loop. + // The same applies to C. + // + // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error + + // Precalc BBB errors + for(colorO_enc[1] = 0; colorO_enc[1]<128; colorO_enc[1]++) + { + for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) + { + BBBtable[colorO_enc[1]*128+colorV_enc[1]] = PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*calcBBBgreen(block, colorO_enc[1], colorV_enc[1]); + } + } + // Precalc CCC errors + for(colorH_enc[1] = 0; colorH_enc[1]<128; colorH_enc[1]++) + { + for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) + { + CCCtable[colorH_enc[1]*128+colorV_enc[1]] = PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*calcCCCgreen(block, colorH_enc[1], colorV_enc[1]); + } + } + best_error = MAXERR1000; + best_error_green_sofar = JAS_MIN(best_error_planar_green, best_error_sofar); + for(colorO_enc[1] = 0; colorO_enc[1]<128; colorO_enc[1]++) + { + for(colorH_enc[1] = 0; colorH_enc[1]<128; colorH_enc[1]++) + { + lowest_possible_error = calcLowestPossibleGreenOHperceptual(block, colorO_enc[1], colorH_enc[1], best_error_green_sofar); + if(lowest_possible_error <= best_error_green_sofar) + { + for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) + { + error = calcErrorPlanarOnlyGreenPerceptual(block, colorO_enc[1], colorH_enc[1], colorV_enc[1], lowest_possible_error, BBBtable[colorO_enc[1]*128+colorV_enc[1]], CCCtable[colorH_enc[1]*128+colorV_enc[1]], best_error_green_sofar); + if(error < best_error) + { + best_error = error; + best_colorO_enc[1] = colorO_enc[1]; + best_colorH_enc[1] = colorH_enc[1]; + best_colorV_enc[1] = colorV_enc[1]; + } + } + } + } + } + + if(best_error < best_error_planar_green) + best_error_planar_green = best_error; + + if(best_error_planar_red + best_error_planar_green > best_error_sofar) + { + // The red component in itself is already bigger than the previously best value ---- we can give up. + // use the dummy color black for all colors and report that the errors for the different color components are infinite + best_error_planar_blue = MAXERR1000; + compressed57_1 = 0; + compressed57_2 = 0; + return; + } + + // The task is to calculate the sum of the error over the entire area of the block. + // + // The block can be partitioned into: O A A A + // B D D C + // B D C D + // B C D D + // where the error in + // O only depends on colorO + // A only depends on colorO and colorH + // B only depends on colorO and colorV + // C only depends on colorH and colorV + // D depends on all three (colorO, colorH and colorV) + // + // Note that B can be precalculated for all combinations of colorO and colorV + // and the precalculated values can be used instead of calculating it in the inner loop. + // The same applies to C. + // + // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error + + // Precalc BBB errors + for(colorO_enc[2] = 0; colorO_enc[2]<64; colorO_enc[2]++) + { + for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) + { + BBBtable[colorO_enc[2]*64+colorV_enc[2]] = calcBBBbluePerceptual(block, colorO_enc[2], colorV_enc[2]); + } + } + // Precalc CCC errors + for(colorH_enc[2] = 0; colorH_enc[2]<64; colorH_enc[2]++) + { + for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) + { + CCCtable[colorH_enc[2]*64+colorV_enc[2]] = calcCCCbluePerceptual(block, colorH_enc[2], colorV_enc[2]); + } + } + best_error = MAXERR1000; + best_error_blue_sofar = JAS_MIN(best_error_planar_blue, best_error_sofar); + for(colorO_enc[2] = 0; colorO_enc[2]<64; colorO_enc[2]++) + { + for(colorH_enc[2] = 0; colorH_enc[2]<64; colorH_enc[2]++) + { + lowest_possible_error = calcLowestPossibleBlueOHperceptual(block, colorO_enc[2], colorH_enc[2], best_error_blue_sofar); + if(lowest_possible_error <= best_error_blue_sofar) + { + for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) + { + error = calcErrorPlanarOnlyBluePerceptual(block, colorO_enc[2], colorH_enc[2], colorV_enc[2], lowest_possible_error, BBBtable[colorO_enc[2]*64+colorV_enc[2]], CCCtable[colorH_enc[2]*64+colorV_enc[2]], best_error_blue_sofar); + if(error < best_error) + { + best_error = error; + best_colorO_enc[2] = colorO_enc[2]; + best_colorH_enc[2] = colorH_enc[2]; + best_colorV_enc[2] = colorV_enc[2]; + } + } + } + } + } + + if(best_error < best_error_planar_blue) + best_error_planar_blue = best_error; + + compressed57_1 = 0; + compressed57_2 = 0; + PUTBITSHIGH( compressed57_1, best_colorO_enc[0], 6, 63); + PUTBITSHIGH( compressed57_1, best_colorO_enc[1], 7, 57); + PUTBITSHIGH( compressed57_1, best_colorO_enc[2], 6, 50); + PUTBITSHIGH( compressed57_1, best_colorH_enc[0], 6, 44); + PUTBITSHIGH( compressed57_1, best_colorH_enc[1], 7, 38); + PUTBITS( compressed57_2, best_colorH_enc[2], 6, 31); + PUTBITS( compressed57_2, best_colorV_enc[0], 6, 25); + PUTBITS( compressed57_2, best_colorV_enc[1], 7, 19); + PUTBITS( compressed57_2, best_colorV_enc[2], 6, 12); + +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// This function uses real exhaustive search for the planar mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockPlanar57Exhaustive(uint8 *img, int width,int height,int startx,int starty, unsigned int &compressed57_1, unsigned int &compressed57_2, unsigned int best_error_sofar, unsigned int best_error_red, unsigned int best_error_green, unsigned int best_error_blue) +{ + int colorO_enc[3], colorH_enc[3], colorV_enc[3]; + int best_colorO_enc[3], best_colorH_enc[3], best_colorV_enc[3]; + + unsigned int error; + unsigned int best_error; + unsigned int lowest_possible_error; + unsigned int best_error_red_sofar; + unsigned int best_error_green_sofar; + unsigned int best_error_blue_sofar; + unsigned int BBBtable[128*128]; + unsigned int CCCtable[128*128]; + + uint8 block[4*4*4]; + + // Use 4 bytes per pixel to make it 32-word aligned. + int count = 0; + int xx, yy; + for(yy=0; yy<4; yy++) + { + for(xx = 0; xx<4; xx++) + { + block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block[(count)*4+3] = 0; + count++; + } + } + + // The task is to calculate the sum of the error over the entire area of the block. + // + // The block can be partitioned into: O A A A + // B D D C + // B D C D + // B C D D + // where the error in + // O only depends on colorO + // A only depends on colorO and colorH + // B only depends on colorO and colorV + // C only depends on colorH and colorV + // D depends on all three (colorO, colorH and colorV) + // + // Note that B can be precalculated for all combinations of colorO and colorV + // and the precalculated values can be used instead of calculating it in the inner loop. + // The same applies to C. + // + // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error + + // Precalc BBB errors + for(colorO_enc[0] = 0; colorO_enc[0]<64; colorO_enc[0]++) + { + for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) + { + BBBtable[colorO_enc[0]*64+colorV_enc[0]] = calcBBBred(block, colorO_enc[0], colorV_enc[0]); + } + } + // Precalc CCC errors + for(colorH_enc[0] = 0; colorH_enc[0]<64; colorH_enc[0]++) + { + for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) + { + CCCtable[colorH_enc[0]*64+colorV_enc[0]] = calcCCCred(block, colorH_enc[0], colorV_enc[0]); + } + } + best_error = MAXERR1000; + best_error_red_sofar = JAS_MIN(best_error_red, best_error_sofar); + for(colorO_enc[0] = 0; colorO_enc[0]<64; colorO_enc[0]++) + { + for(colorH_enc[0] = 0; colorH_enc[0]<64; colorH_enc[0]++) + { + lowest_possible_error = calcLowestPossibleRedOH(block, colorO_enc[0], colorH_enc[0], best_error_red_sofar); + if(lowest_possible_error <= best_error_red_sofar) + { + for(colorV_enc[0] = 0; colorV_enc[0]<64; colorV_enc[0]++) + { + error = calcErrorPlanarOnlyRed(block, colorO_enc[0], colorH_enc[0], colorV_enc[0], lowest_possible_error, BBBtable[colorO_enc[0]*64+colorV_enc[0]], CCCtable[colorH_enc[0]*64+colorV_enc[0]], best_error_red_sofar); + if(error < best_error) + { + best_error = error; + best_colorO_enc[0] = colorO_enc[0]; + best_colorH_enc[0] = colorH_enc[0]; + best_colorV_enc[0] = colorV_enc[0]; + } + } + } + } + } + + // The task is to calculate the sum of the error over the entire area of the block. + // + // The block can be partitioned into: O A A A + // B D D C + // B D C D + // B C D D + // where the error in + // O only depends on colorO + // A only depends on colorO and colorH + // B only depends on colorO and colorV + // C only depends on colorH and colorV + // D depends on all three (colorO, colorH and colorV) + // + // Note that B can be precalculated for all combinations of colorO and colorV + // and the precalculated values can be used instead of calculating it in the inner loop. + // The same applies to C. + // + // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error + + // Precalc BBB errors + for(colorO_enc[1] = 0; colorO_enc[1]<128; colorO_enc[1]++) + { + for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) + { + BBBtable[colorO_enc[1]*128+colorV_enc[1]] = calcBBBgreen(block, colorO_enc[1], colorV_enc[1]); + } + } + // Precalc CCC errors + for(colorH_enc[1] = 0; colorH_enc[1]<128; colorH_enc[1]++) + { + for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) + { + CCCtable[colorH_enc[1]*128+colorV_enc[1]] = calcCCCgreen(block, colorH_enc[1], colorV_enc[1]); + } + } + best_error = MAXERR1000; + best_error_green_sofar = JAS_MIN(best_error_green, best_error_sofar); + for(colorO_enc[1] = 0; colorO_enc[1]<128; colorO_enc[1]++) + { + for(colorH_enc[1] = 0; colorH_enc[1]<128; colorH_enc[1]++) + { + lowest_possible_error = calcLowestPossibleGreenOH(block, colorO_enc[1], colorH_enc[1], best_error_green_sofar); + if(lowest_possible_error <= best_error_green_sofar) + { + for(colorV_enc[1] = 0; colorV_enc[1]<128; colorV_enc[1]++) + { + error = calcErrorPlanarOnlyGreen(block, colorO_enc[1], colorH_enc[1], colorV_enc[1], lowest_possible_error, BBBtable[colorO_enc[1]*128+colorV_enc[1]], CCCtable[colorH_enc[1]*128+colorV_enc[1]], best_error_green_sofar); + if(error < best_error) + { + best_error = error; + best_colorO_enc[1] = colorO_enc[1]; + best_colorH_enc[1] = colorH_enc[1]; + best_colorV_enc[1] = colorV_enc[1]; + } + } + } + } + } + + // The task is to calculate the sum of the error over the entire area of the block. + // + // The block can be partitioned into: O A A A + // B D D C + // B D C D + // B C D D + // where the error in + // O only depends on colorO + // A only depends on colorO and colorH + // B only depends on colorO and colorV + // C only depends on colorH and colorV + // D depends on all three (colorO, colorH and colorV) + // + // Note that B can be precalculated for all combinations of colorO and colorV + // and the precalculated values can be used instead of calculating it in the inner loop. + // The same applies to C. + // + // In the code below, the squared error over O A A A is calculated and store in lowest_possible_error + + // Precalc BBB errors + for(colorO_enc[2] = 0; colorO_enc[2]<64; colorO_enc[2]++) + { + for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) + { + BBBtable[colorO_enc[2]*64+colorV_enc[2]] = calcBBBblue(block, colorO_enc[2], colorV_enc[2]); + } + } + // Precalc CCC errors + for(colorH_enc[2] = 0; colorH_enc[2]<64; colorH_enc[2]++) + { + for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) + { + CCCtable[colorH_enc[2]*64+colorV_enc[2]] = calcCCCblue(block, colorH_enc[2], colorV_enc[2]); + } + } + best_error = MAXERR1000; + best_error_blue_sofar = JAS_MIN(best_error_blue, best_error_sofar); + for(colorO_enc[2] = 0; colorO_enc[2]<64; colorO_enc[2]++) + { + for(colorH_enc[2] = 0; colorH_enc[2]<64; colorH_enc[2]++) + { + lowest_possible_error = calcLowestPossibleBlueOH(block, colorO_enc[2], colorH_enc[2], best_error_blue_sofar); + if(lowest_possible_error <= best_error_blue_sofar) + { + for(colorV_enc[2] = 0; colorV_enc[2]<64; colorV_enc[2]++) + { + error = calcErrorPlanarOnlyBlue(block, colorO_enc[2], colorH_enc[2], colorV_enc[2], lowest_possible_error, BBBtable[colorO_enc[2]*64+colorV_enc[2]], CCCtable[colorH_enc[2]*64+colorV_enc[2]], best_error_blue_sofar); + if(error < best_error) + { + best_error = error; + best_colorO_enc[2] = colorO_enc[2]; + best_colorH_enc[2] = colorH_enc[2]; + best_colorV_enc[2] = colorV_enc[2]; + } + } + } + } + } + + compressed57_1 = 0; + compressed57_2 = 0; + PUTBITSHIGH( compressed57_1, best_colorO_enc[0], 6, 63); + PUTBITSHIGH( compressed57_1, best_colorO_enc[1], 7, 57); + PUTBITSHIGH( compressed57_1, best_colorO_enc[2], 6, 50); + PUTBITSHIGH( compressed57_1, best_colorH_enc[0], 6, 44); + PUTBITSHIGH( compressed57_1, best_colorH_enc[1], 7, 38); + PUTBITS( compressed57_2, best_colorH_enc[2], 6, 31); + PUTBITS( compressed57_2, best_colorV_enc[0], 6, 25); + PUTBITS( compressed57_2, best_colorV_enc[1], 7, 19); + PUTBITS( compressed57_2, best_colorV_enc[2], 6, 12); + +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col0_Rpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_R) +{ + unsigned int block_error = 0, + best_block_error = MAXERR1000, + pixel_error, + best_pixel_error; + int diff; + uint8 color; + uint8 possible_colors[3]; + + color = ((colorRGB444_packed >> 8) & 0xf)*17; + + // Test all distances + for (uint8 d = 0; d < 8; d++) + { + + possible_colors[0] = CLAMP(0,color - table59T[d],255); + possible_colors[1] = CLAMP(0,color,255); + possible_colors[2] = CLAMP(0,color + table59T[d],255); + + // Loop block + for (int x = 0; x < 16; x++) + { + best_pixel_error = MAXERR1000; + + // Loop possible block colors + for (uint8 c = 0; c < 3; c++) + { + + diff = block[4*x + R] - CLAMP(0,possible_colors[c],255); + + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff); + + // Choose best error + if (pixel_error < best_pixel_error) + best_pixel_error = pixel_error; + } + + precalc_err_col0_R[((colorRGB444_packed>>8)*8 + d)*16 + x] = (unsigned int) best_pixel_error; + } + + } + +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col0_R(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_R) +{ + unsigned int block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + int diff; + uint8 color; + uint8 possible_colors[3]; + + color = ((colorRGB444_packed >> 8) & 0xf)*17; + + // Test all distances + for (uint8 d = 0; d < 8; d++) + { + + possible_colors[0] = CLAMP(0,color - table59T[d],255); + possible_colors[1] = CLAMP(0,color,255); + possible_colors[2] = CLAMP(0,color + table59T[d],255); + + // Loop block + for (int x = 0; x < 16; x++) + { + best_pixel_error = MAXIMUM_ERROR; + + // Loop possible block colors + for (uint8 c = 0; c < 3; c++) + { + + diff = block[4*x + R] - CLAMP(0,possible_colors[c],255); + + pixel_error = SQUARE(diff); + + // Choose best error + if (pixel_error < best_pixel_error) + best_pixel_error = pixel_error; + } + precalc_err_col0_R[((colorRGB444_packed>>8)*8 + d)*16 + x] = (unsigned int) best_pixel_error; + } + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col0_RGpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_RG) +{ + unsigned int block_error = 0, + best_block_error = MAXERR1000, + pixel_error, + best_pixel_error; + int diff[3]; + uint8 color[3]; + uint8 possible_colors[3][2]; + + color[R] = ((colorRGB444_packed >> 8) & 0xf)*17; + color[G] = ((colorRGB444_packed >> 4) & 0xf)*17; + + // Test all distances + for (uint8 d = 0; d < 8; d++) + { + + possible_colors[0][R] = CLAMP(0,color[R] - table59T[d],255); + possible_colors[0][G] = CLAMP(0,color[G] - table59T[d],255); + + possible_colors[1][R] = CLAMP(0,color[R],255); + possible_colors[1][G] = CLAMP(0,color[G],255); + + possible_colors[2][R] = CLAMP(0,color[R] + table59T[d],255); + possible_colors[2][G] = CLAMP(0,color[G] + table59T[d],255); + + + + // Loop block + for (int x = 0; x < 16; x++) + { + best_pixel_error = MAXERR1000; + + // Loop possible block colors + for (uint8 c = 0; c < 3; c++) + { + + diff[R] = block[4*x + R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = block[4*x + G] - CLAMP(0,possible_colors[c][G],255); + + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]); + + // Choose best error + if (pixel_error < best_pixel_error) + best_pixel_error = pixel_error; + } + precalc_err_col0_RG[((colorRGB444_packed>>4)*8 + d)*16 + x] = (unsigned int) best_pixel_error; + } + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col0_RG(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_RG) +{ + unsigned int block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + int diff[3]; + uint8 color[3]; + uint8 possible_colors[3][2]; + + color[R] = ((colorRGB444_packed >> 8) & 0xf)*17; + color[G] = ((colorRGB444_packed >> 4) & 0xf)*17; + + // Test all distances + for (uint8 d = 0; d < 8; d++) + { + + possible_colors[0][R] = CLAMP(0,color[R] - table59T[d],255); + possible_colors[0][G] = CLAMP(0,color[G] - table59T[d],255); + + possible_colors[1][R] = CLAMP(0,color[R],255); + possible_colors[1][G] = CLAMP(0,color[G],255); + + possible_colors[2][R] = CLAMP(0,color[R] + table59T[d],255); + possible_colors[2][G] = CLAMP(0,color[G] + table59T[d],255); + + // Loop block + for (int x = 0; x < 16; x++) + { + best_pixel_error = MAXIMUM_ERROR; + + // Loop possible block colors + for (uint8 c = 0; c < 3; c++) + { + diff[R] = block[4*x + R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = block[4*x + G] - CLAMP(0,possible_colors[c][G],255); + + pixel_error = SQUARE(diff[R]) + SQUARE(diff[G]); + + // Choose best error + if (pixel_error < best_pixel_error) + best_pixel_error = pixel_error; + } + precalc_err_col0_RG[((colorRGB444_packed>>4)*8 + d)*16 + x] = (unsigned int) best_pixel_error; + } + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col1_Rpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_R) +{ + unsigned int pixel_error; + int diff; + uint8 color; + + color = ((colorRGB444_packed >> 8) & 0xf)*17; + + // Loop block + for (int x = 0; x < 16; x++) + { + diff = block[4*x + R] - color; + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff); + precalc_err_col1_R[((colorRGB444_packed>>8))*16 + x] = (unsigned int) pixel_error; + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +/** + * Calculate the error for the block at position (startx,starty) + * The parameters needed for reconstruction is calculated as well + * + * In the 59T bit mode, we only have pattern T. + */ +void precalcError59T_col1_R(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_R) +{ + unsigned int pixel_error; + int diff; + uint8 color; + + color = ((colorRGB444_packed >> 8) & 0xf)*17; + + // Loop block + for (int x = 0; x < 16; x++) + { + diff = block[4*x + R] - color; + pixel_error = SQUARE(diff); + precalc_err_col1_R[((colorRGB444_packed>>8))*16 + x] = (unsigned int) pixel_error; + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col1_RGpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_RG) +{ + unsigned int pixel_error; + int diff[3]; + uint8 color[2]; + + color[R] = ((colorRGB444_packed >> 8) & 0xf)*17; + color[G] = ((colorRGB444_packed >> 4) & 0xf)*17; + + // Loop block + for (int x = 0; x < 16; x++) + { + diff[R] = block[4*x + R] - color[R]; + diff[G] = block[4*x + G] - color[G]; + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]); + precalc_err_col1_RG[((colorRGB444_packed>>4))*16 + x] = (unsigned int) pixel_error; + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col1_RG(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_RG) +{ + unsigned int pixel_error; + int diff[3]; + uint8 color[2]; + + color[R] = ((colorRGB444_packed >> 8) & 0xf)*17; + color[G] = ((colorRGB444_packed >> 4) & 0xf)*17; + + // Loop block + for (int x = 0; x < 16; x++) + { + diff[R] = block[4*x + R] - color[R]; + diff[G] = block[4*x + G] - color[G]; + pixel_error = SQUARE(diff[R]) + SQUARE(diff[G]); + precalc_err_col1_RG[((colorRGB444_packed>>4))*16 + x] = (unsigned int) pixel_error; + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col0_RGBpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_RGB) +{ + unsigned int block_error = 0, + best_block_error = MAXERR1000, + pixel_error, + best_pixel_error; + uint8 color[3]; + int possible_colors[3][3]; + unsigned int *precalc_err_col0_RGB_adr; + +#define ONEPOINT59RGB_PERCEP(xval) \ + /* Loop possible block colors */\ + /* unroll loop for (uint8 c = 0; c < 3; c++) */\ + {\ + best_pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[block[4*xval + R] - possible_colors[0][R]]\ + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[block[4*xval + G] - possible_colors[0][G]] \ + + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[block[4*xval + B] - possible_colors[0][B]];\ + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[block[4*xval + R] - possible_colors[1][R]]\ + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[block[4*xval + G] - possible_colors[1][G]]\ + + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[block[4*xval + B] - possible_colors[1][B]];\ + if (pixel_error < best_pixel_error)\ + best_pixel_error = pixel_error;\ + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[block[4*xval + R] - possible_colors[2][R]]\ + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[block[4*xval + G] - possible_colors[2][G]]\ + + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[block[4*xval + B] - possible_colors[2][B]];\ + if (pixel_error < best_pixel_error)\ + best_pixel_error = pixel_error;\ + }\ + precalc_err_col0_RGB_adr[xval] = (unsigned int) best_pixel_error;\ + +#define ONETABLE59RGB_PERCEP(dval) \ + possible_colors[0][R] = clamp_table[color[R] - table59T[dval]+255]-255;\ + possible_colors[0][G] = clamp_table[color[G] - table59T[dval]+255]-255;\ + possible_colors[0][B] = clamp_table[color[B] - table59T[dval]+255]-255;\ + possible_colors[1][R] = color[R]-255;\ + possible_colors[1][G] = color[G]-255;\ + possible_colors[1][B] = color[B]-255;\ + possible_colors[2][R] = clamp_table[color[R] + table59T[dval]+255]-255;\ + possible_colors[2][G] = clamp_table[color[G] + table59T[dval]+255]-255;\ + possible_colors[2][B] = clamp_table[color[B] + table59T[dval]+255]-255;\ + precalc_err_col0_RGB_adr = &precalc_err_col0_RGB[(colorRGB444_packed*8 + dval)*16];\ + /* Loop block */\ + /* unroll loop for (int x = 0; x < 16; x++) */\ + {\ + ONEPOINT59RGB_PERCEP(0)\ + ONEPOINT59RGB_PERCEP(1)\ + ONEPOINT59RGB_PERCEP(2)\ + ONEPOINT59RGB_PERCEP(3)\ + ONEPOINT59RGB_PERCEP(4)\ + ONEPOINT59RGB_PERCEP(5)\ + ONEPOINT59RGB_PERCEP(6)\ + ONEPOINT59RGB_PERCEP(7)\ + ONEPOINT59RGB_PERCEP(8)\ + ONEPOINT59RGB_PERCEP(9)\ + ONEPOINT59RGB_PERCEP(10)\ + ONEPOINT59RGB_PERCEP(11)\ + ONEPOINT59RGB_PERCEP(12)\ + ONEPOINT59RGB_PERCEP(13)\ + ONEPOINT59RGB_PERCEP(14)\ + ONEPOINT59RGB_PERCEP(15)\ + }\ + + color[R] = (((colorRGB444_packed >> 8) ) << 4) | ((colorRGB444_packed >> 8) ) ; + color[G] = (((colorRGB444_packed >> 4) & 0xf) << 4) | ((colorRGB444_packed >> 4) & 0xf) ; + color[B] = (((colorRGB444_packed) & 0xf) << 4) | ((colorRGB444_packed) & 0xf) ; + + /* Test all distances */ + /* unroll loop for (uint8 d = 0; d < 8; ++d) */ + { + ONETABLE59RGB_PERCEP(0) + ONETABLE59RGB_PERCEP(1) + ONETABLE59RGB_PERCEP(2) + ONETABLE59RGB_PERCEP(3) + ONETABLE59RGB_PERCEP(4) + ONETABLE59RGB_PERCEP(5) + ONETABLE59RGB_PERCEP(6) + ONETABLE59RGB_PERCEP(7) + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col0_RGB(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col0_RGB) +{ + unsigned int block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + uint8 color[3]; + int possible_colors[3][3]; + unsigned int *precalc_err_col0_RGB_adr; + +#define ONEPOINT59RGB(xval) \ + /* Loop possible block colors */\ + /* unroll loop for (uint8 c = 0; c < 3; c++) */\ + {\ + best_pixel_error = square_table[block[4*xval + R] - possible_colors[0][R]]\ + + square_table[block[4*xval + G] - possible_colors[0][G]] \ + + square_table[block[4*xval + B] - possible_colors[0][B]];\ + pixel_error = square_table[block[4*xval + R] - possible_colors[1][R]]\ + + square_table[block[4*xval + G] - possible_colors[1][G]]\ + + square_table[block[4*xval + B] - possible_colors[1][B]];\ + if (pixel_error < best_pixel_error)\ + best_pixel_error = pixel_error;\ + pixel_error = square_table[block[4*xval + R] - possible_colors[2][R]]\ + + square_table[block[4*xval + G] - possible_colors[2][G]]\ + + square_table[block[4*xval + B] - possible_colors[2][B]];\ + if (pixel_error < best_pixel_error)\ + best_pixel_error = pixel_error;\ + }\ + precalc_err_col0_RGB_adr[xval] = (unsigned int) best_pixel_error;\ + +#define ONETABLE59RGB(dval) \ + possible_colors[0][R] = clamp_table[color[R] - table59T[dval]+255]-255;\ + possible_colors[0][G] = clamp_table[color[G] - table59T[dval]+255]-255;\ + possible_colors[0][B] = clamp_table[color[B] - table59T[dval]+255]-255;\ + possible_colors[1][R] = color[R]-255;\ + possible_colors[1][G] = color[G]-255;\ + possible_colors[1][B] = color[B]-255;\ + possible_colors[2][R] = clamp_table[color[R] + table59T[dval]+255]-255;\ + possible_colors[2][G] = clamp_table[color[G] + table59T[dval]+255]-255;\ + possible_colors[2][B] = clamp_table[color[B] + table59T[dval]+255]-255;\ + precalc_err_col0_RGB_adr = &precalc_err_col0_RGB[(colorRGB444_packed*8 + dval)*16];\ + /* Loop block */\ + /* unroll loop for (int x = 0; x < 16; x++) */\ + {\ + ONEPOINT59RGB(0)\ + ONEPOINT59RGB(1)\ + ONEPOINT59RGB(2)\ + ONEPOINT59RGB(3)\ + ONEPOINT59RGB(4)\ + ONEPOINT59RGB(5)\ + ONEPOINT59RGB(6)\ + ONEPOINT59RGB(7)\ + ONEPOINT59RGB(8)\ + ONEPOINT59RGB(9)\ + ONEPOINT59RGB(10)\ + ONEPOINT59RGB(11)\ + ONEPOINT59RGB(12)\ + ONEPOINT59RGB(13)\ + ONEPOINT59RGB(14)\ + ONEPOINT59RGB(15)\ + }\ + + color[R] = (((colorRGB444_packed >> 8) ) << 4) | ((colorRGB444_packed >> 8) ) ; + color[G] = (((colorRGB444_packed >> 4) & 0xf) << 4) | ((colorRGB444_packed >> 4) & 0xf) ; + color[B] = (((colorRGB444_packed) & 0xf) << 4) | ((colorRGB444_packed) & 0xf) ; + + /* Test all distances */ + /* unroll loop for (uint8 d = 0; d < 8; ++d) */ + { + ONETABLE59RGB(0) + ONETABLE59RGB(1) + ONETABLE59RGB(2) + ONETABLE59RGB(3) + ONETABLE59RGB(4) + ONETABLE59RGB(5) + ONETABLE59RGB(6) + ONETABLE59RGB(7) + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col1_RGBpercep1000(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_RGB) +{ + unsigned int pixel_error; + int diff[3]; + uint8 colorRGB[3]; + + colorRGB[0] = ((colorRGB444_packed >> 8) & 0xf)*17; + colorRGB[1] = ((colorRGB444_packed >> 4) & 0xf)*17; + colorRGB[2] = ((colorRGB444_packed >> 0) & 0xf)*17; + + // Loop block + for (int x = 0; x < 16; x++) + { + diff[R] = block[4*x + R] - colorRGB[R]; + diff[G] = block[4*x + G] - colorRGB[G]; + diff[B] = block[4*x + B] - colorRGB[B]; + + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]) + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*SQUARE(diff[B]); + + precalc_err_col1_RGB[(colorRGB444_packed)*16 + x] = (unsigned int) pixel_error; + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in exhaustive compression of the T-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError59T_col1_RGB(uint8* block, int colorRGB444_packed, unsigned int *precalc_err_col1_RGB) +{ + unsigned int pixel_error; + int diff[3]; + uint8 colorRGB[3]; + + colorRGB[0] = ((colorRGB444_packed >> 8) & 0xf)*17; + colorRGB[1] = ((colorRGB444_packed >> 4) & 0xf)*17; + colorRGB[2] = ((colorRGB444_packed >> 0) & 0xf)*17; + + // Loop block + for (int x = 0; x < 16; x++) + { + diff[R] = block[4*x + R] - colorRGB[R]; + diff[G] = block[4*x + G] - colorRGB[G]; + diff[B] = block[4*x + B] - colorRGB[B]; + + pixel_error = SQUARE(diff[R]) + SQUARE(diff[G]) + SQUARE(diff[B]); + precalc_err_col1_RGB[(colorRGB444_packed)*16 + x] = (unsigned int) pixel_error; + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimal error for the T-mode when compressing exhaustively. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateError59TusingPrecalcRperceptual1000(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_R, unsigned int *precalc_err_col1_R, unsigned int best_error_so_far) +{ + unsigned int block_error = 0, + best_block_error = MAXERR1000; + + unsigned int *pixel_error_col0_base_adr; + unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; + +#define FIRSTCHOICE59R_PERCEP\ + if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ + block_error = *pixel_error_col0_adr;\ + else\ + block_error = *pixel_error_col1_adr;\ + +#define CHOICE59R_PERCEP(xval)\ + if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ + block_error += pixel_error_col0_adr[xval];\ + else\ + block_error += pixel_error_col1_adr[xval];\ + +#define ONETABLE59R_PERCEP(dval) \ + pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ + /* unroll loop for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ + {\ + FIRSTCHOICE59R_PERCEP\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R_PERCEP(1)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R_PERCEP(2)\ + CHOICE59R_PERCEP(3)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R_PERCEP(4)\ + CHOICE59R_PERCEP(5)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R_PERCEP(6)\ + CHOICE59R_PERCEP(7)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R_PERCEP(8)\ + CHOICE59R_PERCEP(9)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R_PERCEP(10)\ + CHOICE59R_PERCEP(11)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R_PERCEP(12)\ + CHOICE59R_PERCEP(13)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R_PERCEP(14)\ + CHOICE59R_PERCEP(15)\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + if (block_error < best_block_error)\ + best_block_error = block_error;\ + + pixel_error_col0_base_adr = &precalc_err_col0_R[((colorsRGB444_packed[0]>>8)*8)*16]; + pixel_error_col1_adr = &precalc_err_col1_R[((colorsRGB444_packed[1]>>8))*16]; + + // Test all distances + /* unroll loop for (uint8 d = 0; d < 8; d++) */ + { + ONETABLE59R_PERCEP(0) + ONETABLE59R_PERCEP(1) + ONETABLE59R_PERCEP(2) + ONETABLE59R_PERCEP(3) + ONETABLE59R_PERCEP(4) + ONETABLE59R_PERCEP(5) + ONETABLE59R_PERCEP(6) + ONETABLE59R_PERCEP(7) + } + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimal error for the T-mode when compressing exhaustively. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateError59TusingPrecalcR(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_R, unsigned int *precalc_err_col1_R, unsigned int best_error_so_far) +{ + unsigned int block_error = 0, + best_block_error = MAXIMUM_ERROR; + + unsigned int *pixel_error_col0_base_adr; + unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; + +#define FIRSTCHOICE59R\ + if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ + block_error = *pixel_error_col0_adr;\ + else\ + block_error = *pixel_error_col1_adr;\ + +#define CHOICE59R(xval)\ + if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ + block_error += pixel_error_col0_adr[xval];\ + else\ + block_error += pixel_error_col1_adr[xval];\ + +#define ONETABLE59R(dval) \ + pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ + /* unroll loop for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ + {\ + FIRSTCHOICE59R\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R(1)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R(2)\ + CHOICE59R(3)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R(4)\ + CHOICE59R(5)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R(6)\ + CHOICE59R(7)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R(8)\ + CHOICE59R(9)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R(10)\ + CHOICE59R(11)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R(12)\ + CHOICE59R(13)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59R(14)\ + CHOICE59R(15)\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + if (block_error < best_block_error)\ + best_block_error = block_error;\ + + pixel_error_col0_base_adr = &precalc_err_col0_R[((colorsRGB444_packed[0]>>8)*8)*16]; + pixel_error_col1_adr = &precalc_err_col1_R[((colorsRGB444_packed[1]>>8))*16]; + + + // Test all distances + /* unroll loop for (uint8 d = 0; d < 8; d++) */ + { + ONETABLE59R(0) + ONETABLE59R(1) + ONETABLE59R(2) + ONETABLE59R(3) + ONETABLE59R(4) + ONETABLE59R(5) + ONETABLE59R(6) + ONETABLE59R(7) + } + + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimal error for the T-mode when compressing exhaustively. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateError59TusingPrecalcRGperceptual1000(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_RG, unsigned int *precalc_err_col1_RG, unsigned int best_error_so_far) +{ + unsigned int block_error = 0, + best_block_error = MAXERR1000; + + unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; + unsigned int *pixel_error_col0_base_adr; + +#define FIRSTCHOICE59RG_PERCEP \ + if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ + block_error = *pixel_error_col0_adr;\ + else\ + block_error = *pixel_error_col1_adr;\ + + +#define CHOICE59RG_PERCEP(xval) \ + if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ + block_error += pixel_error_col0_adr[xval];\ + else\ + block_error += pixel_error_col1_adr[xval];\ + +#define ONETABLE59RG_PERCEP(dval)\ + pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ + /* unroll loop for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ + {\ + FIRSTCHOICE59RG_PERCEP\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG_PERCEP(1)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG_PERCEP(2)\ + CHOICE59RG_PERCEP(3)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG_PERCEP(4)\ + CHOICE59RG_PERCEP(5)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG_PERCEP(6)\ + CHOICE59RG_PERCEP(7)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG_PERCEP(8)\ + CHOICE59RG_PERCEP(9)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG_PERCEP(10)\ + CHOICE59RG_PERCEP(11)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG_PERCEP(12)\ + CHOICE59RG_PERCEP(13)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG_PERCEP(14)\ + CHOICE59RG_PERCEP(15)\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + if (block_error < best_block_error)\ + best_block_error = block_error;\ + + + pixel_error_col0_base_adr = &precalc_err_col0_RG[((colorsRGB444_packed[0]>>4)*8)*16]; + pixel_error_col1_adr = &precalc_err_col1_RG[((colorsRGB444_packed[1]>>4))*16]; + + // Test all distances + /* unroll loop for (uint8 d = 0; d < 8; d++) */ + { + + ONETABLE59RG_PERCEP(0) + ONETABLE59RG_PERCEP(1) + ONETABLE59RG_PERCEP(2) + ONETABLE59RG_PERCEP(3) + ONETABLE59RG_PERCEP(4) + ONETABLE59RG_PERCEP(5) + ONETABLE59RG_PERCEP(6) + ONETABLE59RG_PERCEP(7) + } + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimal error for the T-mode when compressing exhaustively. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateError59TusingPrecalcRG(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_RG, unsigned int *precalc_err_col1_RG, unsigned int best_error_so_far) +{ + unsigned int block_error = 0, + best_block_error = MAXIMUM_ERROR; + + unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; + unsigned int *pixel_error_col0_base_adr; + +#define FIRSTCHOICE59RG \ + if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ + block_error = *pixel_error_col0_adr;\ + else\ + block_error = *pixel_error_col1_adr;\ + +#define CHOICE59RG(xval) \ + if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ + block_error += pixel_error_col0_adr[xval];\ + else\ + block_error += pixel_error_col1_adr[xval];\ + +#define ONETABLE59RG(dval)\ + pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ + /* unroll loop for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ + {\ + FIRSTCHOICE59RG\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG(1)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG(2)\ + CHOICE59RG(3)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG(4)\ + CHOICE59RG(5)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG(6)\ + CHOICE59RG(7)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG(8)\ + CHOICE59RG(9)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG(10)\ + CHOICE59RG(11)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG(12)\ + CHOICE59RG(13)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59RG(14)\ + CHOICE59RG(15)\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + if (block_error < best_block_error)\ + best_block_error = block_error;\ + + pixel_error_col0_base_adr = &precalc_err_col0_RG[((colorsRGB444_packed[0]>>4)*8)*16]; + pixel_error_col1_adr = &precalc_err_col1_RG[((colorsRGB444_packed[1]>>4))*16]; + + // Test all distances + /* unroll loop for (uint8 d = 0; d < 8; d++) */ + { + ONETABLE59RG(0) + ONETABLE59RG(1) + ONETABLE59RG(2) + ONETABLE59RG(3) + ONETABLE59RG(4) + ONETABLE59RG(5) + ONETABLE59RG(6) + ONETABLE59RG(7) + } + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimal error for the T-mode when compressing exhaustively. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateError59TusingPrecalcRGBperceptual1000(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_RGB, unsigned int *precalc_err_col1_RGB, unsigned int best_error_so_far) +{ + unsigned int block_error = 0, + best_block_error = MAXERR1000; + unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; + unsigned int *pixel_error_col0_base_adr; + +#define FIRSTCHOICE59_PERCEP \ + if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ + block_error = *pixel_error_col0_adr;\ + else\ + block_error = *pixel_error_col1_adr;\ + +#define CHOICE59_PERCEP(xval) \ + if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ + block_error += pixel_error_col0_adr[xval];\ + else\ + block_error += pixel_error_col1_adr[xval];\ + +#define ONETABLE59T_PERCEP(dval)\ + pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ + /* unroll for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ + {\ + FIRSTCHOICE59_PERCEP\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59_PERCEP(1)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59_PERCEP(2)\ + CHOICE59_PERCEP(3)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59_PERCEP(4)\ + CHOICE59_PERCEP(5)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59_PERCEP(6)\ + CHOICE59_PERCEP(7)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59_PERCEP(8)\ + CHOICE59_PERCEP(9)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59_PERCEP(10)\ + CHOICE59_PERCEP(11)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59_PERCEP(12)\ + CHOICE59_PERCEP(13)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59_PERCEP(14)\ + CHOICE59_PERCEP(15)\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + if (block_error < best_block_error)\ + best_block_error = block_error;\ + + pixel_error_col1_adr = &precalc_err_col1_RGB[(colorsRGB444_packed[1])*16]; + pixel_error_col0_base_adr = &precalc_err_col0_RGB[(colorsRGB444_packed[0]*8)*16]; + + // Test all distances + /* unroll loop for (uint8 d = 0; d < 8; d++)*/ + { + ONETABLE59T_PERCEP(0) + ONETABLE59T_PERCEP(1) + ONETABLE59T_PERCEP(2) + ONETABLE59T_PERCEP(3) + ONETABLE59T_PERCEP(4) + ONETABLE59T_PERCEP(5) + ONETABLE59T_PERCEP(6) + ONETABLE59T_PERCEP(7) + } + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimal error for the T-mode when compressing exhaustively. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateError59TusingPrecalcRGB(uint8* block, int *colorsRGB444_packed, unsigned int *precalc_err_col0_RGB, unsigned int *precalc_err_col1_RGB, unsigned int best_error_so_far) +{ + unsigned int block_error = 0, + best_block_error = MAXIMUM_ERROR; + unsigned int *pixel_error_col0_adr, *pixel_error_col1_adr; + unsigned int *pixel_error_col0_base_adr; + +#define FIRSTCHOICE59 \ + if(*pixel_error_col0_adr < *pixel_error_col1_adr)\ + block_error = *pixel_error_col0_adr;\ + else\ + block_error = *pixel_error_col1_adr;\ + +#define CHOICE59(xval) \ + if(pixel_error_col0_adr[xval] < pixel_error_col1_adr[xval])\ + block_error += pixel_error_col0_adr[xval];\ + else\ + block_error += pixel_error_col1_adr[xval];\ + +#define ONETABLE59T(dval)\ + pixel_error_col0_adr = &pixel_error_col0_base_adr[dval*16];\ + /* unroll for(int x = 0; block_error < best_error_so_far && x<16; x++) */\ + {\ + FIRSTCHOICE59\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59(1)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59(2)\ + CHOICE59(3)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59(4)\ + CHOICE59(5)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59(6)\ + CHOICE59(7)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59(8)\ + CHOICE59(9)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59(10)\ + CHOICE59(11)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59(12)\ + CHOICE59(13)\ + if( block_error < best_error_so_far)\ + {\ + CHOICE59(14)\ + CHOICE59(15)\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + if (block_error < best_block_error)\ + best_block_error = block_error;\ + + pixel_error_col1_adr = &precalc_err_col1_RGB[(colorsRGB444_packed[1])*16]; + pixel_error_col0_base_adr = &precalc_err_col0_RGB[(colorsRGB444_packed[0]*8)*16]; + + // Test all distances + /* unroll loop for (uint8 d = 0; d < 8; d++)*/ + { + ONETABLE59T(0) + ONETABLE59T(1) + ONETABLE59T(2) + ONETABLE59T(3) + ONETABLE59T(4) + ONETABLE59T(5) + ONETABLE59T(6) + ONETABLE59T(7) + } + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// The below code should compress the block to 59 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// Note that this method might not return the best possible compression for the T-mode. It will only do so if the best possible T-representation +// is less than best_error_so_far. To guarantee that the best possible T-representation is found, the function should be called using +// best_error_so_far = 255*255*3*16, which is the maximum error for a block. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockTHUMB59TExhaustivePerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int best_error_so_far) +{ + uint8 colorsRGB444[2][3]; + unsigned int pixel_indices; + uint8 distance; + + uint8 block[4*4*4]; + + unsigned int *precalc_err_col0_RGB; + unsigned int *precalc_err_col1_RGB; + unsigned int *precalc_err_col0_RG; + unsigned int *precalc_err_col1_RG; + unsigned int *precalc_err_col0_R; + unsigned int *precalc_err_col1_R; + + int colorRGB444_packed; + + int colorsRGB444_packed[2]; + int best_colorsRGB444_packed[2]; + + unsigned int best_error_using_Tmode; + + // First compress block quickly to a resonable quality so that we can + // rule out all blocks that are of worse quality than that. + best_error_using_Tmode = (unsigned int) compressBlockTHUMB59TFastestOnlyColorPerceptual1000(img, width, height, startx, starty, best_colorsRGB444_packed); + if(best_error_using_Tmode < best_error_so_far) + best_error_so_far = best_error_using_Tmode; + + // Color numbering is reversed between the above function and the precalc functions below; swap colors. + int temp = best_colorsRGB444_packed[0]; + best_colorsRGB444_packed[0] = best_colorsRGB444_packed[1]; + best_colorsRGB444_packed[1] = temp; + + int xx,yy,count = 0; + + // Use 4 bytes per pixel to make it 32-word aligned. + for(xx = 0; xx<4; xx++) + { + for(yy=0; yy<4; yy++) + { + block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block[(count)*4+3] = 0; + count++; + } + } + + // Precalculate error for color 0 (which produces the upper half of the T) + precalc_err_col0_RGB = (unsigned int*) malloc(4096*8*16*sizeof(unsigned int)); + if(!precalc_err_col0_RGB){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) + { + precalcError59T_col0_RGBpercep1000(block, colorRGB444_packed, precalc_err_col0_RGB); + } + + // Precalculate error for color 1 (which produces the lower half of the T -- the lone color) + precalc_err_col1_RGB = (unsigned int*) malloc(4096*16*sizeof(unsigned int)); + if(!precalc_err_col1_RGB){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) + { + precalcError59T_col1_RGBpercep1000(block, colorRGB444_packed, precalc_err_col1_RGB); + } + + precalc_err_col0_RG = (unsigned int*) malloc(16*16*8*16*sizeof(unsigned int)); + if(!precalc_err_col0_RG){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) + { + precalcError59T_col0_RGpercep1000(block, colorRGB444_packed, precalc_err_col0_RG); + } + + precalc_err_col1_RG = (unsigned int*) malloc(16*16*16*sizeof(unsigned int)); + if(!precalc_err_col1_RG){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) + { + precalcError59T_col1_RGpercep1000(block, colorRGB444_packed, precalc_err_col1_RG); + } + + precalc_err_col0_R = (unsigned int*) malloc(16*8*16*sizeof(unsigned int)); + if(!precalc_err_col0_R){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) + { + precalcError59T_col0_Rpercep1000(block, colorRGB444_packed, precalc_err_col0_R); + } + + precalc_err_col1_R = (unsigned int*) malloc(16*16*sizeof(unsigned int)); + if(!precalc_err_col1_R){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) + { + precalcError59T_col1_Rpercep1000(block, colorRGB444_packed, precalc_err_col1_R); + } + + unsigned int error; + unsigned int avoided = 0; + unsigned int notavoided = 0; + + for(colorsRGB444[0][0] = 0; colorsRGB444[0][0] < 16; colorsRGB444[0][0]++) + { + for(colorsRGB444[1][0] = 0; colorsRGB444[1][0] < 16; colorsRGB444[1][0]++) + { + colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8); + colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8); + error = calculateError59TusingPrecalcRperceptual1000(block, colorsRGB444_packed, precalc_err_col0_R, precalc_err_col1_R, best_error_so_far); + if(error < best_error_so_far) + { + notavoided = notavoided + 1; + for(colorsRGB444[0][1] = 0; colorsRGB444[0][1] < 16; colorsRGB444[0][1]++) + { + colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8) + (colorsRGB444[0][1] <<4); + for(colorsRGB444[1][1] = 0; colorsRGB444[1][1] < 16; colorsRGB444[1][1]++) + { + colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8) + (colorsRGB444[1][1] <<4); + error = calculateError59TusingPrecalcRGperceptual1000(block, colorsRGB444_packed, precalc_err_col0_RG, precalc_err_col1_RG, best_error_so_far); + if(error < best_error_so_far) + { + for(colorsRGB444[0][2] = 0; colorsRGB444[0][2] < 16; colorsRGB444[0][2]++) + { + colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8) + (colorsRGB444[0][1] <<4) + colorsRGB444[0][2]; + for(colorsRGB444[1][2] = 0; colorsRGB444[1][2] < 16; colorsRGB444[1][2]++) + { + colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8) + (colorsRGB444[1][1] <<4) + colorsRGB444[1][2]; + error = calculateError59TusingPrecalcRGBperceptual1000(block, colorsRGB444_packed, precalc_err_col0_RGB, precalc_err_col1_RGB, best_error_so_far); + + if(error < best_error_so_far) + { + best_error_so_far = error; + best_error_using_Tmode = error; + best_colorsRGB444_packed[0] = colorsRGB444_packed[0]; + best_colorsRGB444_packed[1] = colorsRGB444_packed[1]; + } + } + } + } + } + } + } + } + } + + free(precalc_err_col0_RGB); + free(precalc_err_col1_RGB); + free(precalc_err_col0_RG); + free(precalc_err_col1_RG); + free(precalc_err_col0_R); + free(precalc_err_col1_R); + + // We have got the two best colors. Now find the best distance and pixel indices. + + // Color numbering are reversed between precalc and noSwap + colorsRGB444[0][0] = (best_colorsRGB444_packed[1] >> 8) & 0xf; + colorsRGB444[0][1] = (best_colorsRGB444_packed[1] >> 4) & 0xf; + colorsRGB444[0][2] = (best_colorsRGB444_packed[1] >> 0) & 0xf; + + colorsRGB444[1][0] = (best_colorsRGB444_packed[0] >> 8) & 0xf; + colorsRGB444[1][1] = (best_colorsRGB444_packed[0] >> 4) & 0xf; + colorsRGB444[1][2] = (best_colorsRGB444_packed[0] >> 0) & 0xf; + + calculateError59TnoSwapPerceptual1000(img, width, startx, starty, colorsRGB444, distance, pixel_indices); + + // Put the compress params into the compression block + packBlock59T(colorsRGB444, distance, pixel_indices, compressed1, compressed2); + + return best_error_using_Tmode; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// The below code should compress the block to 59 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// Note that this method might not return the best possible compression for the T-mode. It will only do so if the best possible T-representation +// is less than best_error_so_far. To guarantee that the best possible T-representation is found, the function should be called using +// best_error_so_far = 255*255*3*16, which is the maximum error for a block. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockTHUMB59TExhaustive(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int best_error_so_far) +{ + uint8 colorsRGB444[2][3]; + unsigned int pixel_indices; + uint8 distance; + + uint8 block[4*4*4]; + + unsigned int *precalc_err_col0_RGB; + unsigned int *precalc_err_col1_RGB; + unsigned int *precalc_err_col0_RG; + unsigned int *precalc_err_col1_RG; + unsigned int *precalc_err_col0_R; + unsigned int *precalc_err_col1_R; + + int colorRGB444_packed; + + int colorsRGB444_packed[2]; + int best_colorsRGB444_packed[2]; + + unsigned int best_error_using_Tmode; + + // First compress block quickly to a resonable quality so that we can + // rule out all blocks that are of worse quality than that. + best_error_using_Tmode = (unsigned int) compressBlockTHUMB59TFastestOnlyColor(img, width, height, startx, starty, best_colorsRGB444_packed); + if(best_error_using_Tmode < best_error_so_far) + best_error_so_far = best_error_using_Tmode; + + + // Colors numbering is reversed between the above function and the precalc below: + int temp = best_colorsRGB444_packed[0]; + best_colorsRGB444_packed[0] = best_colorsRGB444_packed[1]; + best_colorsRGB444_packed[1] = temp; + + int xx,yy,count = 0; + + // Use 4 bytes per pixel to make it 32-word aligned. + for(xx = 0; xx<4; xx++) + { + for(yy=0; yy<4; yy++) + { + block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block[(count)*4+3] = 0; + count++; + } + } + + // Precalculate error for color 0 (which produces the upper half of the T) + precalc_err_col0_RGB = (unsigned int*) malloc(4096*8*16*sizeof(unsigned int)); + if(!precalc_err_col0_RGB){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) + { + precalcError59T_col0_RGB(block, colorRGB444_packed, precalc_err_col0_RGB); + } + + // Precalculate error for color 1 (which produces the lower half of the T -- the lone color) + precalc_err_col1_RGB = (unsigned int*) malloc(4096*16*sizeof(unsigned int)); + if(!precalc_err_col1_RGB){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) + { + precalcError59T_col1_RGB(block, colorRGB444_packed, precalc_err_col1_RGB); + } + + precalc_err_col0_RG = (unsigned int*) malloc(16*16*8*16*sizeof(unsigned int)); + if(!precalc_err_col0_RG){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) + { + precalcError59T_col0_RG(block, colorRGB444_packed, precalc_err_col0_RG); + } + + precalc_err_col1_RG = (unsigned int*) malloc(16*16*16*sizeof(unsigned int)); + if(!precalc_err_col1_RG){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) + { + precalcError59T_col1_RG(block, colorRGB444_packed, precalc_err_col1_RG); + } + + precalc_err_col0_R = (unsigned int*) malloc(16*8*16*sizeof(unsigned int)); + if(!precalc_err_col0_R){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) + { + precalcError59T_col0_R(block, colorRGB444_packed, precalc_err_col0_R); + } + + precalc_err_col1_R = (unsigned int*) malloc(16*16*sizeof(unsigned int)); + if(!precalc_err_col1_R){printf("Out of memory allocating \n");exit(1);} + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) + { + precalcError59T_col1_R(block, colorRGB444_packed, precalc_err_col1_R); + } + + unsigned int error; + unsigned int avoided = 0; + unsigned int notavoided = 0; + + for(colorsRGB444[0][0] = 0; colorsRGB444[0][0] < 16; colorsRGB444[0][0]++) + { + for(colorsRGB444[1][0] = 0; colorsRGB444[1][0] < 16; colorsRGB444[1][0]++) + { + colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8); + colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8); + error = calculateError59TusingPrecalcR(block, colorsRGB444_packed, precalc_err_col0_R, precalc_err_col1_R, best_error_so_far); + if(error < best_error_so_far) + { + notavoided = notavoided + 1; + for(colorsRGB444[0][1] = 0; colorsRGB444[0][1] < 16; colorsRGB444[0][1]++) + { + colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8) + (colorsRGB444[0][1] <<4); + for(colorsRGB444[1][1] = 0; colorsRGB444[1][1] < 16; colorsRGB444[1][1]++) + { + colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8) + (colorsRGB444[1][1] <<4); + error = calculateError59TusingPrecalcRG(block, colorsRGB444_packed, precalc_err_col0_RG, precalc_err_col1_RG, best_error_so_far); + if(error < best_error_so_far) + { + for(colorsRGB444[0][2] = 0; colorsRGB444[0][2] < 16; colorsRGB444[0][2]++) + { + colorsRGB444_packed[0] = (colorsRGB444[0][0] << 8) + (colorsRGB444[0][1] <<4) + colorsRGB444[0][2]; + for(colorsRGB444[1][2] = 0; colorsRGB444[1][2] < 16; colorsRGB444[1][2]++) + { + colorsRGB444_packed[1] = (colorsRGB444[1][0] << 8) + (colorsRGB444[1][1] <<4) + colorsRGB444[1][2]; + error = calculateError59TusingPrecalcRGB(block, colorsRGB444_packed, precalc_err_col0_RGB, precalc_err_col1_RGB, best_error_so_far); + + if(error < best_error_so_far) + { + best_error_so_far = error; + best_error_using_Tmode = error; + best_colorsRGB444_packed[0] = colorsRGB444_packed[0]; + best_colorsRGB444_packed[1] = colorsRGB444_packed[1]; + } + } + } + } + } + } + } + } + } + + free(precalc_err_col0_RGB); + free(precalc_err_col1_RGB); + free(precalc_err_col0_RG); + free(precalc_err_col1_RG); + free(precalc_err_col0_R); + free(precalc_err_col1_R); + + // We have got the two best colors. Now find the best distance and pixel indices. + + // Color numbering are reversed between precalc and noSwap + colorsRGB444[0][0] = (best_colorsRGB444_packed[1] >> 8) & 0xf; + colorsRGB444[0][1] = (best_colorsRGB444_packed[1] >> 4) & 0xf; + colorsRGB444[0][2] = (best_colorsRGB444_packed[1] >> 0) & 0xf; + + colorsRGB444[1][0] = (best_colorsRGB444_packed[0] >> 8) & 0xf; + colorsRGB444[1][1] = (best_colorsRGB444_packed[0] >> 4) & 0xf; + colorsRGB444[1][2] = (best_colorsRGB444_packed[0] >> 0) & 0xf; + + calculateError59TnoSwap(img, width, startx, starty, colorsRGB444, distance, pixel_indices); + + // Put the compress params into the compression block + packBlock59T(colorsRGB444, distance, pixel_indices, compressed1, compressed2); + + return best_error_using_Tmode; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates tables used in the exhaustive compression of the H-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcErrorR_58Hperceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_errR) +{ + unsigned int block_error = 0, + best_block_error = MAXERR1000, + pixel_error, + best_pixel_error; + int diff[3]; + unsigned int pixel_colors; + uint8 possible_colors[2][3]; + uint8 colors[2][3]; + + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) + { + possible_colors[0][R] = CLAMP(0,colors[0][R] - table58H[d],255); + possible_colors[1][R] = CLAMP(0,colors[0][R] + table58H[d],255); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXERR1000; + + // Loop possible block colors + for (uint8 c = 0; c < 2; ++c) + { + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + } + } + precalc_errR[((colorRGB444_packed>>8)*8 + d)*16 + (y*4)+x] = (unsigned int) best_pixel_error; + } + } + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates tables used in the exhaustive compression of the H-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcErrorR_58H(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_errR) +{ + double block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + int diff[3]; + unsigned int pixel_colors; + uint8 possible_colors[2][3]; + uint8 colors[2][3]; + + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) + { + possible_colors[0][R] = CLAMP(0,colors[0][R] - table58H[d],255); + possible_colors[1][R] = CLAMP(0,colors[0][R] + table58H[d],255); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXIMUM_ERROR; + + // Loop possible block colors + for (uint8 c = 0; c < 2; ++c) + { + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + + pixel_error = weight[R]*SQUARE(diff[R]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + } + } + precalc_errR[((colorRGB444_packed>>8)*8 + d)*16 + (y*4)+x] = (unsigned int) best_pixel_error; + } + } + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates tables used in the exhaustive compression of the H-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcErrorRG_58Hperceptual1000(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_errRG) +{ + unsigned int block_error = 0, + best_block_error = MAXERR1000, + pixel_error, + best_pixel_error; + int diff[3]; + unsigned int pixel_colors; + uint8 possible_colors[2][3]; + uint8 colors[2][3]; + + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) + { + possible_colors[0][R] = CLAMP(0,colors[0][R] - table58H[d],255); + possible_colors[0][G] = CLAMP(0,colors[0][G] - table58H[d],255); + possible_colors[1][R] = CLAMP(0,colors[0][R] + table58H[d],255); + possible_colors[1][G] = CLAMP(0,colors[0][G] + table58H[d],255); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXERR1000; + + // Loop possible block colors + for (uint8 c = 0; c < 2; ++c) + { + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*SQUARE(diff[R]) + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*SQUARE(diff[G]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + } + } + precalc_errRG[((colorRGB444_packed>>4)*8 + d)*16 + (y*4)+x] = (unsigned int) best_pixel_error; + } + } + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates tables used in the exhaustive compression of the H-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcErrorRG_58H(uint8* srcimg, int width, int startx, int starty, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_errRG) +{ + double block_error = 0, + best_block_error = MAXIMUM_ERROR, + pixel_error, + best_pixel_error; + int diff[3]; + unsigned int pixel_colors; + uint8 possible_colors[2][3]; + uint8 colors[2][3]; + + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + // Test all distances + for (uint8 d = 0; d < BINPOW(TABLE_BITS_58H); ++d) + { + possible_colors[0][R] = CLAMP(0,colors[0][R] - table58H[d],255); + possible_colors[0][G] = CLAMP(0,colors[0][G] - table58H[d],255); + possible_colors[1][R] = CLAMP(0,colors[0][R] + table58H[d],255); + possible_colors[1][G] = CLAMP(0,colors[0][G] + table58H[d],255); + + block_error = 0; + pixel_colors = 0; + + // Loop block + for (size_t y = 0; y < BLOCKHEIGHT; ++y) + { + for (size_t x = 0; x < BLOCKWIDTH; ++x) + { + best_pixel_error = MAXIMUM_ERROR; + + // Loop possible block colors + for (uint8 c = 0; c < 2; ++c) + { + diff[R] = srcimg[3*((starty+y)*width+startx+x)+R] - CLAMP(0,possible_colors[c][R],255); + diff[G] = srcimg[3*((starty+y)*width+startx+x)+G] - CLAMP(0,possible_colors[c][G],255); + + pixel_error = weight[R]*SQUARE(diff[R]) + + weight[G]*SQUARE(diff[G]); + + // Choose best error + if (pixel_error < best_pixel_error) + { + best_pixel_error = pixel_error; + } + } + precalc_errRG[((colorRGB444_packed>>4)*8 + d)*16 + (y*4)+x] = (unsigned int) best_pixel_error; + } + } + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in the exhaustive compression of the H-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError58Hperceptual1000(uint8* block, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_err) +{ + unsigned int pixel_error, + best_pixel_error; + int possible_colors[2][3]; + uint8 colors[2][3]; + unsigned int *precalc_err_tab; + int red_original; + int green_original; + int blue_original; + +#define PRECALC_ONE_58H_PERCEP(qvalue)\ + red_original = block[qvalue*4];\ + green_original = block[qvalue*4+1];\ + blue_original = block[qvalue*4+2];\ + /* unroll loop for (color = 0; color< 2; color++) */\ + best_pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[(possible_colors[0][R] - red_original)] \ + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[(possible_colors[0][G] - green_original)]\ + + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[(possible_colors[0][B] - blue_original)];\ + pixel_error = PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000*square_table[(possible_colors[1][R] - red_original)]\ + + PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000*square_table[(possible_colors[1][G] - green_original)]\ + + PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000*square_table[(possible_colors[1][B] - blue_original)];\ + if (pixel_error < best_pixel_error)\ + best_pixel_error = pixel_error;\ + /* end unroll loop */\ + precalc_err_tab[qvalue] = best_pixel_error;\ + +#define PRECALC_ONE_TABLE_58H_PERCEP(dvalue)\ + precalc_err_tab = &precalc_err[((colorRGB444_packed*8)+dvalue)*16];\ + possible_colors[0][R] = CLAMP_LEFT_ZERO(colors[0][R] - table58H[dvalue])+255;\ + possible_colors[0][G] = CLAMP_LEFT_ZERO(colors[0][G] - table58H[dvalue])+255;\ + possible_colors[0][B] = CLAMP_LEFT_ZERO(colors[0][B] - table58H[dvalue])+255;\ + possible_colors[1][R] = CLAMP_RIGHT_255(colors[0][R] + table58H[dvalue])+255;\ + possible_colors[1][G] = CLAMP_RIGHT_255(colors[0][G] + table58H[dvalue])+255;\ + possible_colors[1][B] = CLAMP_RIGHT_255(colors[0][B] + table58H[dvalue])+255;\ + /* unrolled loop for(q = 0; q<16; q++)*/\ + PRECALC_ONE_58H_PERCEP(0)\ + PRECALC_ONE_58H_PERCEP(1)\ + PRECALC_ONE_58H_PERCEP(2)\ + PRECALC_ONE_58H_PERCEP(3)\ + PRECALC_ONE_58H_PERCEP(4)\ + PRECALC_ONE_58H_PERCEP(5)\ + PRECALC_ONE_58H_PERCEP(6)\ + PRECALC_ONE_58H_PERCEP(7)\ + PRECALC_ONE_58H_PERCEP(8)\ + PRECALC_ONE_58H_PERCEP(9)\ + PRECALC_ONE_58H_PERCEP(10)\ + PRECALC_ONE_58H_PERCEP(11)\ + PRECALC_ONE_58H_PERCEP(12)\ + PRECALC_ONE_58H_PERCEP(13)\ + PRECALC_ONE_58H_PERCEP(14)\ + PRECALC_ONE_58H_PERCEP(15)\ + /* end unroll loop */\ + + colors[0][R] = (colorsRGB444[0][R] << 4) | colorsRGB444[0][R]; + colors[0][G] = (colorsRGB444[0][G] << 4) | colorsRGB444[0][G]; + colors[0][B] = (colorsRGB444[0][B] << 4) | colorsRGB444[0][B]; + + // Test all distances + /* unroll loop for (uint8 d = 0; d < 8; ++d) */ + + PRECALC_ONE_TABLE_58H_PERCEP(0) + PRECALC_ONE_TABLE_58H_PERCEP(1) + PRECALC_ONE_TABLE_58H_PERCEP(2) + PRECALC_ONE_TABLE_58H_PERCEP(3) + PRECALC_ONE_TABLE_58H_PERCEP(4) + PRECALC_ONE_TABLE_58H_PERCEP(5) + PRECALC_ONE_TABLE_58H_PERCEP(6) + PRECALC_ONE_TABLE_58H_PERCEP(7) + + /* end unroll loop */ +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Precalculates a table used in the exhaustive compression of the H-mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void precalcError58H(uint8* block, uint8 (colorsRGB444)[2][3],int colorRGB444_packed, unsigned int *precalc_err) +{ + unsigned int pixel_error, + best_pixel_error; + int possible_colors[2][3]; + uint8 colors[2][3]; + unsigned int *precalc_err_tab; + int red_original; + int green_original; + int blue_original; + +#define PRECALC_ONE_58H(qvalue)\ + red_original = block[qvalue*4];\ + green_original = block[qvalue*4+1];\ + blue_original = block[qvalue*4+2];\ + /* unroll loop for (color = 0; color< 2; color++) */\ + best_pixel_error = square_table[(possible_colors[0][R] - red_original)] + square_table[(possible_colors[0][G] - green_original)] + square_table[(possible_colors[0][B] - blue_original)];\ + pixel_error = square_table[(possible_colors[1][R] - red_original)] + square_table[(possible_colors[1][G] - green_original)] + square_table[(possible_colors[1][B] - blue_original)];\ + if (pixel_error < best_pixel_error)\ + best_pixel_error = pixel_error;\ + /* end unroll loop */\ + precalc_err_tab[qvalue] = best_pixel_error;\ + +#define PRECALC_ONE_TABLE_58H(dvalue)\ + precalc_err_tab = &precalc_err[((colorRGB444_packed*8)+dvalue)*16];\ + possible_colors[0][R] = CLAMP_LEFT_ZERO(colors[0][R] - table58H[dvalue])+255;\ + possible_colors[0][G] = CLAMP_LEFT_ZERO(colors[0][G] - table58H[dvalue])+255;\ + possible_colors[0][B] = CLAMP_LEFT_ZERO(colors[0][B] - table58H[dvalue])+255;\ + possible_colors[1][R] = CLAMP_RIGHT_255(colors[0][R] + table58H[dvalue])+255;\ + possible_colors[1][G] = CLAMP_RIGHT_255(colors[0][G] + table58H[dvalue])+255;\ + possible_colors[1][B] = CLAMP_RIGHT_255(colors[0][B] + table58H[dvalue])+255;\ + /* unrolled loop for(q = 0; q<16; q++)*/\ + PRECALC_ONE_58H(0)\ + PRECALC_ONE_58H(1)\ + PRECALC_ONE_58H(2)\ + PRECALC_ONE_58H(3)\ + PRECALC_ONE_58H(4)\ + PRECALC_ONE_58H(5)\ + PRECALC_ONE_58H(6)\ + PRECALC_ONE_58H(7)\ + PRECALC_ONE_58H(8)\ + PRECALC_ONE_58H(9)\ + PRECALC_ONE_58H(10)\ + PRECALC_ONE_58H(11)\ + PRECALC_ONE_58H(12)\ + PRECALC_ONE_58H(13)\ + PRECALC_ONE_58H(14)\ + PRECALC_ONE_58H(15)\ + /* end unroll loop */\ + + colors[0][R] = (colorsRGB444[0][R] << 4) | colorsRGB444[0][R]; + colors[0][G] = (colorsRGB444[0][G] << 4) | colorsRGB444[0][G]; + colors[0][B] = (colorsRGB444[0][B] << 4) | colorsRGB444[0][B]; + + // Test all distances + /* unroll loop for (uint8 d = 0; d < 8; ++d) */ + + PRECALC_ONE_TABLE_58H(0) + PRECALC_ONE_TABLE_58H(1) + PRECALC_ONE_TABLE_58H(2) + PRECALC_ONE_TABLE_58H(3) + PRECALC_ONE_TABLE_58H(4) + PRECALC_ONE_TABLE_58H(5) + PRECALC_ONE_TABLE_58H(6) + PRECALC_ONE_TABLE_58H(7) + + /* end unroll loop */ +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimum error for the H-mode when doing exhaustive compression. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateErrorFromPrecalcR58Hperceptual1000(int *colorsRGB444_packed, unsigned int *precalc_errR, unsigned int best_err_so_far) +{ + unsigned int block_error = 0; + unsigned int best_block_error = MAXERR1000; + unsigned int *precalc_col1, *precalc_col2; + unsigned int *precalc_col1tab, *precalc_col2tab; + + precalc_col1 = &precalc_errR[(colorsRGB444_packed[0]>>8)*8*16]; + precalc_col2 = &precalc_errR[(colorsRGB444_packed[1]>>8)*8*16]; + +#define CHOICE_R58H_PERCEP(value)\ + if(precalc_col1tab[value] < precalc_col2tab[value])\ + block_error += precalc_col1tab[value];\ + else\ + block_error += precalc_col2tab[value];\ + + // Test all distances + for (uint8 d = 0; d < 8; ++d) + { + block_error = 0; + precalc_col1tab = &precalc_col1[d*16]; + precalc_col2tab = &precalc_col2[d*16]; + // Loop block + + /* unroll loop for(q = 0; q<16 && block_error < best_err_so_far; q++) */ + CHOICE_R58H_PERCEP(0) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(1) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(2) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(3) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(4) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(5) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(6) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(7) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(8) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(9) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(10) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(11) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(12) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(13) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(14) + if( block_error < best_err_so_far ) + { + CHOICE_R58H_PERCEP(15) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + /* end unroll loop */ + + if (block_error < best_block_error) + best_block_error = block_error; + } + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimum error for the H-mode when doing exhaustive compression. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateErrorFromPrecalcR58H(int *colorsRGB444_packed, unsigned int *precalc_errR, unsigned int best_err_so_far) +{ + unsigned int block_error = 0; + unsigned int best_block_error = MAXIMUM_ERROR; + unsigned int *precalc_col1, *precalc_col2; + unsigned int *precalc_col1tab, *precalc_col2tab; + + precalc_col1 = &precalc_errR[(colorsRGB444_packed[0]>>8)*8*16]; + precalc_col2 = &precalc_errR[(colorsRGB444_packed[1]>>8)*8*16]; + +#define CHOICE_R58H(value)\ + if(precalc_col1tab[value] < precalc_col2tab[value])\ + block_error += precalc_col1tab[value];\ + else\ + block_error += precalc_col2tab[value];\ + + // Test all distances + for (uint8 d = 0; d < 8; ++d) + { + block_error = 0; + precalc_col1tab = &precalc_col1[d*16]; + precalc_col2tab = &precalc_col2[d*16]; + // Loop block + + /* unroll loop for(q = 0; q<16 && block_error < best_err_so_far; q++) */ + CHOICE_R58H(0) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(1) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(2) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(3) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(4) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(5) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(6) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(7) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(8) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(9) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(10) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(11) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(12) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(13) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(14) + if( block_error < best_err_so_far ) + { + CHOICE_R58H(15) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + /* end unroll loop */ + + if (block_error < best_block_error) + best_block_error = block_error; + + } + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimum error for the H-mode when doing exhaustive compression. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateErrorFromPrecalcRG58Hperceptual1000(int *colorsRGB444_packed, unsigned int *precalc_errRG, unsigned int best_err_so_far) +{ + unsigned int block_error = 0; + unsigned int best_block_error = MAXIMUM_ERROR; + unsigned int *precalc_col1, *precalc_col2; + unsigned int *precalc_col1tab, *precalc_col2tab; + + precalc_col1 = &precalc_errRG[(colorsRGB444_packed[0]>>4)*8*16]; + precalc_col2 = &precalc_errRG[(colorsRGB444_packed[1]>>4)*8*16]; + +#define CHOICE_RG58H_PERCEP(value)\ + if(precalc_col1tab[value] < precalc_col2tab[value])\ + block_error += precalc_col1tab[value];\ + else\ + block_error += precalc_col2tab[value];\ + + // Test all distances + for (uint8 d = 0; d < 8; ++d) + { + block_error = 0; + precalc_col1tab = &precalc_col1[d*16]; + precalc_col2tab = &precalc_col2[d*16]; + // Loop block + + /* unroll loop for(q = 0; q<16 && block_error < best_err_so_far; q++) */ + CHOICE_RG58H_PERCEP(0) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(1) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(2) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(3) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(4) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(5) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(6) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(7) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(8) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(9) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(10) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(11) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(12) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(13) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(14) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H_PERCEP(15) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + /* end unroll loop */ + + if (block_error < best_block_error) + best_block_error = block_error; + } + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimum error for the H-mode when doing exhaustive compression. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateErrorFromPrecalcRG58H(int *colorsRGB444_packed, unsigned int *precalc_errRG, unsigned int best_err_so_far) +{ + unsigned int block_error = 0; + unsigned int best_block_error = MAXIMUM_ERROR; + unsigned int *precalc_col1, *precalc_col2; + unsigned int *precalc_col1tab, *precalc_col2tab; + + precalc_col1 = &precalc_errRG[(colorsRGB444_packed[0]>>4)*8*16]; + precalc_col2 = &precalc_errRG[(colorsRGB444_packed[1]>>4)*8*16]; + +#define CHOICE_RG58H(value)\ + if(precalc_col1tab[value] < precalc_col2tab[value])\ + block_error += precalc_col1tab[value];\ + else\ + block_error += precalc_col2tab[value];\ + + // Test all distances + for (uint8 d = 0; d < 8; ++d) + { + block_error = 0; + precalc_col1tab = &precalc_col1[d*16]; + precalc_col2tab = &precalc_col2[d*16]; + // Loop block + + /* unroll loop for(q = 0; q<16 && block_error < best_err_so_far; q++) */ + CHOICE_RG58H(0) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(1) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(2) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(3) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(4) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(5) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(6) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(7) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(8) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(9) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(10) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(11) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(12) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(13) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(14) + if( block_error < best_err_so_far ) + { + CHOICE_RG58H(15) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + /* end unroll loop */ + + if (block_error < best_block_error) + best_block_error = block_error; + } + return best_block_error; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimum error for the H-mode when doing exhaustive compression. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateErrorFromPrecalc58Hperceptual1000(int *colorsRGB444_packed, unsigned int *precalc_err, unsigned int total_best_err) +{ + unsigned int block_error;\ + unsigned int *precalc_col1, *precalc_col2;\ + unsigned int *precalc_col1tab, *precalc_col2tab;\ + + unsigned int error; + +#define FIRSTCHOICE_RGB58H_PERCEP(value)\ + if(precalc_col1tab[value] < precalc_col2tab[value])\ + block_error = precalc_col1tab[value];\ + else\ + block_error = precalc_col2tab[value];\ + +#define CHOICE_RGB58H_PERCEP(value)\ + if(precalc_col1tab[value] < precalc_col2tab[value])\ + block_error += precalc_col1tab[value];\ + else\ + block_error += precalc_col2tab[value];\ + +#define ONETABLE_RGB58H_PERCEP(distance)\ + precalc_col1tab = &precalc_col1[distance*16];\ + precalc_col2tab = &precalc_col2[distance*16];\ + /* unroll loop for(q = 0; q<16 && block_error < total_best_err; q++) */\ + FIRSTCHOICE_RGB58H_PERCEP(0)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H_PERCEP(1)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H_PERCEP(2)\ + CHOICE_RGB58H_PERCEP(3)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H_PERCEP(4)\ + CHOICE_RGB58H_PERCEP(5)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H_PERCEP(6)\ + CHOICE_RGB58H_PERCEP(7)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H_PERCEP(8)\ + CHOICE_RGB58H_PERCEP(9)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H_PERCEP(10)\ + CHOICE_RGB58H_PERCEP(11)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H_PERCEP(12)\ + CHOICE_RGB58H_PERCEP(13)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H_PERCEP(14)\ + CHOICE_RGB58H_PERCEP(15)\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + /* end unroll loop */\ + if (block_error < error)\ + error = block_error;\ + +#define CALCULATE_ERROR_FROM_PRECALC_RGB58H_PERCEP\ + error = MAXERR1000;\ + precalc_col1 = &precalc_err[colorsRGB444_packed[0]*8*16];\ + precalc_col2 = &precalc_err[colorsRGB444_packed[1]*8*16];\ + /* Test all distances*/\ + /* unroll loop for (uint8 d = 0; d < 8; ++d) */\ + ONETABLE_RGB58H_PERCEP(0)\ + ONETABLE_RGB58H_PERCEP(1)\ + ONETABLE_RGB58H_PERCEP(2)\ + ONETABLE_RGB58H_PERCEP(3)\ + ONETABLE_RGB58H_PERCEP(4)\ + ONETABLE_RGB58H_PERCEP(5)\ + ONETABLE_RGB58H_PERCEP(6)\ + ONETABLE_RGB58H_PERCEP(7)\ + /* end unroll loop */\ + + CALCULATE_ERROR_FROM_PRECALC_RGB58H_PERCEP + return error;\ +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Calculate a minimum error for the H-mode when doing exhaustive compression. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int calculateErrorFromPrecalc58H(int *colorsRGB444_packed, unsigned int *precalc_err, unsigned int total_best_err) +{ + unsigned int block_error;\ + unsigned int *precalc_col1, *precalc_col2;\ + unsigned int *precalc_col1tab, *precalc_col2tab;\ + + unsigned int error; + +#define FIRSTCHOICE_RGB58H(value)\ + if(precalc_col1tab[value] < precalc_col2tab[value])\ + block_error = precalc_col1tab[value];\ + else\ + block_error = precalc_col2tab[value];\ + +#define CHOICE_RGB58H(value)\ + if(precalc_col1tab[value] < precalc_col2tab[value])\ + block_error += precalc_col1tab[value];\ + else\ + block_error += precalc_col2tab[value];\ + +#define ONETABLE_RGB58H(distance)\ + precalc_col1tab = &precalc_col1[distance*16];\ + precalc_col2tab = &precalc_col2[distance*16];\ + /* unroll loop for(q = 0; q<16 && block_error < total_best_err; q++) */\ + FIRSTCHOICE_RGB58H(0)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H(1)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H(2)\ + CHOICE_RGB58H(3)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H(4)\ + CHOICE_RGB58H(5)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H(6)\ + CHOICE_RGB58H(7)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H(8)\ + CHOICE_RGB58H(9)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H(10)\ + CHOICE_RGB58H(11)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H(12)\ + CHOICE_RGB58H(13)\ + if( block_error < total_best_err)\ + {\ + CHOICE_RGB58H(14)\ + CHOICE_RGB58H(15)\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + }\ + /* end unroll loop */\ + if (block_error < error)\ + error = block_error;\ + +#define CALCULATE_ERROR_FROM_PRECALC_RGB58H\ + error = MAXIMUM_ERROR;\ + precalc_col1 = &precalc_err[colorsRGB444_packed[0]*8*16];\ + precalc_col2 = &precalc_err[colorsRGB444_packed[1]*8*16];\ + /* Test all distances*/\ + /* unroll loop for (uint8 d = 0; d < 8; ++d) */\ + ONETABLE_RGB58H(0)\ + ONETABLE_RGB58H(1)\ + ONETABLE_RGB58H(2)\ + ONETABLE_RGB58H(3)\ + ONETABLE_RGB58H(4)\ + ONETABLE_RGB58H(5)\ + ONETABLE_RGB58H(6)\ + ONETABLE_RGB58H(7)\ + /* end unroll loop */\ + + CALCULATE_ERROR_FROM_PRECALC_RGB58H + return error;\ +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// The below code should compress the block to 58 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// The bit layout is thought to be: +// +//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| +//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. +// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. +// Else, it is assumed to be 1. + +// The below code should compress the block to 58 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// The bit layout is thought to be: +// +//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| +//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. +// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. +// Else, it is assumed to be 1. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockTHUMB58HExhaustivePerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int best_error_so_far) +{ + unsigned int best_error_using_Hmode; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + unsigned int error; + uint8 colorsRGB444[2][3]; + int colorsRGB444_packed[2]; + int best_colorsRGB444_packed[2]; + int colorRGB444_packed; + unsigned int pixel_indices; + uint8 distance; + unsigned int *precalc_err; // smallest error per color, table and pixel + unsigned int *precalc_err_RG; // smallest pixel error for an entire table + unsigned int *precalc_err_R; // smallest pixel error for an entire table + uint8 block[4*4*4]; + + best_error_using_Hmode = MAXERR1000; + + precalc_err = (unsigned int*) malloc(4096*8*16*sizeof(unsigned int)); + if(!precalc_err){printf("Out of memory allocating \n");exit(1);} + + precalc_err_RG = (unsigned int*) malloc(16*16*8*16*sizeof(unsigned int)); + if(!precalc_err_RG){printf("Out of memory allocating \n");exit(1);} + + precalc_err_R = (unsigned int*) malloc(16*8*16*sizeof(unsigned int)); + if(!precalc_err_R){printf("Out of memory allocating \n");exit(1);} + + unsigned int test1, test2; + best_error_using_Hmode = (unsigned int)compressBlockTHUMB58HFastestPerceptual1000(img,width, height, startx, starty, test1, test2); + best_colorsRGB444_packed[0] = 0; + best_colorsRGB444_packed[0] = GETBITSHIGH(test1, 12, 57); + best_colorsRGB444_packed[1] = 0; + best_colorsRGB444_packed[1] = GETBITSHIGH(test1, 12, 45); + + if(best_error_using_Hmode < best_error_so_far) + best_error_so_far = best_error_using_Hmode; + + int xx,yy,count = 0; + + // Use 4 bytes per pixel to make it 32-word aligned. + for(xx = 0; xx<4; xx++) + { + for(yy=0; yy<4; yy++) + { + block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block[(count)*4+3] = 0; + count++; + } + } + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) + { + colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; + colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; + colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; + + precalcError58Hperceptual1000(block, colorsRGB444, colorRGB444_packed, precalc_err); + } + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) + { + colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; + colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; + colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; + precalcErrorRG_58Hperceptual1000(img, width, startx, starty, colorsRGB444, colorRGB444_packed, precalc_err_RG); + } + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) + { + colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; + colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; + colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; + precalcErrorR_58Hperceptual1000(img, width, startx, starty, colorsRGB444, colorRGB444_packed, precalc_err_R); + } + + int trycols = 0; + int allcols = 0; + + for( colorsRGB444[0][0] = 0; colorsRGB444[0][0] <16; colorsRGB444[0][0]++) + { + colorsRGB444_packed[0] = colorsRGB444[0][0]*256; + for( colorsRGB444[1][0] = 0; colorsRGB444[1][0] <16; colorsRGB444[1][0]++) + { + colorsRGB444_packed[1] = colorsRGB444[1][0]*256; + if(colorsRGB444_packed[0] <= colorsRGB444_packed[1]) + { + error = calculateErrorFromPrecalcR58Hperceptual1000(colorsRGB444_packed, precalc_err_R, best_error_so_far); + if(error < best_error_so_far) + { + for( colorsRGB444[0][1] = 0; colorsRGB444[0][1] <16; colorsRGB444[0][1]++) + { + colorsRGB444_packed[0] = colorsRGB444[0][0]*256 + colorsRGB444[0][1]*16; + for( colorsRGB444[1][1] = 0; colorsRGB444[1][1] <16; colorsRGB444[1][1]++) + { + colorsRGB444_packed[1] = colorsRGB444[1][0]*256 + colorsRGB444[1][1]*16; + if(colorsRGB444_packed[0] <= colorsRGB444_packed[1]) + { + error = calculateErrorFromPrecalcRG58Hperceptual1000(colorsRGB444_packed, precalc_err_RG, best_error_so_far); + if(error < best_error_so_far) + { + for( colorsRGB444[0][2] = 0; colorsRGB444[0][2] <16; colorsRGB444[0][2]++) + { + colorsRGB444_packed[0] = colorsRGB444[0][0]*256 + colorsRGB444[0][1]*16 + colorsRGB444[0][2]; + for( colorsRGB444[1][2] = 0; colorsRGB444[1][2] <16; colorsRGB444[1][2]++) + { + colorsRGB444_packed[1] = colorsRGB444[1][0]*256 + colorsRGB444[1][1]*16 + colorsRGB444[1][2]; + if(colorsRGB444_packed[0] < colorsRGB444_packed[1]) + { + error = calculateErrorFromPrecalc58Hperceptual1000(colorsRGB444_packed, precalc_err, best_error_so_far); + if(error < best_error_so_far) + { + best_error_so_far = error; + best_error_using_Hmode = error; + best_colorsRGB444_packed[0] = colorsRGB444_packed[0]; + best_colorsRGB444_packed[1] = colorsRGB444_packed[1]; + } + } + } + } + } + } + } + } + } + } + } + } + best_colorsRGB444[0][0] = (best_colorsRGB444_packed[0] >> 8) & 0xf; + best_colorsRGB444[0][1] = (best_colorsRGB444_packed[0] >> 4) & 0xf; + best_colorsRGB444[0][2] = (best_colorsRGB444_packed[0]) & 0xf; + best_colorsRGB444[1][0] = (best_colorsRGB444_packed[1] >> 8) & 0xf; + best_colorsRGB444[1][1] = (best_colorsRGB444_packed[1] >> 4) & 0xf; + best_colorsRGB444[1][2] = (best_colorsRGB444_packed[1]) & 0xf; + + free(precalc_err); + free(precalc_err_RG); + free(precalc_err_R); + + error = (unsigned int) calculateErrorAndCompress58Hperceptual1000(img, width, startx, starty, best_colorsRGB444, distance, pixel_indices); + best_distance = distance; + best_pixel_indices = pixel_indices; + + // | col0 >= col1 col0 < col1 + //------------------------------------------------------ + // (dist & 1) = 1 | no need to swap | need to swap + // |-----------------+---------------- + // (dist & 1) = 0 | need to swap | no need to swap + // + // This can be done with an xor test. + + best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; + best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; + if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) + { + swapColors(best_colorsRGB444); + + // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 + best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); + } + + // Put the compress params into the compression block + compressed1 = 0; + + PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); + PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); + best_pixel_indices=indexConversion(best_pixel_indices); + compressed2 = 0; + PUTBITS( compressed2, best_pixel_indices, 32, 31); + + return best_error_using_Hmode; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// The below code should compress the block to 58 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// The bit layout is thought to be: +// +//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| +//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. +// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. +// Else, it is assumed to be 1. + +// The below code should compress the block to 58 bits. +// This is supposed to match the first of the three modes in TWOTIMER. +// The bit layout is thought to be: +// +//|63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32| +//|-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// +// The distance d is three bits, d2 (MSB), d1 and d0 (LSB). d0 is not stored explicitly. +// Instead if the 12-bit word red0,green0,blue0 < red1,green1,blue1, d0 is assumed to be 0. +// Else, it is assumed to be 1. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +unsigned int compressBlockTHUMB58HExhaustive(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2, unsigned int best_error_so_far) +{ + unsigned int best_error_using_Hmode; + uint8 best_colorsRGB444[2][3]; + unsigned int best_pixel_indices; + uint8 best_distance; + + unsigned int error; + uint8 colorsRGB444[2][3]; + int colorsRGB444_packed[2]; + int best_colorsRGB444_packed[2]; + int colorRGB444_packed; + unsigned int pixel_indices; + uint8 distance; + unsigned int *precalc_err; // smallest error per color, table and pixel + unsigned int *precalc_err_RG; // smallest pixel error for an entire table + unsigned int *precalc_err_R; // smallest pixel error for an entire table + uint8 block[4*4*4]; + + best_error_using_Hmode = MAXIMUM_ERROR; + + precalc_err = (unsigned int*) malloc(4096*8*16*sizeof(unsigned int)); + if(!precalc_err){printf("Out of memory allocating \n");exit(1);} + + precalc_err_RG = (unsigned int*) malloc(16*16*8*16*sizeof(unsigned int)); + if(!precalc_err_RG){printf("Out of memory allocating \n");exit(1);} + + precalc_err_R = (unsigned int*) malloc(16*8*16*sizeof(unsigned int)); + if(!precalc_err_R){printf("Out of memory allocating \n");exit(1);} + + unsigned int test1, test2; + best_error_using_Hmode = (unsigned int)compressBlockTHUMB58HFastest(img,width, height, startx, starty, test1, test2); + best_colorsRGB444_packed[0] = 0; + best_colorsRGB444_packed[0] = GETBITSHIGH(test1, 12, 57); + best_colorsRGB444_packed[1] = 0; + best_colorsRGB444_packed[1] = GETBITSHIGH(test1, 12, 45); + + if(best_error_using_Hmode < best_error_so_far) + best_error_so_far = best_error_using_Hmode; + + int xx,yy,count = 0; + + // Reshuffle pixels so that the top left 2x2 pixels arrive first, then the top right 2x2 pixels etc. Also put use 4 bytes per pixel to make it 32-word aligned. + for(xx = 0; xx<4; xx++) + { + for(yy=0; yy<4; yy++) + { + block[(count)*4] = img[((starty+yy)*width+(startx+xx))*3]; + block[(count)*4+1] = img[((starty+yy)*width+(startx+xx))*3+1]; + block[(count)*4+2] = img[((starty+yy)*width+(startx+xx))*3+2]; + block[(count)*4+3] = 0; + count++; + } + } + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed++) + { + colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; + colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; + colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; + precalcError58H(block, colorsRGB444, colorRGB444_packed, precalc_err); + } + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16) + { + colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; + colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; + colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; + precalcErrorRG_58H(img, width, startx, starty, colorsRGB444, colorRGB444_packed, precalc_err_RG); + } + + for( colorRGB444_packed = 0; colorRGB444_packed<16*16*16; colorRGB444_packed+=16*16) + { + colorsRGB444[0][0] = (colorRGB444_packed >> 8) & 0xf; + colorsRGB444[0][1] = (colorRGB444_packed >> 4) & 0xf; + colorsRGB444[0][2] = (colorRGB444_packed) & 0xf; + precalcErrorR_58H(img, width, startx, starty, colorsRGB444, colorRGB444_packed, precalc_err_R); + } + + int trycols = 0; + int allcols = 0; + + for( colorsRGB444[0][0] = 0; colorsRGB444[0][0] <16; colorsRGB444[0][0]++) + { + colorsRGB444_packed[0] = colorsRGB444[0][0]*256; + for( colorsRGB444[1][0] = 0; colorsRGB444[1][0] <16; colorsRGB444[1][0]++) + { + colorsRGB444_packed[1] = colorsRGB444[1][0]*256; + if(colorsRGB444_packed[0] <= colorsRGB444_packed[1]) + { + error = calculateErrorFromPrecalcR58H(colorsRGB444_packed, precalc_err_R, best_error_so_far); + if(error < best_error_so_far) + { + for( colorsRGB444[0][1] = 0; colorsRGB444[0][1] <16; colorsRGB444[0][1]++) + { + colorsRGB444_packed[0] = colorsRGB444[0][0]*256 + colorsRGB444[0][1]*16; + for( colorsRGB444[1][1] = 0; colorsRGB444[1][1] <16; colorsRGB444[1][1]++) + { + colorsRGB444_packed[1] = colorsRGB444[1][0]*256 + colorsRGB444[1][1]*16; + if(colorsRGB444_packed[0] <= colorsRGB444_packed[1]) + { + error = calculateErrorFromPrecalcRG58H(colorsRGB444_packed, precalc_err_RG, best_error_so_far); + if(error < best_error_so_far) + { + for( colorsRGB444[0][2] = 0; colorsRGB444[0][2] <16; colorsRGB444[0][2]++) + { + colorsRGB444_packed[0] = colorsRGB444[0][0]*256 + colorsRGB444[0][1]*16 + colorsRGB444[0][2]; + for( colorsRGB444[1][2] = 0; colorsRGB444[1][2] <16; colorsRGB444[1][2]++) + { + colorsRGB444_packed[1] = colorsRGB444[1][0]*256 + colorsRGB444[1][1]*16 + colorsRGB444[1][2]; + if(colorsRGB444_packed[0] < colorsRGB444_packed[1]) + { + error = calculateErrorFromPrecalc58H(colorsRGB444_packed, precalc_err, best_error_so_far); + if(error < best_error_so_far) + { + best_error_so_far = error; + best_error_using_Hmode = error; + best_colorsRGB444_packed[0] = colorsRGB444_packed[0]; + best_colorsRGB444_packed[1] = colorsRGB444_packed[1]; + } + } + } + } + } + } + } + } + } + } + } + } + best_colorsRGB444[0][0] = (best_colorsRGB444_packed[0] >> 8) & 0xf; + best_colorsRGB444[0][1] = (best_colorsRGB444_packed[0] >> 4) & 0xf; + best_colorsRGB444[0][2] = (best_colorsRGB444_packed[0]) & 0xf; + best_colorsRGB444[1][0] = (best_colorsRGB444_packed[1] >> 8) & 0xf; + best_colorsRGB444[1][1] = (best_colorsRGB444_packed[1] >> 4) & 0xf; + best_colorsRGB444[1][2] = (best_colorsRGB444_packed[1]) & 0xf; + + free(precalc_err); + free(precalc_err_RG); + free(precalc_err_R); + + error = (unsigned int) calculateErrorAndCompress58H(img, width, startx, starty, best_colorsRGB444, distance, pixel_indices); + best_distance = distance; + best_pixel_indices = pixel_indices; + + // | col0 >= col1 col0 < col1 + //------------------------------------------------------ + // (dist & 1) = 1 | no need to swap | need to swap + // |-----------------+---------------- + // (dist & 1) = 0 | need to swap | no need to swap + // + // This can be done with an xor test. + + best_colorsRGB444_packed[0] = (best_colorsRGB444[0][R] << 8) + (best_colorsRGB444[0][G] << 4) + best_colorsRGB444[0][B]; + best_colorsRGB444_packed[1] = (best_colorsRGB444[1][R] << 8) + (best_colorsRGB444[1][G] << 4) + best_colorsRGB444[1][B]; + if( (best_colorsRGB444_packed[0] >= best_colorsRGB444_packed[1]) ^ ((best_distance & 1)==1) ) + { + swapColors(best_colorsRGB444); + + // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4 + best_pixel_indices = (0x55555555 & best_pixel_indices) | (0xaaaaaaaa & (~best_pixel_indices)); + } + + // Put the compress params into the compression block + compressed1 = 0; + + PUTBITSHIGH( compressed1, best_colorsRGB444[0][R], 4, 57); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][G], 4, 53); + PUTBITSHIGH( compressed1, best_colorsRGB444[0][B], 4, 49); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][R], 4, 45); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][G], 4, 41); + PUTBITSHIGH( compressed1, best_colorsRGB444[1][B], 4, 37); + PUTBITSHIGH( compressed1, (best_distance >> 1), 2, 33); + best_pixel_indices=indexConversion(best_pixel_indices); + compressed2 = 0; + PUTBITS( compressed2, best_pixel_indices, 32, 31); + + return best_error_using_Hmode; +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Compress a block exhaustively for the ETC1 codec. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockETC1Exhaustive(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int error_currently_best; + + unsigned int etc1_differential_word1; + unsigned int etc1_differential_word2; + unsigned int error_etc1_differential; + + unsigned int etc1_individual_word1; + unsigned int etc1_individual_word2; + unsigned int error_etc1_individual; + + unsigned int error_best; + signed char best_char; + int best_mode; + + error_currently_best = 255*255*16*3; + + // First pass -- quickly find a low error so that we can later cull away a lot of + // calculations later that are guaranteed to be higher than that error. + unsigned int error_etc1; + unsigned int etc1_word1; + unsigned int etc1_word2; + + error_etc1 = (unsigned int) compressBlockDiffFlipFast(img, imgdec, width, height, startx, starty, etc1_word1, etc1_word2); + if(error_etc1 < error_currently_best) + error_currently_best = error_etc1; + + error_etc1_individual = compressBlockIndividualExhaustive(img, width, height, startx, starty, etc1_individual_word1, etc1_individual_word2, error_currently_best); + if(error_etc1_individual < error_currently_best) + error_currently_best = error_etc1_individual; + + error_etc1_differential = compressBlockDifferentialExhaustive(img, width, height, startx, starty, etc1_differential_word1, etc1_differential_word2, error_currently_best); + if(error_etc1_differential < error_currently_best) + error_currently_best = error_etc1_differential; + + error_best = error_etc1_differential; + compressed1 = etc1_differential_word1; + compressed2 = etc1_differential_word2; + best_char = '.'; + best_mode = MODE_ETC1; + + if(error_etc1_individual < error_best) + { + compressed1 = etc1_individual_word1; + compressed2 = etc1_individual_word2; + best_char = ','; + error_best = error_etc1_individual; + best_mode = MODE_ETC1; + } + + if(error_etc1 < error_best) + { + compressed1 = etc1_word1; + compressed2 = etc1_word2; + best_char = '.'; + error_best = error_etc1; + best_mode = MODE_ETC1; + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Compress a block exhaustively for the ETC1 codec using perceptual error measure. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockETC1ExhaustivePerceptual(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int error_currently_best; + + unsigned int etc1_differential_word1; + unsigned int etc1_differential_word2; + unsigned int error_etc1_differential; + + unsigned int etc1_individual_word1; + unsigned int etc1_individual_word2; + unsigned int error_etc1_individual; + + unsigned int error_best; + signed char best_char; + int best_mode; + + + error_currently_best = 255*255*16*1000; + + // First pass -- quickly find a low error so that we can later cull away a lot of + // calculations later that are guaranteed to be higher than that error. + unsigned int error_etc1; + unsigned int etc1_word1; + unsigned int etc1_word2; + + compressBlockDiffFlipFastPerceptual(img, imgdec, width, height, startx, starty, etc1_word1, etc1_word2); + decompressBlockDiffFlip(etc1_word1, etc1_word2, imgdec, width, height, startx, starty); + error_etc1 = 1000*calcBlockPerceptualErrorRGB(img, imgdec, width, height, startx, starty); + if(error_etc1 < error_currently_best) + error_currently_best = error_etc1; + + // Second pass --- now find the lowest error, but only if it is lower than error_currently_best + + error_etc1_differential = compressBlockDifferentialExhaustivePerceptual(img, width, height, startx, starty, etc1_differential_word1, etc1_differential_word2, error_currently_best); + if(error_etc1_differential < error_currently_best) + error_currently_best = error_etc1_differential; + + error_etc1_individual = compressBlockIndividualExhaustivePerceptual(img, width, height, startx, starty, etc1_individual_word1, etc1_individual_word2, error_currently_best); + if(error_etc1_individual < error_currently_best) + error_currently_best = error_etc1_individual; + + // Now find the best error. + error_best = error_etc1; + compressed1 = etc1_word1; + compressed2 = etc1_word2; + best_char = '.'; + best_mode = MODE_ETC1; + + if(error_etc1_differential < error_best) + { + error_best = error_etc1_differential; + compressed1 = etc1_differential_word1; + compressed2 = etc1_differential_word2; + best_char = '.'; + best_mode = MODE_ETC1; + } + + if(error_etc1_individual < error_best) + { + compressed1 = etc1_individual_word1; + compressed2 = etc1_individual_word2; + best_char = ','; + error_best = error_etc1_individual; + best_mode = MODE_ETC1; + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Compress a block exhaustively for the ETC2 RGB codec using perceptual error measure. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockETC2ExhaustivePerceptual(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int error_currently_best; + + unsigned int etc1_differential_word1; + unsigned int etc1_differential_word2; + unsigned int error_etc1_differential; + + unsigned int etc1_individual_word1; + unsigned int etc1_individual_word2; + unsigned int error_etc1_individual; + + unsigned int planar57_word1; + unsigned int planar57_word2; + unsigned int planar_word1; + unsigned int planar_word2; + double error_planar; + unsigned int error_planar_red, error_planar_green, error_planar_blue; + + unsigned int thumbH58_word1; + unsigned int thumbH58_word2; + unsigned int thumbH_word1; + unsigned int thumbH_word2; + unsigned int error_thumbH; + + unsigned int thumbT59_word1; + unsigned int thumbT59_word2; + unsigned int thumbT_word1; + unsigned int thumbT_word2; + unsigned int error_thumbT; + + unsigned int error_best; + signed char best_char; + int best_mode; + + error_currently_best = 255*255*16*1000; + + // First pass -- quickly find a low error so that we can later cull away a lot of + // calculations later that are guaranteed to be higher than that error. + unsigned int error_etc1; + unsigned int etc1_word1; + unsigned int etc1_word2; + + compressBlockDiffFlipFastPerceptual(img, imgdec, width, height, startx, starty, etc1_word1, etc1_word2); + decompressBlockDiffFlip(etc1_word1, etc1_word2, imgdec, width, height, startx, starty); + error_etc1 = 1000*calcBlockPerceptualErrorRGB(img, imgdec, width, height, startx, starty); + if(error_etc1 < error_currently_best) + error_currently_best = error_etc1; + + // The planar mode treats every channel independently and should not be affected by the weights in the error measure. + // We can hence use the nonperceptual version of the encoder also to find the best perceptual description of the block. + compressBlockPlanar57(img, width, height, startx, starty, planar57_word1, planar57_word2); + decompressBlockPlanar57errorPerComponent(planar57_word1, planar57_word2, imgdec, width, height, startx, starty, img, error_planar_red, error_planar_green, error_planar_blue); + error_planar = 1000*calcBlockPerceptualErrorRGB(img, imgdec, width, height, startx, starty); + stuff57bits(planar57_word1, planar57_word2, planar_word1, planar_word2); + if(error_planar < error_currently_best) + error_currently_best = (unsigned int) error_planar; + + error_thumbT = (unsigned int) compressBlockTHUMB59TFastestPerceptual1000(img,width, height, startx, starty, thumbT59_word1, thumbT59_word2); + stuff59bits(thumbT59_word1, thumbT59_word2, thumbT_word1, thumbT_word2); + if(error_thumbT < error_currently_best) + error_currently_best = error_thumbT; + + error_thumbH = (unsigned int) compressBlockTHUMB58HFastestPerceptual1000(img,width,height,startx, starty, thumbH58_word1, thumbH58_word2); + stuff58bits(thumbH58_word1, thumbH58_word2, thumbH_word1, thumbH_word2); + if(error_thumbH < error_currently_best) + error_currently_best = error_thumbH; + + // Second pass --- now find the lowest error, but only if it is lower than error_currently_best + + // Correct the individual errors for the different planes so that they sum to 1000 instead of 1. + error_planar_red *=PERCEPTUAL_WEIGHT_R_SQUARED_TIMES1000; + error_planar_green *=PERCEPTUAL_WEIGHT_G_SQUARED_TIMES1000; + error_planar_blue *=PERCEPTUAL_WEIGHT_B_SQUARED_TIMES1000; + compressBlockPlanar57ExhaustivePerceptual(img, width, height, startx, starty, planar57_word1, planar57_word2, error_currently_best, error_planar_red, error_planar_green, error_planar_blue); + decompressBlockPlanar57(planar57_word1, planar57_word2, imgdec, width, height, startx, starty); + error_planar = 1000*calcBlockPerceptualErrorRGB(img, imgdec, width, height, startx, starty); + stuff57bits(planar57_word1, planar57_word2, planar_word1, planar_word2); + if(error_planar < error_currently_best) + error_currently_best = (unsigned int) error_planar; + + error_etc1_differential = compressBlockDifferentialExhaustivePerceptual(img, width, height, startx, starty, etc1_differential_word1, etc1_differential_word2, error_currently_best); + if(error_etc1_differential < error_currently_best) + error_currently_best = error_etc1_differential; + + error_etc1_individual = compressBlockIndividualExhaustivePerceptual(img, width, height, startx, starty, etc1_individual_word1, etc1_individual_word2, error_currently_best); + if(error_etc1_individual < error_currently_best) + error_currently_best = error_etc1_individual; + + error_thumbH = compressBlockTHUMB58HExhaustivePerceptual(img,width,height,startx, starty, thumbH58_word1, thumbH58_word2, error_currently_best); + stuff58bits(thumbH58_word1, thumbH58_word2, thumbH_word1, thumbH_word2); + if( error_thumbH < error_currently_best) + error_currently_best = error_thumbH; + + error_thumbT = compressBlockTHUMB59TExhaustivePerceptual(img,width, height, startx, starty, thumbT59_word1, thumbT59_word2, error_currently_best); + stuff59bits(thumbT59_word1, thumbT59_word2, thumbT_word1, thumbT_word2); + if(error_thumbT < error_currently_best) + error_currently_best = error_thumbT; + + // Now find the best error. + error_best = error_etc1; + compressed1 = etc1_word1; + compressed2 = etc1_word2; + best_char = '.'; + best_mode = MODE_ETC1; + + if(error_etc1_differential < error_best) + { + error_best = error_etc1_differential; + compressed1 = etc1_differential_word1; + compressed2 = etc1_differential_word2; + best_char = '.'; + best_mode = MODE_ETC1; + } + + if(error_etc1_individual < error_best) + { + compressed1 = etc1_individual_word1; + compressed2 = etc1_individual_word2; + best_char = ','; + error_best = error_etc1_individual; + best_mode = MODE_ETC1; + } + if(error_planar < error_best) + { + compressed1 = planar_word1; + compressed2 = planar_word2; + best_char = 'p'; + error_best = (unsigned int) error_planar; + best_mode = MODE_PLANAR; + } + if(error_thumbH < error_best) + { + compressed1 = thumbH_word1; + compressed2 = thumbH_word2; + best_char = 'H'; + error_best = error_thumbH; + best_mode = MODE_THUMB_H; + } + if(error_thumbT < error_best) + { + compressed1 = thumbT_word1; + compressed2 = thumbT_word2; + best_char = 'T'; + error_best = error_thumbT; + best_mode = MODE_THUMB_T; + } +} +#endif + +#if EXHAUSTIVE_CODE_ACTIVE +// Compress a block exhaustively for the ETC2 RGB codec. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressBlockETC2Exhaustive(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int error_currently_best; + + unsigned int etc1_differential_word1; + unsigned int etc1_differential_word2; + unsigned int error_etc1_differential; + + unsigned int etc1_individual_word1; + unsigned int etc1_individual_word2; + unsigned int error_etc1_individual; + + unsigned int planar57_word1; + unsigned int planar57_word2; + unsigned int planar_word1; + unsigned int planar_word2; + double error_planar; + unsigned int error_planar_red; + unsigned int error_planar_green; + unsigned int error_planar_blue; + + unsigned int thumbH58_word1; + unsigned int thumbH58_word2; + unsigned int thumbH_word1; + unsigned int thumbH_word2; + unsigned int error_thumbH; + + unsigned int thumbT59_word1; + unsigned int thumbT59_word2; + unsigned int thumbT_word1; + unsigned int thumbT_word2; + unsigned int error_thumbT; + + unsigned int error_best; + signed char best_char; + int best_mode; + + error_currently_best = 255*255*16*3; + + // First pass -- quickly find a low error so that we can later cull away a lot of + // calculations later that are guaranteed to be higher than that error. + unsigned int error_etc1; + unsigned int etc1_word1; + unsigned int etc1_word2; + + error_etc1 = (unsigned int) compressBlockDiffFlipFast(img, imgdec, width, height, startx, starty, etc1_word1, etc1_word2); + if(error_etc1 < error_currently_best) + error_currently_best = error_etc1; + + compressBlockPlanar57(img, width, height, startx, starty, planar57_word1, planar57_word2); + decompressBlockPlanar57errorPerComponent(planar57_word1, planar57_word2, imgdec, width, height, startx, starty, img, error_planar_red, error_planar_green, error_planar_blue); + error_planar = calcBlockErrorRGB(img, imgdec, width, height, startx, starty); + stuff57bits(planar57_word1, planar57_word2, planar_word1, planar_word2); + if(error_planar < error_currently_best) + error_currently_best = (unsigned int) error_planar; + + error_thumbT = (unsigned int) compressBlockTHUMB59TFastest(img,width, height, startx, starty, thumbT59_word1, thumbT59_word2); + stuff59bits(thumbT59_word1, thumbT59_word2, thumbT_word1, thumbT_word2); + if(error_thumbT < error_currently_best) + error_currently_best = error_thumbT; + + error_thumbH = (unsigned int) compressBlockTHUMB58HFastest(img,width,height,startx, starty, thumbH58_word1, thumbH58_word2); + stuff58bits(thumbH58_word1, thumbH58_word2, thumbH_word1, thumbH_word2); + if(error_thumbH < error_currently_best) + error_currently_best = error_thumbH; + + // Second pass --- now find the lowest error, but only if it is lower than error_currently_best + error_etc1_differential = compressBlockDifferentialExhaustive(img, width, height, startx, starty, etc1_differential_word1, etc1_differential_word2, error_currently_best); + if(error_etc1_differential < error_currently_best) + error_currently_best = error_etc1_differential; + + compressBlockPlanar57Exhaustive(img, width, height, startx, starty, planar57_word1, planar57_word2, error_currently_best, error_planar_red, error_planar_green, error_planar_blue); + decompressBlockPlanar57(planar57_word1, planar57_word2, imgdec, width, height, startx, starty); + error_planar = calcBlockErrorRGB(img, imgdec, width, height, startx, starty); + stuff57bits(planar57_word1, planar57_word2, planar_word1, planar_word2); + if(error_planar < error_currently_best) + error_currently_best = (unsigned int) error_planar; + + error_etc1_individual = compressBlockIndividualExhaustive(img, width, height, startx, starty, etc1_individual_word1, etc1_individual_word2, error_currently_best); + if(error_etc1_individual < error_currently_best) + error_currently_best = error_etc1_individual; + + error_thumbH = compressBlockTHUMB58HExhaustive(img,width,height,startx, starty, thumbH58_word1, thumbH58_word2, error_currently_best); + if( error_thumbH < error_currently_best) + error_currently_best = error_thumbH; + stuff58bits(thumbH58_word1, thumbH58_word2, thumbH_word1, thumbH_word2); + + error_thumbT = compressBlockTHUMB59TExhaustive(img,width, height, startx, starty, thumbT59_word1, thumbT59_word2, error_currently_best); + if(error_thumbT < error_currently_best) + error_currently_best = error_thumbT; + stuff59bits(thumbT59_word1, thumbT59_word2, thumbT_word1, thumbT_word2); + + error_best = 255*255*3*16; + // Now find the best error. + error_best = error_etc1; + compressed1 = etc1_word1; + compressed2 = etc1_word2; + best_char = '.'; + best_mode = MODE_ETC1; + + if(error_etc1_differential < error_best) + { + error_best = error_etc1_differential; + compressed1 = etc1_differential_word1; + compressed2 = etc1_differential_word2; + best_char = '.'; + best_mode = MODE_ETC1; + } + if(error_etc1_individual < error_best) + { + compressed1 = etc1_individual_word1; + compressed2 = etc1_individual_word2; + best_char = ','; + error_best = error_etc1_individual; + best_mode = MODE_ETC1; + } + if(error_planar < error_best) + { + compressed1 = planar_word1; + compressed2 = planar_word2; + best_char = 'p'; + error_best = (unsigned int) error_planar; + best_mode = MODE_PLANAR; + } + if(error_thumbH < error_best) + { + compressed1 = thumbH_word1; + compressed2 = thumbH_word2; + best_char = 'H'; + error_best = error_thumbH; + best_mode = MODE_THUMB_H; + } + if(error_thumbT < error_best) + { + compressed1 = thumbT_word1; + compressed2 = thumbT_word2; + best_char = 'T'; + error_best = error_thumbT; + best_mode = MODE_THUMB_T; + } +} +#endif + +//// Exhaustive code ends here. + + +// Compress an image file. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressImageFile(uint8 *img, uint8 *alphaimg,int width,int height,char *dstfile, int expandedwidth, int expandedheight) +{ + FILE *f; + int x,y,w,h; + unsigned int block1, block2; + unsigned short wi, hi; + unsigned char magic[4]; + unsigned char version[2]; + unsigned short texture_type=format; + uint8 *imgdec; + uint8* alphaimg2; + imgdec = (unsigned char*) malloc(expandedwidth*expandedheight*3); + if(!imgdec) + { + printf("Could not allocate decompression buffer --- exiting\n"); + } + + magic[0] = 'P'; magic[1] = 'K'; magic[2] = 'M'; magic[3] = ' '; + + if(codec==CODEC_ETC2) + { + version[0] = '2'; version[1] = '0'; + } + else + { + version[0] = '1'; version[1] = '0'; + } + + if(f=fopen(dstfile,"wb")) + { + w=expandedwidth/4; w*=4; + h=expandedheight/4; h*=4; + wi = w; + hi = h; + if(ktxFile) + { + //.ktx file: KTX header followed by compressed binary data. + KTX_header header; + //identifier + for(int i=0; i<12; i++) + { + header.identifier[i]=ktx_identifier[i]; + } + //endianess int.. if this comes out reversed, all of the other ints will too. + header.endianness=KTX_ENDIAN_REF; + + //these values are always 0/1 for compressed textures. + header.glType=0; + header.glTypeSize=1; + header.glFormat=0; + + header.pixelWidth=width; + header.pixelHeight=height; + header.pixelDepth=0; + + //we only support single non-mipmapped non-cubemap textures.. + header.numberOfArrayElements=0; + header.numberOfFaces=1; + header.numberOfMipmapLevels=1; + + //and no metadata.. + header.bytesOfKeyValueData=0; + + int halfbytes=1; + //header.glInternalFormat=? + //header.glBaseInternalFormat=? + if(format==ETC2PACKAGE_R_NO_MIPMAPS) + { + header.glBaseInternalFormat=GL_R; + if(formatSigned) + header.glInternalFormat=GL_COMPRESSED_SIGNED_R11_EAC; + else + header.glInternalFormat=GL_COMPRESSED_R11_EAC; + } + else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) + { + halfbytes=2; + header.glBaseInternalFormat=GL_RG; + if(formatSigned) + header.glInternalFormat=GL_COMPRESSED_SIGNED_RG11_EAC; + else + header.glInternalFormat=GL_COMPRESSED_RG11_EAC; + } + else if(format==ETC2PACKAGE_RGB_NO_MIPMAPS) + { + header.glBaseInternalFormat=GL_RGB; + header.glInternalFormat=GL_COMPRESSED_RGB8_ETC2; + } + else if(format==ETC2PACKAGE_sRGB_NO_MIPMAPS) + { + header.glBaseInternalFormat=GL_SRGB; + header.glInternalFormat=GL_COMPRESSED_SRGB8_ETC2; + } + else if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS) + { + halfbytes=2; + header.glBaseInternalFormat=GL_RGBA; + header.glInternalFormat=GL_COMPRESSED_RGBA8_ETC2_EAC; + } + else if(format==ETC2PACKAGE_sRGBA_NO_MIPMAPS) + { + halfbytes=2; + header.glBaseInternalFormat=GL_SRGB8_ALPHA8; + header.glInternalFormat=GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + } + else if(format==ETC2PACKAGE_RGBA1_NO_MIPMAPS) + { + header.glBaseInternalFormat=GL_RGBA; + header.glInternalFormat=GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + } + else if(format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) + { + header.glBaseInternalFormat=GL_SRGB8_ALPHA8; + header.glInternalFormat=GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; + } + else if(format==ETC1_RGB_NO_MIPMAPS) + { + header.glBaseInternalFormat=GL_RGB; + header.glInternalFormat=GL_ETC1_RGB8_OES; + } + else + { + printf("internal error: bad format!\n"); + exit(1); + } + //write header + fwrite(&header,sizeof(KTX_header),1,f); + + //write size of compressed data.. which depend on the expanded size.. + unsigned int imagesize=(w*h*halfbytes)/2; + fwrite(&imagesize,sizeof(int),1,f); + } + else + { + //.pkm file, contains small header.. + + // Write magic number + fwrite(&magic[0], sizeof(unsigned char), 1, f); + fwrite(&magic[1], sizeof(unsigned char), 1, f); + fwrite(&magic[2], sizeof(unsigned char), 1, f); + fwrite(&magic[3], sizeof(unsigned char), 1, f); + + // Write version + fwrite(&version[0], sizeof(unsigned char), 1, f); + fwrite(&version[1], sizeof(unsigned char), 1, f); + + // Write texture type + if(texture_type==ETC2PACKAGE_RG_NO_MIPMAPS&&formatSigned) + { + unsigned short temp = ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS; + write_big_endian_2byte_word(&temp,f); + } + else if(texture_type==ETC2PACKAGE_R_NO_MIPMAPS&&formatSigned) + { + unsigned short temp = ETC2PACKAGE_R_SIGNED_NO_MIPMAPS; + write_big_endian_2byte_word(&temp,f); + } + else + write_big_endian_2byte_word(&texture_type, f); + + // Write binary header: the width and height as unsigned 16-bit words + write_big_endian_2byte_word(&wi, f); + write_big_endian_2byte_word(&hi, f); + + // Also write the active pixels. For instance, if we want to compress + // a 128 x 129 image, we have to extend it to 128 x 132 pixels. + // Then the wi and hi written above will be 128 and 132, but the + // additional information that we write below will be 128 and 129, + // to indicate that it is only the top 129 lines of data in the + // decompressed image that will be valid data, and the rest will + // be just garbage. + + unsigned short activew, activeh; + activew = width; + activeh = height; + + write_big_endian_2byte_word(&activew, f); + write_big_endian_2byte_word(&activeh, f); + } + int totblocks = expandedheight/4 * expandedwidth/4; + int countblocks = 0; + double percentageblocks=-1.0; + double oldpercentageblocks; + + if(format==ETC2PACKAGE_RG_NO_MIPMAPS) + { + //extract data from red and green channel into two alpha channels. + //note that the image will be 16-bit per channel in this case. + alphaimg= (unsigned char*)malloc(expandedwidth*expandedheight*2); + alphaimg2=(unsigned char*)malloc(expandedwidth*expandedheight*2); + setupAlphaTableAndValtab(); + if(!alphaimg||!alphaimg2) + { + printf("failed allocating space for alpha buffers!\n"); + exit(1); + } + for(y=0;y.\n",dstfile); + } +} + +// Compress an file. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void compressFile(char *srcfile,char *dstfile) +{ + uint8 *srcimg; + int width,height; + int extendedwidth, extendedheight; + struct _timeb tstruct; + int tstart; + int tstop; + // 0: compress from .any to .pkm with SPEED_FAST, METRIC_NONPERCEPTUAL, ETC + // 1: compress from .any to .pkm with SPEED_MEDIUM, METRIC_NONPERCEPTUAL, ETC + // 2: compress from .any to .pkm with SPEED_SLOW, METRIC_NONPERCEPTUAL, ETC + // 3: compress from .any to .pkm with SPEED_FAST, METRIC_PERCEPTUAL, ETC + // 4: compress from .any to .pkm with SPEED_MEDIUM, METRIC_PERCEPTUAL, ETC + // 5: compress from .any to .pkm with SPEED_SLOW, METRIC_PERCEPTUAL, ETC + // 6: decompress from .pkm to .any + // 7: calculate PSNR between .any and .any + // 8: compress from .any to .pkm with SPEED_FAST, METRIC_NONPERCEPTUAL, ETC2 + // 9: compress from .any to .pkm with SPEED_MEDIUM, METRIC_NONPERCEPTUAL, ETC2 + //10: compress from .any to .pkm with SPEED_SLOW, METRIC_NONPERCEPTUAL, ETC2 + //11: compress from .any to .pkm with SPEED_FAST, METRIC_PERCEPTUAL, ETC2 + //12: compress from .any to .pkm with SPEED_MEDIUM, METRIC_PERCEPTUAL, ETC2 + //13: compress from .any to .pkm with SPEED_SLOW, METRIC_PERCEPTUAL, ETC2 + + printf("\n"); + if(codec==CODEC_ETC) + printf("ETC codec, "); + else + printf("ETC2 codec, "); + if(speed==SPEED_FAST) + printf("using FAST compression mode and "); + else if(speed==SPEED_MEDIUM) + printf("using MEDIUM compression mode and "); + else + printf("using SLOW compression mode and "); + if(metric==METRIC_PERCEPTUAL) + printf("PERCEPTUAL error metric, "); + else + printf("NONPERCEPTUAL error metric, "); + if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS) + printf("in RGBA format"); + else if(format==ETC2PACKAGE_sRGBA_NO_MIPMAPS) + printf("in sRGBA format"); + else if(format==ETC2PACKAGE_RGBA1_NO_MIPMAPS) + printf("in RGB + punch-through alpha format"); + else if(format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) + printf("in sRGB + punch-through alpha format"); + else if(format==ETC2PACKAGE_R_NO_MIPMAPS) + printf("in R format"); + else if(format==ETC2PACKAGE_RGB_NO_MIPMAPS||format==ETC1_RGB_NO_MIPMAPS) + printf("in RGB format"); + else if(format==ETC2PACKAGE_RG_NO_MIPMAPS) + printf("in RG format"); + else + printf("in OTHER format"); + printf("\n"); + if(readCompressParams()) + { + if(format==ETC2PACKAGE_R_NO_MIPMAPS||readSrcFile(srcfile,srcimg,width,height,extendedwidth, extendedheight)) + { + //make sure that alphasrcimg contains the alpha channel or is null here, and pass it to compressimagefile + uint8* alphaimg=NULL; + if(format==ETC2PACKAGE_RGBA_NO_MIPMAPS||format==ETC2PACKAGE_RGBA1_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA_NO_MIPMAPS||format==ETC2PACKAGE_sRGBA1_NO_MIPMAPS) + { + char str[300]; + //printf("reading alpha channel...."); + sprintf(str,"magick convert %s -alpha extract alpha.pgm\n",srcfile); + system(str); + readAlpha(alphaimg,width,height,extendedwidth,extendedheight); + printf("ok!\n"); + setupAlphaTableAndValtab(); + } + else if(format==ETC2PACKAGE_R_NO_MIPMAPS) + { + char str[300]; + sprintf(str,"magick convert %s alpha.pgm\n",srcfile); + system(str); + readAlpha(alphaimg,width,height,extendedwidth,extendedheight); + printf("read alpha ok, size is %d,%d (%d,%d)",width,height,extendedwidth,extendedheight); + setupAlphaTableAndValtab(); + } + printf("Compressing...\n"); + + tstart=time(NULL); + _ftime( &tstruct ); + tstart=tstart*1000+tstruct.millitm; + compressImageFile(srcimg,alphaimg,width,height,dstfile,extendedwidth, extendedheight); + tstop = time(NULL); + _ftime( &tstruct ); + tstop = tstop*1000+tstruct.millitm; + printf( "It took %u milliseconds to compress:\n", tstop - tstart); + calculatePSNRfile(dstfile,srcimg,alphaimg); + } + } +} + +// Calculates the PSNR between two files. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +double calculatePSNRTwoFiles(char *srcfile1,char *srcfile2) +{ + uint8 *srcimg1; + uint8 *srcimg2; + int width1, height1; + int width2, height2; + double PSNR; + double perceptually_weighted_PSNR; + + if(readSrcFileNoExpand(srcfile1,srcimg1,width1,height1)) + { + if(readSrcFileNoExpand(srcfile2,srcimg2,width2,height2)) + { + if((width1 == width2) && (height1 == height2)) + { + PSNR = calculatePSNR(srcimg1, srcimg2, width1, height1); + printf("%f\n",PSNR); + perceptually_weighted_PSNR = calculateWeightedPSNR(srcimg1, srcimg2, width1, height1, 0.299, 0.587, 0.114); + } + else + { + printf("\n Width and height do no not match for image: width, height = (%d, %d) and (%d, %d)\n",width1,height1, width2, height2); + } + } + else + { + printf("Couldn't open file %s.\n",srcfile2); + } + } + else + { + printf("Couldn't open file %s.\n",srcfile1); + } + + return PSNR; +} + +// Main function +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int main(int argc,char *argv[]) +{ + if(argc==3 || argc==4 || argc == 5 || argc == 7 || argc == 9 || argc == 11 || argc == 13) + { + // The source file is always the second last one. + char srcfile[200]; + char dstfile[200]; + readArguments(argc,argv,srcfile,dstfile); + + int q = find_pos_of_extension(srcfile); + int q2 = find_pos_of_extension(dstfile); + + if(!fileExist(srcfile)) + { + printf("Error: file <%s> does not exist.\n",srcfile); + exit(0); + } + + if(mode==MODE_UNCOMPRESS) + { + printf("Decompressing .pkm/.ktx file ...\n"); + uint8* alphaimg=NULL, *img; + int w, h; + uncompressFile(srcfile,img,alphaimg,w,h); + writeOutputFile(dstfile,img,alphaimg,w,h); + } + else if(mode==MODE_PSNR) + { + calculatePSNRTwoFiles(srcfile,dstfile); + } + else + { + compressFile(srcfile, dstfile); + } + } + else + { + printf("ETCPACK v2.74 For ETC and ETC2\n"); + printf("Compresses and decompresses images using the Ericsson Texture Compression (ETC) version 1.0 and 2.0.\n\nUsage: etcpack srcfile dstfile\n\n"); + printf(" -s {fast|slow} Compression speed. Slow = exhaustive \n"); + printf(" search for optimal quality\n"); + printf(" (default: fast)\n"); + printf(" -e {perceptual|nonperceptual} Error metric: Perceptual (nicest) or \n"); + printf(" nonperceptual (highest PSNR)\n"); + printf(" (default: perceptual)\n"); + printf(" -c {etc1|etc2} Codec: etc1 (most compatible) or \n"); + printf(" etc2 (highest quality)\n"); + printf(" (default: etc2)\n"); + printf(" -f {R|R_signed|RG|RG_signed| Format: one, two, three or four \n"); + printf(" RGB|RGBA1|RGBA8| channels, and 1 or 8 bits for alpha\n"); + printf(" sRGB|sRGBA1|sRGBA8|} RGB or sRGB.\n"); + printf(" (1 equals punchthrough)\n"); + printf(" (default: RGB)\n"); + printf(" -v {on|off} Detailed progress info. (default on)\n"); + printf(" \n"); + printf("Examples: \n"); + printf(" etcpack img.ppm img.pkm Compresses img.ppm to img.pkm in\n"); + printf(" ETC2 RGB format\n"); + printf(" etcpack img.ppm img.ktx Compresses img.ppm to img.ktx in\n"); + printf(" ETC2 RGB format\n"); + printf(" etcpack img.pkm img_copy.ppm Decompresses img.pkm to img_copy.ppm\n"); + printf(" etcpack -s slow img.ppm img.pkm Compress using the slow mode.\n"); + printf(" etcpack -p orig.ppm copy.ppm Calculate PSNR between orig and copy\n"); + printf(" etcpack -f RGBA8 img.tga img.pkm Compresses img.tga to img.pkm, using \n"); + printf(" etc2 + alpha.\n"); + printf(" etcpack -f RG img.ppm img.pkm Compresses red and green channels of\n"); + printf(" img.ppm\n"); + } + return 0; +} diff --git a/source/image.cxx b/source/image.cpp old mode 100755 new mode 100644 similarity index 92% rename from source/image.cxx rename to source/image.cpp index 8ab8c66..d0e938d --- a/source/image.cxx +++ b/source/image.cpp @@ -1,461 +1,461 @@ -//// etcpack v2.74 -//// -//// NO WARRANTY -//// -//// BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE THE PROGRAM IS PROVIDED -//// "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF ANY KIND, EXTENDS NO -//// WARRANTIES OR CONDITIONS OF ANY KIND; EITHER EXPRESS, IMPLIED OR -//// STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, IMPLIED OR -//// STATUTORY WARRANTIES OR CONDITIONS OF TITLE, MERCHANTABILITY, -//// SATISFACTORY QUALITY, SUITABILITY AND FITNESS FOR A PARTICULAR -//// PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -//// PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME -//// THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ERICSSON -//// MAKES NO WARRANTY THAT THE MANUFACTURE, SALE, OFFERING FOR SALE, -//// DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER THE LICENSE WILL BE FREE -//// FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER INTELLECTUAL -//// PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE LICENSE IS SUBJECT -//// TO YOUR SOLE RESPONSIBILITY TO MAKE SUCH DETERMINATION AND ACQUIRE -//// SUCH LICENSES AS MAY BE NECESSARY WITH RESPECT TO PATENTS, COPYRIGHT -//// AND OTHER INTELLECTUAL PROPERTY OF THIRD PARTIES. -//// -//// FOR THE AVOIDANCE OF DOUBT THE PROGRAM (I) IS NOT LICENSED FOR; (II) -//// IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY NOT BE USED FOR; -//// ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT LIMITED TO -//// OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR NETWORKS, -//// AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR ANY OTHER -//// COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR COMMUNICATION -//// SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE PROGRAM COULD LEAD TO -//// DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR ENVIRONMENTAL -//// DAMAGE. YOUR RIGHTS UNDER THIS LICENSE WILL TERMINATE AUTOMATICALLY -//// AND IMMEDIATELY WITHOUT NOTICE IF YOU FAIL TO COMPLY WITH THIS -//// PARAGRAPH. -//// -//// IN NO EVENT WILL ERICSSON, BE LIABLE FOR ANY DAMAGES WHATSOEVER, -//// INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, -//// INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN -//// CONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT -//// NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY OTHER -//// COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING RENDERED -//// INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF -//// THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) REGARDLESS OF THE -//// THEORY OF LIABILITY (CONTRACT, TORT OR OTHERWISE), EVEN IF SUCH HOLDER -//// OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -//// -//// (C) Ericsson AB 2005-2013. All Rights Reserved. -//// - -#include -#include -#include -#include "image.h" - -// Remove warnings for unsafe functions such as strcpy -#pragma warning(disable : 4996) -// Remove warnings for conversions between different time variables -#pragma warning(disable : 4244) - -// Removes comments in a .ppm file -// (i.e., lines starting with #) -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void removeComments(FILE *f1) -{ - int c; - - while((c = getc(f1)) == '#') - { - char line[1024]; - fgets(line, 1024, f1); - } - ungetc(c, f1); -} - - -// Removes white spaces in a .ppm file -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -void removeSpaces(FILE *f1) -{ - int c; - - c = getc(f1); - while(c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r') - { - c = getc(f1); - } - ungetc(c, f1); -} - -// fReadPPM -// -// reads a ppm file with P6 header (meaning binary, as opposed to P5, which is ascII) -// and returns the image in pixels. -// -// The header must look like this: -// -// P6 -// # Comments (not necessary) -// width height -// 255 -// -// after that follows RGBRGBRGB... -// -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -bool fReadPPM(char *filename, int &width, int &height, unsigned char *&pixels, int targetbitrate) -{ - FILE *f1; - int maximum; - f1 = fopen(filename, "rb"); - - if(f1) - { - char line[255]; - - removeSpaces(f1); - removeComments(f1); - removeSpaces(f1); - - fscanf(f1, "%s", line); - - if(strcmp(line, "P6")!=0) - { - printf("Error: %s is not binary\n"); - printf("(Binary .ppm files start with P6).\n"); - fclose(f1); - return false; - } - removeSpaces(f1); - removeComments(f1); - removeSpaces(f1); - - fscanf(f1, "%d %d", &width, &height); - if( width<=0 || height <=0) - { - printf("Error: width or height negative. File: %s\n",filename); - fclose(f1); - return false; - } - - removeSpaces(f1); - removeComments(f1); - removeSpaces(f1); - - fscanf(f1, "%d", &maximum); - if( maximum!= 255&&maximum!=(1<<16)-1) - { - printf("Error: Color resolution must be 255. File: %s\n",filename); - fclose(f1); - return false; - } - - //printf("maximum is %d\n",maximum); - int bitrate=8; - if(maximum!=255) - bitrate=16; - - // We need to remove the newline. - char c = 0; - while(c != '\n') - fscanf(f1, "%c", &c); - - unsigned char* readbuffer = (unsigned char*) malloc(3*width*height*bitrate/8); - if(!readbuffer) - { - printf("Error: Could not allocate memory for image. File: %s\n", filename); - fclose(f1); - return false; - } - - if(fread(readbuffer, 3*width*height*bitrate/8, 1, f1) != 1) - { - printf("Error: Could not read all pixels. File: %s\n", filename); - free(pixels); - fclose(f1); - return false; - } - - // If we have reached this point, we have successfully loaded the image. - - //now, convert it to the target bitrate - if(targetbitrate==bitrate) - pixels=readbuffer; - else - { - pixels = (unsigned char*) malloc(3*width*height*targetbitrate/8); - if(targetbitrate 8 bits.. - printf("converting 16 bit input to 8 bits\n"); - for(int x=0; x 16 bits... - printf("converting 8 bit input to 16 bits\n"); - for(int x=0; x.\n",filename); - return false; - } -} - -/* reads a ppm file with the P6 header (means raw RGB), puts data into pixel pointer and returns bit depth (8 or 16 bpp) */ -/* the header looks like this: - *--------- - * P5 - * # comments if you want to - * width height - * 255 - *--------- - * then follows RGBRGBRGBRGBRGB... - */ -// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. -int fReadPGM(char *filename, int &width, int &height, unsigned char *&pixels, int wantedBitDepth) -{ - FILE *f; - int colres; - int bitdepth=8; - f=fopen(filename,"rb"); - if(f) - { - char str[100]; - removeSpaces(f); - removeComments(f); - removeSpaces(f); - fscanf(f,"%s",str); - if(strcmp(str,"P5")!=0) - { - printf("Error: the alpha image file must be of raw color PGM format,\n"); - printf("i.e., it must have P5 in the header. File: %s\n",filename); - fclose(f); - return 0; - } - removeSpaces(f); - removeComments(f); - removeSpaces(f); - fscanf(f,"%d %d",&width,&height); - if(width<=0 || height<=0) - { - printf("Error: width and height of the image must be greater than zero. File: %s\n",filename); - fclose(f); - return 0; - } - removeSpaces(f); - removeComments(f); - removeSpaces(f); - fscanf(f,"%d",&colres); - if(colres!=255&&colres!=65535) - { - printf("Error: color resolution must be 255 or 65535.File: %s\n",filename); - fclose(f); - return 0; - } - if(colres==65535) - bitdepth=16; - - /* gotta eat the newline too */ - char ch=0; - while(ch!='\n') fscanf(f,"%c",&ch); - - pixels=(unsigned char*)malloc(width*height*bitdepth/8); - if(!pixels) - { - printf("Error: could not allocate memory for the pixels of the texture. File: %s\n",filename); - fclose(f); - return 0; - } - - if(fread(pixels,width*height*bitdepth/8,1,f)!=1) - { - printf("Error: could not read %d bytes of pixel info. File: %s\n",width*height*bitdepth/8,filename); - free(pixels); - fclose(f); - return 0; - } - fclose(f); - printf("read %d-bit alpha channel",bitdepth); - if(bitdepth!=wantedBitDepth) - { - printf(", converting to %d-bit!",wantedBitDepth); - unsigned char* newpixels = (unsigned char*)malloc(width*height*wantedBitDepth/8); - for(int x=0; x=0; yy--) - { - for(xx = 0; xx +#include +#include +#include "image.hpp" + +// Remove warnings for unsafe functions such as strcpy +#pragma warning(disable : 4996) +// Remove warnings for conversions between different time variables +#pragma warning(disable : 4244) + +// Removes comments in a .ppm file +// (i.e., lines starting with #) +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void removeComments(FILE *f1) +{ + int c; + + while((c = getc(f1)) == '#') + { + char line[1024]; + fgets(line, 1024, f1); + } + ungetc(c, f1); +} + + +// Removes white spaces in a .ppm file +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +void removeSpaces(FILE *f1) +{ + int c; + + c = getc(f1); + while(c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r') + { + c = getc(f1); + } + ungetc(c, f1); +} + +// fReadPPM +// +// reads a ppm file with P6 header (meaning binary, as opposed to P5, which is ascII) +// and returns the image in pixels. +// +// The header must look like this: +// +// P6 +// # Comments (not necessary) +// width height +// 255 +// +// after that follows RGBRGBRGB... +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +bool fReadPPM(const char *filename, int &width, int &height, unsigned char *&pixels, int targetbitrate) +{ + FILE *f1; + int maximum; + f1 = fopen(filename, "rb"); + + if(f1) + { + char line[255]; + + removeSpaces(f1); + removeComments(f1); + removeSpaces(f1); + + fscanf(f1, "%s", line); + + if(strcmp(line, "P6")!=0) + { + printf("Error: %s is not binary\n"); + printf("(Binary .ppm files start with P6).\n"); + fclose(f1); + return false; + } + removeSpaces(f1); + removeComments(f1); + removeSpaces(f1); + + fscanf(f1, "%d %d", &width, &height); + if( width<=0 || height <=0) + { + printf("Error: width or height negative. File: %s\n",filename); + fclose(f1); + return false; + } + + removeSpaces(f1); + removeComments(f1); + removeSpaces(f1); + + fscanf(f1, "%d", &maximum); + if( maximum!= 255&&maximum!=(1<<16)-1) + { + printf("Error: Color resolution must be 255. File: %s\n",filename); + fclose(f1); + return false; + } + + //printf("maximum is %d\n",maximum); + int bitrate=8; + if(maximum!=255) + bitrate=16; + + // We need to remove the newline. + char c = 0; + while(c != '\n') + fscanf(f1, "%c", &c); + + unsigned char* readbuffer = (unsigned char*) malloc(3*width*height*bitrate/8); + if(!readbuffer) + { + printf("Error: Could not allocate memory for image. File: %s\n", filename); + fclose(f1); + return false; + } + + if(fread(readbuffer, 3*width*height*bitrate/8, 1, f1) != 1) + { + printf("Error: Could not read all pixels. File: %s\n", filename); + free(pixels); + fclose(f1); + return false; + } + + // If we have reached this point, we have successfully loaded the image. + + //now, convert it to the target bitrate + if(targetbitrate==bitrate) + pixels=readbuffer; + else + { + pixels = (unsigned char*) malloc(3*width*height*targetbitrate/8); + if(targetbitrate 8 bits.. + printf("converting 16 bit input to 8 bits\n"); + for(int x=0; x 16 bits... + printf("converting 8 bit input to 16 bits\n"); + for(int x=0; x.\n",filename); + return false; + } +} + +/* reads a ppm file with the P6 header (means raw RGB), puts data into pixel pointer and returns bit depth (8 or 16 bpp) */ +/* the header looks like this: + *--------- + * P5 + * # comments if you want to + * width height + * 255 + *--------- + * then follows RGBRGBRGBRGBRGB... + */ +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved. +int fReadPGM(const char *filename, int &width, int &height, unsigned char *&pixels, int wantedBitDepth) +{ + FILE *f; + int colres; + int bitdepth=8; + f=fopen(filename,"rb"); + if(f) + { + char str[100]; + removeSpaces(f); + removeComments(f); + removeSpaces(f); + fscanf(f,"%s",str); + if(strcmp(str,"P5")!=0) + { + printf("Error: the alpha image file must be of raw color PGM format,\n"); + printf("i.e., it must have P5 in the header. File: %s\n",filename); + fclose(f); + return 0; + } + removeSpaces(f); + removeComments(f); + removeSpaces(f); + fscanf(f,"%d %d",&width,&height); + if(width<=0 || height<=0) + { + printf("Error: width and height of the image must be greater than zero. File: %s\n",filename); + fclose(f); + return 0; + } + removeSpaces(f); + removeComments(f); + removeSpaces(f); + fscanf(f,"%d",&colres); + if(colres!=255&&colres!=65535) + { + printf("Error: color resolution must be 255 or 65535.File: %s\n",filename); + fclose(f); + return 0; + } + if(colres==65535) + bitdepth=16; + + /* gotta eat the newline too */ + char ch=0; + while(ch!='\n') fscanf(f,"%c",&ch); + + pixels=(unsigned char*)malloc(width*height*bitdepth/8); + if(!pixels) + { + printf("Error: could not allocate memory for the pixels of the texture. File: %s\n",filename); + fclose(f); + return 0; + } + + if(fread(pixels,width*height*bitdepth/8,1,f)!=1) + { + printf("Error: could not read %d bytes of pixel info. File: %s\n",width*height*bitdepth/8,filename); + free(pixels); + fclose(f); + return 0; + } + fclose(f); + printf("read %d-bit alpha channel",bitdepth); + if(bitdepth!=wantedBitDepth) + { + printf(", converting to %d-bit!",wantedBitDepth); + unsigned char* newpixels = (unsigned char*)malloc(width*height*wantedBitDepth/8); + for(int x=0; x=0; yy--) + { + for(xx = 0; xx Date: Wed, 29 Oct 2025 21:09:23 +0200 Subject: [PATCH 2/2] Added bin folder back --- .gitignore | 1 - bin/etcpack.exe | Bin 0 -> 960500 bytes 2 files changed, 1 deletion(-) delete mode 100644 .gitignore create mode 100644 bin/etcpack.exe diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 65776c3..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ \ No newline at end of file diff --git a/bin/etcpack.exe b/bin/etcpack.exe new file mode 100644 index 0000000000000000000000000000000000000000..49666e2a2ceac5c545c35d9b1451536840b09218 GIT binary patch literal 960500 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Ν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
  • 9kgoFHZ_>3V4 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 literal 0 HcmV?d00001