├── AUTHORS ├── CONTRIBUTING ├── LICENSE ├── README.md ├── base ├── integral_types.h └── logging.h ├── image_compression.gyp └── image_compression ├── internal ├── bit_util.h ├── color_types.h ├── color_util.h ├── compressor4x4_helper.cc ├── compressor4x4_helper.h ├── dxtc_compressor.cc ├── dxtc_const_color_table.cc ├── dxtc_const_color_table.h ├── dxtc_to_etc_transcoder.cc ├── etc_compressor.cc ├── pixel4x4.cc ├── pixel4x4.h └── pvrtc_compressor.cc └── public ├── compressed_image.h ├── compressor.h ├── dxtc_compressor.h ├── dxtc_to_etc_transcoder.h ├── etc_compressor.h └── pvrtc_compressor.h /AUTHORS: -------------------------------------------------------------------------------- 1 | opensource@google.com 2 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at 2 | the end). 3 | 4 | ### Before you contribute 5 | Before we can use your code, you must sign the 6 | [Google Individual Contributor License Agreement] 7 | (https://developers.google.com/open-source/cla/individual?csw=1) 8 | (CLA), which you can do online. The CLA is necessary mainly because you own the 9 | copyright to your changes, even after your contribution becomes part of our 10 | codebase, so we need your permission to use and distribute your code. We also 11 | need to be sure of various other things—for instance that you'll tell us if you 12 | know that your code infringes on other people's patents. You don't have to sign 13 | the CLA until after you've submitted your code for review and a member has 14 | approved it, but you must do it before we can put your code into our codebase. 15 | Before you start working on a larger contribution, you should get in touch with 16 | us first through the issue tracker with your idea so that we can help out and 17 | possibly guide you. Coordinating up front makes it much easier to avoid 18 | frustration later on. 19 | 20 | ### Code reviews 21 | All submissions, including submissions by project members, require review. We 22 | use Github pull requests for this purpose. 23 | 24 | ### The small print 25 | Contributions made by corporations are covered by a different agreement than 26 | the one above, the Software Grant and Corporate Contributor License Agreement. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #image-compression 2 | 3 | This repository contains an image compression library that provides routines 4 | for converting to and from various compressed image formats, such as DXT, ETC, 5 | and PVRTC. 6 | -------------------------------------------------------------------------------- /base/integral_types.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Basic integer type definitions for various platforms 16 | 17 | #ifndef BASE_INTEGRAL_TYPES_H_ 18 | #define BASE_INTEGRAL_TYPES_H_ 19 | 20 | // Standard typedefs 21 | typedef signed char schar; 22 | typedef signed char int8; 23 | typedef short int16; 24 | typedef int int32; 25 | #ifdef _MSC_VER 26 | typedef __int64 int64; 27 | #else 28 | typedef long long int64; 29 | #endif /* _MSC_VER */ 30 | 31 | // NOTE: unsigned types are DANGEROUS in loops and other arithmetical 32 | // places. Use the signed types unless your variable represents a bit 33 | // pattern (eg a hash value) or you really need the extra bit. Do NOT 34 | // use 'unsigned' to express "this value should always be positive"; 35 | // use assertions for this. 36 | 37 | typedef unsigned char uint8; 38 | typedef unsigned short uint16; 39 | typedef unsigned int uint32; 40 | #ifdef _MSC_VER 41 | typedef unsigned __int64 uint64; 42 | #else 43 | typedef unsigned long long uint64; 44 | #endif /* _MSC_VER */ 45 | 46 | // A type to represent a Unicode code-point value. As of Unicode 4.0, 47 | // such values require up to 21 bits. 48 | // (For type-checking on pointers, make this explicitly signed, 49 | // and it should always be the signed version of whatever int32 is.) 50 | typedef signed int char32; 51 | 52 | // A type to represent a natural machine word (for e.g. efficiently 53 | // scanning through memory for checksums or index searching). Don't use 54 | // this for storing normal integers. Ideally this would be just 55 | // unsigned int, but our 64-bit architectures use the LP64 model 56 | // (http://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models), hence 57 | // their ints are only 32 bits. We want to use the same fundamental 58 | // type on all archs if possible to preserve *printf() compatability. 59 | typedef unsigned long uword_t; 60 | 61 | // long long macros to be used because gcc and vc++ use different suffixes, 62 | // and different size specifiers in format strings 63 | #undef GG_LONGLONG 64 | #undef GG_ULONGLONG 65 | #undef GG_LL_FORMAT 66 | 67 | #ifdef _MSC_VER /* if Visual C++ */ 68 | 69 | // VC++ long long suffixes 70 | #define GG_LONGLONG(x) x##I64 71 | #define GG_ULONGLONG(x) x##UI64 72 | 73 | // Length modifier in printf format string for int64's (e.g. within %d) 74 | #define GG_LL_FORMAT "I64" // As in printf("%I64d", ...) 75 | #define GG_LL_FORMAT_W L"I64" 76 | 77 | #else /* not Visual C++ */ 78 | 79 | #define GG_LONGLONG(x) x##LL 80 | #define GG_ULONGLONG(x) x##ULL 81 | #define GG_LL_FORMAT "ll" // As in "%lld". Note that "q" is poor form also. 82 | #define GG_LL_FORMAT_W L"ll" 83 | 84 | #endif // _MSC_VER 85 | 86 | 87 | static const uint8 kuint8max = (( uint8) 0xFF); 88 | static const uint16 kuint16max = ((uint16) 0xFFFF); 89 | static const uint32 kuint32max = ((uint32) 0xFFFFFFFF); 90 | static const uint64 kuint64max = ((uint64) GG_LONGLONG(0xFFFFFFFFFFFFFFFF)); 91 | static const int8 kint8min = (( int8) ~0x7F); 92 | static const int8 kint8max = (( int8) 0x7F); 93 | static const int16 kint16min = (( int16) ~0x7FFF); 94 | static const int16 kint16max = (( int16) 0x7FFF); 95 | static const int32 kint32min = (( int32) ~0x7FFFFFFF); 96 | static const int32 kint32max = (( int32) 0x7FFFFFFF); 97 | static const int64 kint64min = (( int64) GG_LONGLONG(~0x7FFFFFFFFFFFFFFF)); 98 | static const int64 kint64max = (( int64) GG_LONGLONG(0x7FFFFFFFFFFFFFFF)); 99 | 100 | // TODO(user): remove this eventually. 101 | // No object has kIllegalFprint as its Fingerprint. 102 | typedef uint64 Fprint; 103 | static const Fprint kIllegalFprint = 0; 104 | static const Fprint kMaxFprint = GG_ULONGLONG(0xFFFFFFFFFFFFFFFF); 105 | 106 | #endif // BASE_INTEGRAL_TYPES_H_ 107 | -------------------------------------------------------------------------------- /base/logging.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef BASE_LOGGING_H_ 16 | #define BASE_LOGGING_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | class LogNull { 24 | public: 25 | LogNull() {} 26 | ~LogNull() {} 27 | std::ostream& GetStream() { 28 | static std::ostream null_stream(NULL); 29 | return null_stream; 30 | } 31 | }; 32 | 33 | #ifdef _DEBUG 34 | 35 | class LogFatal { 36 | public: 37 | LogFatal(const char* file, int line) { str_ << file << ":" << line << ": "; } 38 | ~LogFatal() { 39 | const std::string s = str_.str(); 40 | std::cerr << s << std::endl; 41 | abort(); 42 | } 43 | std::ostream& GetStream() { return str_; } 44 | 45 | private: 46 | std::ostringstream str_; 47 | }; 48 | 49 | #define LOG_IF_NOT(condition) \ 50 | !(condition) ? LogFatal(__FILE__, __LINE__).GetStream() \ 51 | : LogNull().GetStream() 52 | 53 | #define DCHECK_OP(a, b, op) LOG_IF_NOT((a)op(b)) 54 | 55 | #define DCHECK_LE(a, b) DCHECK_OP((a), (b), <=) 56 | #define DCHECK_GE(a, b) DCHECK_OP((a), (b), >=) 57 | #define DCHECK_LT(a, b) DCHECK_OP((a), (b), <) 58 | #define DCHECK_GT(a, b) DCHECK_OP((a), (b), >) 59 | #define DCHECK_EQ(a, b) DCHECK_OP((a), (b), ==) 60 | #define DCHECK_NE(a, b) DCHECK_OP((a), (b), !=) 61 | #define DCHECK(a) LOG_IF_NOT((a)) 62 | 63 | #else 64 | 65 | #define DCHECK(condition) LogNull().GetStream() 66 | #define DCHECK_EQ(a, b) LogNull().GetStream() 67 | #define DCHECK_NE(a, b) LogNull().GetStream() 68 | #define DCHECK_GT(a, b) LogNull().GetStream() 69 | #define DCHECK_LT(a, b) LogNull().GetStream() 70 | #define DCHECK_GE(a, b) LogNull().GetStream() 71 | #define DCHECK_LE(a, b) LogNull().GetStream() 72 | 73 | #endif 74 | 75 | #endif // BASE_LOGGING_H_ 76 | -------------------------------------------------------------------------------- /image_compression.gyp: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # Top-level gyp configuration for Image Compression. 16 | # 17 | # Projects that use Image Compression should depend on the target defined here. 18 | { 19 | 'targets': [ 20 | { 21 | 'target_name': 'imagecompression', 22 | 'type': 'static_library', 23 | 'sources': [ 24 | 'image_compression/internal/bit_util.h', 25 | 'image_compression/internal/color_types.h', 26 | 'image_compression/internal/color_util.h', 27 | 'image_compression/internal/compressor4x4_helper.cc', 28 | 'image_compression/internal/compressor4x4_helper.h', 29 | 'image_compression/internal/dxtc_compressor.cc', 30 | 'image_compression/internal/dxtc_const_color_table.cc', 31 | 'image_compression/internal/dxtc_const_color_table.h', 32 | 'image_compression/internal/dxtc_to_etc_transcoder.cc', 33 | 'image_compression/internal/etc_compressor.cc', 34 | 'image_compression/internal/pixel4x4.cc', 35 | 'image_compression/internal/pixel4x4.h', 36 | 'image_compression/internal/pvrtc_compressor.cc', 37 | 'image_compression/public/compressed_image.h', 38 | 'image_compression/public/compressor.h', 39 | 'image_compression/public/dxtc_compressor.h', 40 | 'image_compression/public/dxtc_to_etc_transcoder.h', 41 | 'image_compression/public/etc_compressor.h', 42 | 'image_compression/public/pvrtc_compressor.h', 43 | ], 44 | 'include_dirs': ['.'], 45 | 'defines': [ 46 | 'IS_LITTLE_ENDIAN', 47 | ], 48 | 'direct_dependent_settings': { 49 | 'defines': [ 50 | 'IS_LITTLE_ENDIAN', 51 | ], 52 | }, 53 | }, 54 | ], 55 | } 56 | -------------------------------------------------------------------------------- /image_compression/internal/bit_util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef IMAGE_COMPRESSION_INTERNAL_BIT_UTIL_H_ 16 | #define IMAGE_COMPRESSION_INTERNAL_BIT_UTIL_H_ 17 | 18 | #include "base/integral_types.h" 19 | #include "base/logging.h" 20 | 21 | namespace image_codec_compression { 22 | 23 | // 24 | // This file contains types and functions that help deal with bit 25 | // operations in image compression and decompression functions. 26 | // 27 | 28 | // Returns a 32-bit mask containing num_ones 1's in the least 29 | // significant bits. 30 | inline uint32 GetMask(int num_ones) { 31 | return (1 << num_ones) - 1; 32 | } 33 | 34 | // Generic 32-bit word bit accessor. Example: GetBits(n, 3, 2) returns 35 | // a 2-bit integer defined by bits 3 and 4 of n. Bits are numbered 36 | // from 0 (LSB) to 31 (MSB). 37 | inline int GetBits(uint32 bits, int start_bit, int num_bits) { 38 | // Shift right to remove bits to the right of start_bit, then 39 | // mask off all but num_bits bits. 40 | return (bits >> start_bit) & GetMask(num_bits); 41 | } 42 | 43 | // Generic 3-bit word bit setter. Example: SetBits(3, 2, 1, &n) stores 44 | // the 2-bit integer 1 into bits 3 and 4 of n. Bits are numbered from 45 | // 0 (LSB) to 31 (MSB). 46 | inline void SetBits(int start_bit, int num_bits, int value, uint32 *bits) { 47 | DCHECK_LT(value, 1 << num_bits); 48 | 49 | // Negative numbers must be converted to unsigned so they do not 50 | // sign-extend into the rest of the target value. This cast takes 51 | // care of both positive and negative cases. 52 | const uint32 mask = GetMask(num_bits); 53 | const uint32 unsigned_value = static_cast(value) & mask; 54 | 55 | // Clear any bits that are set, then set the new bits. 56 | *bits = (*bits & ~(mask << start_bit)) | (unsigned_value << start_bit); 57 | } 58 | 59 | // Converts a signed value represented in num_bits bits to a 32-bit 60 | // integer by extending the sign bit. 61 | inline int32 ExtendSignBit(int32 value, int num_bits) { 62 | DCHECK_GE(num_bits, 0); 63 | DCHECK_LT(num_bits, 32); 64 | DCHECK_LT(value, 1 << num_bits); 65 | // Shift left so that the sign bit is in the high-order bit, then 66 | // shift back, which should copy the sign bit all the way. 67 | const int num_to_shift = 32 - num_bits; 68 | return (value << num_to_shift) >> num_to_shift; 69 | } 70 | 71 | } // namespace image_codec_compression 72 | 73 | #endif // IMAGE_COMPRESSION_INTERNAL_BIT_UTIL_H_ 74 | -------------------------------------------------------------------------------- /image_compression/internal/color_types.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef IMAGE_COMPRESSION_INTERNAL_COLOR_TYPES_H_ 16 | #define IMAGE_COMPRESSION_INTERNAL_COLOR_TYPES_H_ 17 | 18 | #include 19 | 20 | #include "base/integral_types.h" 21 | #include "base/logging.h" 22 | 23 | namespace image_codec_compression { 24 | 25 | // 26 | // This file contains some basic types that help deal with colors in image 27 | // compression and decompression functions. 28 | // 29 | 30 | //----------------------------------------------------------------------------- 31 | 32 | // Simple templated RGB color type. 33 | template struct RgbColor { 34 | // The default constructor initializes to 0. 35 | RgbColor() : r(0), g(0), b(0) {} 36 | 37 | // Constructor that sets all components. 38 | RgbColor(T red, T green, T blue) : r(red), g(green), b(blue) {} 39 | 40 | // Accesses a component by index, which can be useful in loops. This is 41 | // range-checked and emits range errors in debug builds. 42 | T operator[](int index) const { 43 | switch (index) { 44 | case 0: return r; 45 | case 1: return g; 46 | case 2: return b; 47 | default: 48 | DCHECK(false) << "Invalid index " << index << " for RGB color"; 49 | return T(0); 50 | } 51 | } 52 | 53 | // Unary addition. 54 | RgbColor& operator+=(const RgbColor& c) { 55 | r += c.r; 56 | g += c.g; 57 | b += c.b; 58 | return *this; 59 | } 60 | 61 | // Equality operator for testing. 62 | bool operator==(const RgbColor &c) const { 63 | return r == c.r && g == c.g && b == c.b; 64 | } 65 | 66 | T r; 67 | T g; 68 | T b; 69 | }; 70 | 71 | // Simple templated RGBA color type. 72 | template struct RgbaColor : public RgbColor { 73 | // The default constructor initializes to 0. 74 | RgbaColor() : a(0) {} 75 | 76 | // Constructor that sets all components. 77 | RgbaColor(T red, T green, T blue, T alpha) 78 | : RgbColor(red, green, blue), 79 | a(alpha) {} 80 | 81 | // Constructor taking an RGB color and alpha. 82 | RgbaColor(const RgbColor& rgb, T alpha) 83 | : RgbColor(rgb), 84 | a(alpha) {} 85 | 86 | // Accesses a component by index, which can be useful in loops. This is 87 | // range-checked and emits range errors in debug builds. 88 | T operator[](int index) const { 89 | switch (index) { 90 | case 0: return this->r; 91 | case 1: return this->g; 92 | case 2: return this->b; 93 | case 3: return a; 94 | default: 95 | DCHECK(false) << "Invalid index " << index << " for RGBA color"; 96 | return T(0); 97 | } 98 | } 99 | 100 | // Unary addition. 101 | RgbaColor& operator+=(const RgbaColor& c) { 102 | RgbColor::operator+=(c); 103 | a += c.a; 104 | return *this; 105 | } 106 | 107 | // Equality operator for testing. 108 | bool operator==(const RgbaColor &c) const { 109 | return RgbColor::operator==(c) && a == c.a; 110 | } 111 | 112 | T a; 113 | }; 114 | 115 | // Defines an RGB pixel stored as 5 bits of red, 6 bits of green, and 116 | // 5 bits of blue for a total of 16 bits, which fits in a uint16. It 117 | // uses endian-specific bit fields to allow casting to be used for 118 | // very efficient conversions to and from uint16's. This efficiency 119 | // is required for performance-critical sections of compression and 120 | // decompression functions. 121 | struct Rgb565 { 122 | // Empty constructor does no initialization. 123 | Rgb565() {} 124 | 125 | // Constructor taking 3 component values assumed to be in range. 126 | Rgb565(uint8 red, uint8 green, uint8 blue) { 127 | DCHECK_LT(red, 1 << 5); 128 | DCHECK_LT(green, 1 << 6); 129 | DCHECK_LT(blue, 1 << 5); 130 | // Use assignment instead of initializers because the order of the 131 | // variables changes with endianness. 132 | r = red; 133 | g = green; 134 | b = blue; 135 | } 136 | 137 | // Equality operator for testing. 138 | bool operator==(const Rgb565 &c) const { 139 | return r == c.r && g == c.g && b == c.b; 140 | } 141 | 142 | #if defined IS_LITTLE_ENDIAN 143 | unsigned int b : 5; 144 | unsigned int g : 6; 145 | unsigned int r : 5; 146 | #elif defined IS_BIG_ENDIAN 147 | unsigned int r : 5; 148 | unsigned int g : 6; 149 | unsigned int b : 5; 150 | #else 151 | #error Neither IS_LITTLE_ENDIAN nor IS_BIG_ENDIAN is defined 152 | #endif 153 | }; 154 | 155 | //----------------------------------------------------------------------------- 156 | 157 | // 158 | // Type-specific instantiations. 159 | // 160 | 161 | // 8-bit-per-component RGB and RGBA colors. 162 | typedef RgbColor Rgb888; 163 | typedef RgbaColor Rgba8888; 164 | 165 | // Int-component versions of RGB and RGBA colors. They are useful for holding 166 | // intermediate results of color computations. 167 | typedef RgbColor RgbInt; 168 | typedef RgbaColor RgbaInt; 169 | 170 | } // namespace image_codec_compression 171 | 172 | #endif // IMAGE_COMPRESSION_INTERNAL_COLOR_TYPES_H_ 173 | -------------------------------------------------------------------------------- /image_compression/internal/color_util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef IMAGE_COMPRESSION_INTERNAL_COLOR_UTIL_H_ 16 | #define IMAGE_COMPRESSION_INTERNAL_COLOR_UTIL_H_ 17 | 18 | #include 19 | 20 | #include "base/integral_types.h" 21 | #include "base/logging.h" 22 | #include "image_compression/internal/color_types.h" 23 | 24 | namespace image_codec_compression { 25 | 26 | // 27 | // This file contains functions that help deal with colors in image compression 28 | // and decompression functions. 29 | // 30 | 31 | //----------------------------------------------------------------------------- 32 | 33 | // 34 | // Binary arithmetic operators on integer color types. 35 | // 36 | 37 | inline const RgbInt operator+(const RgbInt& c0, const RgbInt& c1) { 38 | return RgbInt(c0.r + c1.r, c0.g + c1.g, c0.b + c1.b); 39 | } 40 | 41 | inline const RgbaInt operator+(const RgbaInt& c0, const RgbaInt& c1) { 42 | return RgbaInt(c0.r + c1.r, c0.g + c1.g, c0.b + c1.b, c0.a + c1.a); 43 | } 44 | 45 | inline const RgbInt operator-(const RgbInt& c0, const RgbInt& c1) { 46 | return RgbInt(c0.r - c1.r, c0.g - c1.g, c0.b - c1.b); 47 | } 48 | 49 | inline const RgbaInt operator-(const RgbaInt& c0, const RgbaInt& c1) { 50 | return RgbaInt(c0.r - c1.r, c0.g - c1.g, c0.b - c1.b, c0.a - c1.a); 51 | } 52 | 53 | inline const RgbInt operator/(const RgbInt& c, int32 div) { 54 | return RgbInt(c.r / div, c.g / div, c.b / div); 55 | } 56 | 57 | inline const RgbaInt operator/(const RgbaInt& c, int32 div) { 58 | return RgbaInt(c.r / div, c.g / div, c.b / div, c.a / div); 59 | } 60 | 61 | //----------------------------------------------------------------------------- 62 | 63 | // 64 | // Conversion functions. 65 | // 66 | 67 | // Converts an Rgb888 or Rgba8888 color to an RgbInt color. (Rgba8888 68 | // drops the alpha.) These are overloaded to allow use in templates. 69 | template inline const RgbInt ToRgbInt(const ColorType &c) { 70 | return RgbInt(c.r, c.g, c.b); 71 | } 72 | 73 | // Converts an Rgba8888 color to an RgbaInt color. 74 | inline const RgbaInt ToRgbaInt(const Rgba8888 &c) { 75 | return RgbaInt(c.r, c.g, c.b, c.a); 76 | } 77 | 78 | // Converts an RgbInt color in which each component is assumed to be 79 | // 0-255 to an Rgb888. 80 | inline const Rgb888 ToRgb888(const RgbInt &c) { 81 | return Rgb888(c.r, c.g, c.b); 82 | } 83 | 84 | // Converts an RgbaInt color in which each component is assumed to be 85 | // 0-255 to an Rgba8888. 86 | inline const Rgba8888 ToRgba8888(const RgbaInt &c) { 87 | return Rgba8888(c.r, c.g, c.b, c.a); 88 | } 89 | 90 | // Converts an Rgb565 to a uint16. 91 | inline uint16 ToUInt16(const Rgb565& c) { 92 | return (c.r << 11 | 93 | c.g << 5 | 94 | c.b); 95 | } 96 | 97 | // Converts a uint16 to an Rgb565. 98 | inline const Rgb565 ToRgb565(const uint16 p) { 99 | return Rgb565(p >> 11, 100 | (p >> 5) & 0x3F, 101 | p & 0x1F); 102 | } 103 | 104 | // Swaps the red and blue channels in an RgbInt color. This makes it 105 | // easier to deal with DirectX images, which store colors as BGR. 106 | inline const RgbInt ToBgrInt(const RgbInt &c) { 107 | return RgbInt(c.b, c.g, c.r); 108 | } 109 | 110 | // Swaps the red and blue channels in an RgbaInt color. This makes it 111 | // easier to deal with DirectX images, which store colors as BGRA. 112 | inline const RgbaInt ToBgraInt(const RgbaInt &c) { 113 | return RgbaInt(c.b, c.g, c.r, c.a); 114 | } 115 | 116 | // Convenience function that swaps red and blue channels in an RgbInt 117 | // color if a passed flag is true. 118 | inline const RgbInt ToRgbOrBgrInt(const RgbInt &c, bool swap_red_and_blue) { 119 | return swap_red_and_blue ? ToBgrInt(c) : c; 120 | } 121 | 122 | // Convenience function that swaps red and blue channels in an RgbaInt 123 | // color if a passed flag is true. 124 | inline const RgbaInt ToRgbaOrBgraInt(const RgbaInt &c, bool swap_red_and_blue) { 125 | return swap_red_and_blue ? ToBgraInt(c) : c; 126 | } 127 | 128 | // Templated color swap function. 129 | template inline void SwapRedAndBlue(ColorType *c) { 130 | std::swap(c->r, c->b); 131 | } 132 | 133 | //----------------------------------------------------------------------------- 134 | 135 | // 136 | // Basic compression/decompression functions. 137 | // 138 | 139 | // Quantizes a color component assumed to be in the range 0-255 to an 140 | // N-bit unsigned value, where 0 inline const uint8 Quantize8Fast(int v) { 143 | DCHECK_GT(num_bits, 0); 144 | DCHECK_LT(num_bits, 8); 145 | DCHECK_GE(v, 0); 146 | DCHECK_LT(v, 256); 147 | return v >> (8 - num_bits); 148 | } 149 | 150 | 151 | // Quantizes a color component assumed to be in the range 0-255 to an N-bit 152 | // unsigned value, where 0 inline const uint8 Quantize8(int v) { 157 | DCHECK_GT(num_bits, 0); 158 | DCHECK_LT(num_bits, 8); 159 | DCHECK_GE(v, 0); 160 | DCHECK_LT(v, 256); 161 | int max_val = (1 << num_bits) - 1; 162 | int i = v * max_val + 128; 163 | return (i + (i >> 8)) >> 8; 164 | } 165 | 166 | 167 | // Quantizes an RGB color with 8 bits per component to N-bit components. 168 | template inline const RgbInt QuantizeRgbFast(const RgbInt &c) { 169 | return RgbInt(Quantize8Fast(c.r), 170 | Quantize8Fast(c.g), 171 | Quantize8Fast(c.b)); 172 | } 173 | 174 | // Quantizes an RGBA color with 8 bits per component to N-bit components. 175 | template 176 | inline const RgbaInt QuantizeRgbaFast(const RgbaInt &c) { 177 | return RgbaInt(Quantize8Fast(c.r), 178 | Quantize8Fast(c.g), 179 | Quantize8Fast(c.b), 180 | Quantize8Fast(c.a)); 181 | } 182 | 183 | // Quantizes an RgbInt color in which each component is assumed to be 184 | // 0-255 to an Rgb565. 185 | inline const Rgb565 QuantizeToRgb565(const RgbInt &c) { 186 | return Rgb565(Quantize8<5>(c.r), 187 | Quantize8<6>(c.g), 188 | Quantize8<5>(c.b)); 189 | } 190 | 191 | // Extends a 4-bit color component to an 8-bit component by 192 | // replicating all 4 bits. For example: '1011' -> '10111011'. 193 | inline int Extend4Bit(int bits) { 194 | return (bits << 4) | bits; 195 | } 196 | 197 | // Extends a 5-bit color component to an 8-bit component by 198 | // replicating the 3 highest-order bits as the new low-order bits. 199 | // For example: '10110' -> '10110101'. 200 | inline int Extend5Bit(int bits) { 201 | return (bits << 3) | ((bits >> 2) & 7); 202 | } 203 | 204 | // Converts an Rgb565 to an RgbInt color quickly. 205 | // This conversion taken from: 206 | // http://developer.download.nvidia.com/compute/cuda/3_0/sdk/website/OpenCL/website/OpenCL/src/oclDXTCompression/doc/opencl_dxtc.pdf 207 | // In particular it is claimed that this is how the hardware expands the 565 208 | // colors. The approximation is off-by-1 from the standard rounded floating 209 | // point conversion for the following values: 210 | // 5-bit Fast Float 211 | // 3 24 25 212 | // 7 57 58 213 | // 24 198 197 214 | // 28 231 230 215 | // 6-bit Fast Float 216 | // 11 44 45 217 | // 12 48 49 218 | // 13 52 53 219 | // 14 56 57 220 | // 15 60 61 221 | // 48 195 194 222 | // 49 199 198 223 | // 50 203 202 224 | // 51 207 206 225 | // 52 211 210 226 | inline const RgbInt ExtendToRgbInt(const Rgb565 &c) { 227 | return RgbInt((c.r << 3) | (c.r >> 2), 228 | (c.g << 2) | (c.g >> 4), 229 | (c.b << 3) | (c.b >> 2)); 230 | } 231 | 232 | // Extends an Rgb565 to an Rgb888. 233 | inline const Rgb888 ExtendToRgb888(const Rgb565 &c) { 234 | return Rgb888((c.r << 3) | (c.r >> 2), 235 | (c.g << 2) | (c.g >> 4), 236 | (c.b << 3) | (c.b >> 2)); 237 | } 238 | 239 | //----------------------------------------------------------------------------- 240 | 241 | // 242 | // Arithmetic operations on colors, using only integer math. Some of 243 | // these sacrifice a little bit of precision for speed, which is 244 | // usually the right tradeoff during compression and decompression. 245 | // 246 | 247 | // Clamps an integer to 0-255 and returns the result. 248 | inline int ClampTo8Bits(int value) { 249 | // This could more easily and clearly be written as: 250 | // return std::max(0, std::min(255, value)); 251 | // 252 | // However, this function is used in some super-time-critical 253 | // functions, and the sign-bit trick used here actually speeds 254 | // things up quite a bit. Note that doing a similar trick to remove 255 | // the call to max actually resulted in slower code: 256 | // const int one_if_max = 257 | // (static_cast(255 - value) & 0x80000000) >> 31; 258 | // return zero_if_negative * ((value | (one_if_max * 255)) & 255); 259 | // 260 | // Also note that moving the sign-bit extraction into a separate 261 | // inline function resulted in slower code as well. Go figure. 262 | const int zero_if_negative = 263 | 1 - ((static_cast(value) & 0x80000000) >> 31); 264 | return zero_if_negative * std::min(255, value); 265 | } 266 | 267 | // Clamps all components of an RgbInt to 0-255 and returns the result. 268 | inline const RgbInt ClampRgbInt(const RgbInt &c) { 269 | return RgbInt(ClampTo8Bits(c.r), ClampTo8Bits(c.g), ClampTo8Bits(c.b)); 270 | } 271 | 272 | // Clamps all components of an RgbaInt to 0-255 and returns the result. 273 | inline const RgbaInt ClampRgbaInt(const RgbaInt &c) { 274 | return RgbaInt(ClampTo8Bits(c.r), ClampTo8Bits(c.g), 275 | ClampTo8Bits(c.b), ClampTo8Bits(c.a)); 276 | } 277 | 278 | // Linear combination of two integer values, using all integer math. 279 | // The sum of the integer scales is used as the divisor for each 280 | // product. For example, given scales of 3 and 8, the linear 281 | // combination is 3/11 of value0 and 8/11 of value1. 282 | template 283 | inline int CombineIntFast(int value0, int value1) { 284 | DCHECK_NE(scale0 + scale1, 0); 285 | return (scale0 * value0 + scale1 * value1) / (scale0 + scale1); 286 | } 287 | 288 | // Version of CombineIntFast() that operates on uint8 values, avoiding 289 | // overflow. 290 | template 291 | inline uint8 CombineUint8Fast(uint8 value0, uint8 value1) { 292 | return static_cast(CombineIntFast(value0, value1)); 293 | } 294 | 295 | // Version of CombineIntFast() that operates on Rgb888 colors. 296 | template 297 | inline const Rgb888 CombineRgb888Fast(const Rgb888 &color0, 298 | const Rgb888 &color1) { 299 | return Rgb888(CombineUint8Fast(color0.r, color1.r), 300 | CombineUint8Fast(color0.g, color1.g), 301 | CombineUint8Fast(color0.b, color1.b)); 302 | } 303 | 304 | // Version of CombineIntFast() that operates on Rgba8888 colors. 305 | template 306 | inline const Rgba8888 CombineRgba8888Fast(const Rgba8888 &color0, 307 | const Rgba8888 &color1) { 308 | return Rgba8888(CombineUint8Fast(color0.r, color1.r), 309 | CombineUint8Fast(color0.g, color1.g), 310 | CombineUint8Fast(color0.b, color1.b), 311 | CombineUint8Fast(color0.a, color1.a)); 312 | } 313 | 314 | // Version of CombineIntFast() that operates on RgbInt colors. 315 | template 316 | inline const RgbInt CombineRgbIntFast(const RgbInt &color0, 317 | const RgbInt &color1) { 318 | return RgbInt(CombineIntFast(color0.r, color1.r), 319 | CombineIntFast(color0.g, color1.g), 320 | CombineIntFast(color0.b, color1.b)); 321 | } 322 | 323 | // Version of CombineIntFast() that operates on RgbaInt colors. 324 | template 325 | inline const RgbaInt CombineRgbaIntFast(const RgbaInt &color0, 326 | const RgbaInt &color1) { 327 | return RgbaInt(CombineIntFast(color0.r, color1.r), 328 | CombineIntFast(color0.g, color1.g), 329 | CombineIntFast(color0.b, color1.b), 330 | CombineIntFast(color0.a, color1.a)); 331 | } 332 | 333 | // Averaging 4 uint8 values using only integer math, which is useful 334 | // during image reduction. 335 | inline uint8 Average4Uint8Fast(uint8 value0, uint8 value1, 336 | uint8 value2, uint8 value3) { 337 | return static_cast((static_cast(value0) + 338 | static_cast(value1) + 339 | static_cast(value2) + 340 | static_cast(value3)) / 4); 341 | } 342 | 343 | // Templated averaging function for 4 colors. 344 | template 345 | inline const ColorType Average4ColorsFast(const ColorType &color0, 346 | const ColorType &color1, 347 | const ColorType &color2, 348 | const ColorType &color3); 349 | 350 | // Specialized version for Rgb888 colors. 351 | template <> inline const Rgb888 Average4ColorsFast(const Rgb888 &color0, 352 | const Rgb888 &color1, 353 | const Rgb888 &color2, 354 | const Rgb888 &color3) { 355 | return Rgb888(Average4Uint8Fast(color0.r, color1.r, color2.r, color3.r), 356 | Average4Uint8Fast(color0.g, color1.g, color2.g, color3.g), 357 | Average4Uint8Fast(color0.b, color1.b, color2.b, color3.b)); 358 | } 359 | 360 | // Specialized version for Rgba8888 colors. 361 | template <> inline const Rgba8888 Average4ColorsFast(const Rgba8888 &color0, 362 | const Rgba8888 &color1, 363 | const Rgba8888 &color2, 364 | const Rgba8888 &color3) { 365 | return Rgba8888(Average4Uint8Fast(color0.r, color1.r, color2.r, color3.r), 366 | Average4Uint8Fast(color0.g, color1.g, color2.g, color3.g), 367 | Average4Uint8Fast(color0.b, color1.b, color2.b, color3.b), 368 | Average4Uint8Fast(color0.a, color1.a, color2.a, color3.a)); 369 | } 370 | 371 | // Templated function to average the colors of the 2x2 block of pixels 372 | // whose upper-left pixel is at the given row and column. 373 | template 374 | static ColorType ComputeAveragePixel2x2(const ColorType pixels[4][4], 375 | int row, int col) { 376 | return Average4ColorsFast(pixels[row][col], 377 | pixels[row][col + 1], 378 | pixels[row + 1][col], 379 | pixels[row + 1][col + 1]); 380 | } 381 | 382 | // Computes the approximate luminance of an RGB color. 383 | inline int ComputeLuminanceFast(const RgbInt &c) { 384 | // The RGB-to-luminance formula is usually: 385 | // 386 | // Luminance = 0.299 * red + 0.587 * green + 0.114 * blue 387 | // 388 | // Since we don't care about exact values, we use integer 389 | // approximations in roughly the same ratios, but which the compiler 390 | // can optimize very easily. 391 | static const int kRedFactor = 4; 392 | static const int kGreenFactor = 8; 393 | static const int kBlueFactor = 1; 394 | return (c.r * kRedFactor + c.g * kGreenFactor + c.b * kBlueFactor); 395 | } 396 | 397 | // Computes the distance between two RGB colors using a 398 | // squared luminance-based metric. 399 | inline int ComputeSquaredLuminanceDistanceFast(const RgbInt &c0, 400 | const RgbInt &c1) { 401 | const int diff = ComputeLuminanceFast(c1) - ComputeLuminanceFast(c0); 402 | return diff * diff; 403 | } 404 | 405 | // Computes the distance between two RGB colors using a 406 | // luminance-of-difference metric. This computes the luminance of the 407 | // absolute difference between two colors rather than the difference of the 408 | // luminance. The latter is subject to confusing two iso-luminant but 409 | // chromatically very different colors. 410 | inline int ComputeDifferenceLuminanceFast(const RgbInt &c0, 411 | const RgbInt &c1) { 412 | const RgbInt cdiff(std::abs(c0.r - c1.r), 413 | std::abs(c0.g - c1.g), 414 | std::abs(c0.b - c1.b)); 415 | const int diff_luminance = ComputeLuminanceFast(cdiff); 416 | return diff_luminance * diff_luminance; 417 | } 418 | 419 | // Computes the squared distance between two color component values. 420 | inline int ComputeSquaredComponentDistance(int component0, int component1) { 421 | const int diff = component1 - component0; 422 | return diff * diff; 423 | } 424 | 425 | //----------------------------------------------------------------------------- 426 | 427 | // 428 | // Helper functions for dealing with colors in image buffers. 429 | // 430 | 431 | // Returns a mutable pointer to the first RGB or RGBA color value 432 | // corresponding to a given row and column in an image buffer. 433 | template 434 | inline ColorType* GetMutableColorInImageBuffer( 435 | ColorType *image_buffer, int bytes_per_row, int row, int column) { 436 | uint8* row_start = 437 | reinterpret_cast(image_buffer) + (row * bytes_per_row); 438 | return reinterpret_cast(row_start) + column; 439 | } 440 | 441 | // Returns a const pointer to the first RGB or RGBA color value 442 | // corresponding to a given row and column in an image buffer. 443 | template 444 | inline const ColorType* GetColorInImageBuffer( 445 | const ColorType *image_buffer, int bytes_per_row, int row, int column) { 446 | const uint8* row_start = 447 | reinterpret_cast(image_buffer) + (row * bytes_per_row); 448 | return reinterpret_cast(row_start) + column; 449 | } 450 | 451 | // Stores RGB or RGBA colors from a 4x4 array into an image buffer of 452 | // the given width starting at the given row and column. The 453 | // num_rows_in_block and num_cols_in_block parameters indicate how 454 | // many rows and columns of the block should be stored. 455 | template 456 | inline void StoreColorsInBuffer(const ColorType block_pixels[4][4], 457 | int image_width, 458 | int num_rows_in_block, int num_cols_in_block, 459 | int row, int col, ColorType *image_buffer) { 460 | for (int y = 0; y < num_rows_in_block; ++y) { 461 | ColorType *row_pixels = GetMutableColorInImageBuffer( 462 | image_buffer, image_width, row + y, col); 463 | for (int x = 0; x < num_cols_in_block; ++x) 464 | row_pixels[x] = block_pixels[y][x]; 465 | } 466 | } 467 | 468 | //----------------------------------------------------------------------------- 469 | 470 | // 471 | // Streaming operators for test output. 472 | // 473 | 474 | template 475 | inline std::ostream &operator <<(std::ostream &out, const RgbColor &c) { 476 | out << "[" << c.r << ", " << c.g << ", " << c.b << "]"; 477 | return out; 478 | } 479 | 480 | template 481 | inline std::ostream &operator <<(std::ostream &out, const RgbaColor &c) { 482 | out << "[" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << "]"; 483 | return out; 484 | } 485 | 486 | } // namespace image_codec_compression 487 | 488 | #endif // IMAGE_COMPRESSION_INTERNAL_COLOR_UTIL_H_ 489 | -------------------------------------------------------------------------------- /image_compression/internal/compressor4x4_helper.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "image_compression/internal/compressor4x4_helper.h" 16 | 17 | #include "base/integral_types.h" 18 | #include "base/logging.h" 19 | 20 | namespace image_codec_compression { 21 | 22 | bool Compressor4x4Helper::SetUpCompressedImage( 23 | const std::string &compressor_name, size_t block_size, 24 | CompressedImage::Format format, uint32 height, uint32 width, 25 | uint32 padding_bytes_per_row, CompressedImage *image) { 26 | DCHECK(image); 27 | const uint32 num_block_rows = GetNumBlocks(height); 28 | const uint32 num_block_cols = GetNumBlocks(width); 29 | const size_t data_size = num_block_rows * num_block_cols * block_size; 30 | 31 | const CompressedImage::Metadata metadata( 32 | format, compressor_name, height, width, 33 | 4 * num_block_rows, 4 * num_block_cols, padding_bytes_per_row); 34 | if (image->OwnsData()) { 35 | image->CreateOwnedData(metadata, data_size); 36 | } else { 37 | // Make sure the external storage has the correct size. 38 | if (image->GetDataSize() != data_size) 39 | return false; 40 | image->SetMetadata(metadata); 41 | } 42 | return true; 43 | } 44 | 45 | } // namespace image_codec_compression 46 | -------------------------------------------------------------------------------- /image_compression/internal/compressor4x4_helper.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Compressor4x4Helper is an internal helper class for derived 16 | // Compressor classes that operate on 4x4 blocks of pixels. Most of 17 | // the methods are templated on the type used to represent the encoded 18 | // 4x4 block of pixels and also the type used to represent a single 19 | // pixel. 20 | // 21 | // ---------- FUNCTORS --------- 22 | // 23 | // The helper uses templated functors to do the compressor-specific 24 | // work for most of the operations. The functors and their template 25 | // parameters are named consistently in the interface. The signatures 26 | // are as follows: 27 | // 28 | // EncodeFunctor: 29 | // const BlockType func(const Pixel4x4 &pixel4x4, bool swap_red_and_blue); 30 | // This function is used to encode a single 4x4 block represented by 31 | // a Pixel4x4 instance. The swap_red_and_blue parameter indicates 32 | // whether the format is BGR or BGRA in contrast to RGB or RGBA. 33 | // 34 | // DecodeFunctor: 35 | // void func(const BlockType &block, bool swap_red_and_blue, 36 | // ColorType decoded_pixels[4][4]); 37 | // This function is used to decode a 4x4 block represented by an 38 | // encoded BlockType instance. The swap_red_and_blue parameter 39 | // indicates whether the format is BGR or BGRA in contrast to RGB or 40 | // RGBA. The decoded pixels are to be stored in the array. 41 | // 42 | // ColumnPadBlockFunctor: 43 | // const BlockType func(const BlockType &last_column_block); 44 | // This function is used during calls to Pad() to create a block 45 | // used to pad to the right of the last block in a row of a 46 | // compressed image. The returned block should have all 4 pixel 47 | // columns equal to the rightmost column of last_column_block. 48 | // 49 | // RowPadBlockFunctor: 50 | // const BlockType func(const BlockType &last_row_block); 51 | // This function is used during calls to Pad() to create a block 52 | // used to pad below the last block in a column of a compressed 53 | // image. The returned block should have all 4 pixel rows equal to 54 | // the bottom row of last_row_block. 55 | // 56 | // CornerPadBlockFunctor: 57 | // const BlockType func(const BlockType &last_block); 58 | // This function is used during calls to Pad() to create a block 59 | // used to pad below and to the right of the bottom-right corner of 60 | // a compressed image. The returned block should have all 16 pixels 61 | // equal to the bottom-right pixel of last_block. 62 | // 63 | 64 | #ifndef IMAGE_COMPRESSION_INTERNAL_COMPRESSOR4X4_HELPER_H_ 65 | #define IMAGE_COMPRESSION_INTERNAL_COMPRESSOR4X4_HELPER_H_ 66 | 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | 73 | #include "base/integral_types.h" 74 | #include "base/logging.h" 75 | #include "image_compression/internal/color_util.h" 76 | #include "image_compression/internal/pixel4x4.h" 77 | #include "image_compression/public/compressed_image.h" 78 | 79 | namespace image_codec_compression { 80 | 81 | class Compressor4x4Helper { 82 | public: 83 | // Given the number of pixels in the height or width of an image, this 84 | // returns the number of encoded blocks that are needed in that 85 | // dimension. 86 | static inline uint32 GetNumBlocks(uint32 num_pixels) { 87 | return (num_pixels + 3) / 4; 88 | } 89 | 90 | // Compresses an image. 91 | template 92 | static bool Compress(EncodeFunctor encode, 93 | const std::string &compressor_name, 94 | CompressedImage::Format format, 95 | uint32 height, uint32 width, 96 | uint32 padding_bytes_per_row, 97 | const uint8 *buffer, CompressedImage *image); 98 | 99 | // Decompresses an image. 100 | template 101 | inline static bool Decompress(DecodeFunctor decode, 102 | const CompressedImage &image, 103 | std::vector *decompressed_buffer); 104 | 105 | // Downsamples a compressed image. This returns false if the input 106 | // image is invalid or if there is an odd number of blocks in either 107 | // dimension. 108 | template 110 | inline static bool Downsample(EncodeFunctor encode, DecodeFunctor decode, 111 | const CompressedImage &image, 112 | CompressedImage *downsampled_image); 113 | 114 | // Pads an image. 115 | template 117 | inline static bool Pad(ColumnPadBlockFunctor get_column_pad_block, 118 | RowPadBlockFunctor get_row_pad_block, 119 | CornerPadBlockFunctor get_corner_pad_block, 120 | const CompressedImage &image, uint32 padded_height, 121 | uint32 padded_width, CompressedImage *padded_image); 122 | 123 | // Compresses and pads an image in a single operation. This is more 124 | // efficient than compressing and padding in separate calls. 125 | template 126 | inline static bool CompressAndPad( 127 | EncodeFunctor encode, const std::string &compressor_name, 128 | CompressedImage::Format format, uint32 height, uint32 width, 129 | uint32 padded_height, uint32 padded_width, uint32 padding_bytes_per_row, 130 | const uint8 *buffer, CompressedImage *padded_image); 131 | 132 | // Creates a solid image. 133 | template 134 | inline static bool CreateSolidImage( 135 | const std::string &compressor_name, 136 | CompressedImage::Format format, uint32 height, uint32 width, 137 | const BlockType &block, CompressedImage *image); 138 | 139 | // Copies a subimage. Returns false if either starting index or 140 | // dimension is not a multiple of 4. 141 | template 142 | inline static bool CopySubimage(const CompressedImage &image, 143 | uint32 start_row, uint32 start_column, 144 | uint32 height, uint32 width, 145 | CompressedImage *subimage); 146 | 147 | private: 148 | // These are used by Downsample() to downsample a 2x2, 2x1, or 1x2 of blocks 149 | // into a single block. 150 | template 152 | inline static const BlockType DownsampleBlocks2x2( 153 | EncodeFunctor encode, DecodeFunctor decode, 154 | const BlockType *blocks[2][2]); 155 | template 157 | inline static const BlockType DownsampleBlocks2x1( 158 | EncodeFunctor encode, DecodeFunctor decode, const BlockType *blocks[2]); 159 | template 161 | inline static const BlockType DownsampleBlocks1x2( 162 | EncodeFunctor encode, DecodeFunctor decode, const BlockType *blocks[2]); 163 | 164 | // Sets up the given CompressedImage to be used for an image with 165 | // the given info. This handles instances with internal or external 166 | // storage. Returns false on error. 167 | static bool SetUpCompressedImage( 168 | const std::string &compressor_name, size_t block_size, 169 | CompressedImage::Format format, uint32 height, uint32 width, 170 | uint32 padding_bytes_per_row, CompressedImage *image); 171 | }; 172 | 173 | //----------------------------------------------------------------------------- 174 | 175 | template 176 | bool Compressor4x4Helper::Compress( 177 | EncodeFunctor encode, const std::string &compressor_name, 178 | CompressedImage::Format format, uint32 height, uint32 width, 179 | uint32 padding_bytes_per_row, const uint8 *buffer, CompressedImage *image) { 180 | DCHECK(buffer); 181 | DCHECK(image); 182 | if (!SetUpCompressedImage(compressor_name, sizeof(BlockType), 183 | format, height, width, 184 | padding_bytes_per_row, image)) { 185 | return false; 186 | } 187 | 188 | const ColorType *uncompressed_pixels = 189 | reinterpret_cast(buffer); 190 | BlockType *block = reinterpret_cast(image->GetMutableData()); 191 | const bool swap_red_and_blue = NeedsRedAndBlueSwapped(format); 192 | 193 | // TODO(user): Don't need to compress blocks after the first 194 | // row or col that is completely outside the original image (i.e., 195 | // (row > (uncompressed_height + 7) / 4 196 | // or (col > (uncompressed_width + 7) / 4) 197 | // Instead, just copy or memcpy blocks or rows of blocks. 198 | const uint32 num_block_rows = GetNumBlocks(height); 199 | const uint32 num_block_cols = GetNumBlocks(width); 200 | DCHECK_GT(num_block_rows, 0U); 201 | DCHECK_GT(num_block_cols, 0U); 202 | for (uint32 row = 0; row < num_block_rows; ++row) { 203 | for (uint32 col = 0; col < num_block_cols; ++col) { 204 | // Encode one 4x4 block of pixels. The Pixel4x4 constructor 205 | // handles the cases where the 4x4 window is no longer over 206 | // valid uncompressed data. 207 | DCHECK_LT(static_cast(block), 208 | static_cast(image->GetData() + 209 | image->GetDataSize())); 210 | *block++ = encode(Pixel4x4(uncompressed_pixels, height, width, 211 | padding_bytes_per_row, row * 4, col * 4), 212 | swap_red_and_blue); 213 | } 214 | } 215 | return true; 216 | } 217 | 218 | template 219 | bool Compressor4x4Helper::Decompress(DecodeFunctor decode, 220 | const CompressedImage &image, 221 | std::vector *decompressed_buffer) { 222 | DCHECK(decompressed_buffer); 223 | 224 | const CompressedImage::Metadata &metadata = image.GetMetadata(); 225 | decompressed_buffer->resize(metadata.uncompressed_height * 226 | metadata.uncompressed_width * sizeof(ColorType)); 227 | 228 | const BlockType *block = reinterpret_cast(image.GetData()); 229 | ColorType *decompressed_pixels = 230 | reinterpret_cast(&decompressed_buffer->at(0)); 231 | 232 | const int height = static_cast(metadata.uncompressed_height); 233 | const int width = static_cast(metadata.uncompressed_width); 234 | 235 | const int num_block_rows = GetNumBlocks(metadata.uncompressed_height); 236 | const int num_block_cols = GetNumBlocks(metadata.uncompressed_width); 237 | const bool swap_red_and_blue = NeedsRedAndBlueSwapped(metadata.format); 238 | const int bytes_per_row = width * sizeof(decompressed_pixels[0]) + 239 | metadata.padding_bytes_per_row; 240 | 241 | for (int block_row = 0; block_row < num_block_rows; ++block_row) { 242 | for (int block_col = 0; block_col < num_block_cols; ++block_col) { 243 | // Decode one 4x4 block of pixel colors. 244 | ColorType block_pixels[4][4]; 245 | decode(*block++, swap_red_and_blue, block_pixels); 246 | 247 | // Store the pixels in the destination buffer, checking first 248 | // if the pixels are outside the destination image area. 249 | // TODO(user): Use separate loops if this is too slow. 250 | const int image_row = 4 * block_row; 251 | const int image_col = 4 * block_col; 252 | const int rows_inside_image = std::min(4, height - image_row); 253 | const int cols_inside_image = std::min(4, width - image_col); 254 | if (rows_inside_image > 0 && cols_inside_image > 0) 255 | StoreColorsInBuffer(block_pixels, bytes_per_row, 256 | rows_inside_image, cols_inside_image, 257 | image_row, image_col, 258 | decompressed_pixels); 259 | } 260 | } 261 | return true; 262 | } 263 | 264 | template 266 | bool Compressor4x4Helper::Downsample(EncodeFunctor encode, DecodeFunctor decode, 267 | const CompressedImage &image, 268 | CompressedImage *downsampled_image) { 269 | DCHECK(downsampled_image); 270 | // TODO(user): As with compression, can avoid reducing blocks outside 271 | // the original uncompressed image - just downsample once and copy 272 | // across rows and columns. 273 | 274 | // Downsampling requires an even number of blocks in both dimensions, except 275 | // for the special case of 1: if there is 1 block in a dimension, the 276 | // downsampled version will also have 1 block in that dimension, meaning the 277 | // minimum downsampled image size is 4 in each dimension. 278 | const CompressedImage::Metadata &metadata = image.GetMetadata(); 279 | const int num_orig_block_rows = GetNumBlocks(metadata.uncompressed_height); 280 | const int num_orig_block_cols = GetNumBlocks(metadata.uncompressed_width); 281 | if ((num_orig_block_rows > 1 && num_orig_block_rows % 2 != 0) || 282 | (num_orig_block_cols > 1 && num_orig_block_cols % 2 != 0)) { 283 | return false; 284 | } 285 | 286 | const uint32 orig_height = metadata.uncompressed_height; 287 | const uint32 orig_width = metadata.uncompressed_width; 288 | const uint32 downsampled_height = (orig_height + 1) / 2; 289 | const uint32 downsampled_width = (orig_width + 1) / 2; 290 | if (!SetUpCompressedImage(metadata.compressor_name, sizeof(BlockType), 291 | metadata.format, downsampled_height, 292 | downsampled_width, 0, downsampled_image)) { 293 | return false; 294 | } 295 | 296 | const BlockType *orig_blocks = 297 | reinterpret_cast(image.GetData()); 298 | BlockType *downsampled_block = 299 | reinterpret_cast(downsampled_image->GetMutableData()); 300 | const int num_downsampled_block_rows = num_orig_block_rows / 2; 301 | const int num_downsampled_block_cols = num_orig_block_cols / 2; 302 | 303 | // There are four possible cases, depending on whether there are multiple 304 | // rows and columns of blocks to downsample. 305 | if (num_orig_block_rows > 1 && num_orig_block_cols > 1) { 306 | // The common case of at least 2 blocks in each dimension. 307 | const BlockType *blocks_to_downsample[2][2]; 308 | for (int row = 0; row < num_downsampled_block_rows; ++row) { 309 | for (int col = 0; col < num_downsampled_block_cols; ++col) { 310 | const int r0 = 2 * (num_orig_block_cols * row + col); 311 | const int r1 = r0 + num_orig_block_cols; 312 | blocks_to_downsample[0][0] = &orig_blocks[r0]; 313 | blocks_to_downsample[0][1] = &orig_blocks[r0 + 1]; 314 | blocks_to_downsample[1][0] = &orig_blocks[r1]; 315 | blocks_to_downsample[1][1] = &orig_blocks[r1 + 1]; 316 | *downsampled_block++ = 317 | DownsampleBlocks2x2( 319 | encode, decode, blocks_to_downsample); 320 | } 321 | } 322 | } else if (num_orig_block_rows > 1) { 323 | // At least 2 rows of blocks, only one column of blocks. 324 | const BlockType *blocks_to_downsample[2]; 325 | for (int row = 0; row < num_downsampled_block_rows; ++row) { 326 | blocks_to_downsample[0] = &orig_blocks[2 * row]; 327 | blocks_to_downsample[1] = &orig_blocks[2 * row + 1]; 328 | *downsampled_block++ = 329 | DownsampleBlocks2x1( 331 | encode, decode, blocks_to_downsample); 332 | } 333 | } else if (num_orig_block_cols > 1) { 334 | // At least 2 columns of blocks, only one row of blocks. 335 | const BlockType *blocks_to_downsample[2]; 336 | for (int col = 0; col < num_downsampled_block_cols; ++col) { 337 | blocks_to_downsample[0] = &orig_blocks[2 * col]; 338 | blocks_to_downsample[1] = &orig_blocks[2 * col + 1]; 339 | *downsampled_block++ = 340 | DownsampleBlocks1x2( 342 | encode, decode, blocks_to_downsample); 343 | } 344 | } else { 345 | // Only 1 row and 1 column. This means each dimension of the original 346 | // block must be 4, 2, or 1. 347 | if (orig_height == 3 || orig_width == 3) 348 | return false; 349 | 350 | ColorType decoded_pixels[4][4]; 351 | Pixel4x4 pixel4x4; 352 | decode(orig_blocks[0], false, decoded_pixels); 353 | 354 | // Replicate pixels if necessary to fill the full 4x4 array so that the 355 | // standard 4x4 block downsampler can be used. 356 | if (orig_width == 1) { 357 | for (int row = 0; row < 4; ++row) { 358 | decoded_pixels[row][1] = decoded_pixels[row][0]; 359 | decoded_pixels[row][2] = decoded_pixels[row][0]; 360 | decoded_pixels[row][3] = decoded_pixels[row][0]; 361 | } 362 | } else if (orig_width == 2) { 363 | for (int row = 0; row < 4; ++row) { 364 | decoded_pixels[row][2] = decoded_pixels[row][0]; 365 | decoded_pixels[row][3] = decoded_pixels[row][1]; 366 | } 367 | } 368 | if (orig_height == 1) { 369 | for (int col = 0; col < 4; ++col) { 370 | decoded_pixels[1][col] = decoded_pixels[0][col]; 371 | decoded_pixels[2][col] = decoded_pixels[0][col]; 372 | decoded_pixels[3][col] = decoded_pixels[0][col]; 373 | } 374 | } else if (orig_height == 2) { 375 | for (int col = 0; col < 4; ++col) { 376 | decoded_pixels[2][col] = decoded_pixels[0][col]; 377 | decoded_pixels[3][col] = decoded_pixels[1][col]; 378 | } 379 | } 380 | 381 | // Now downsample it. 382 | for (int row = 0; row < 2; ++row) { 383 | for (int col = 0; col < 2; ++col) { 384 | StoreDownsampledPixels4x4(decoded_pixels, 2 * row, 2 * col, 385 | &pixel4x4); 386 | } 387 | } 388 | *downsampled_block = encode(pixel4x4, false); 389 | } 390 | return true; 391 | } 392 | 393 | template 395 | bool Compressor4x4Helper::Pad(ColumnPadBlockFunctor get_column_pad_block, 396 | RowPadBlockFunctor get_row_pad_block, 397 | CornerPadBlockFunctor get_corner_pad_block, 398 | const CompressedImage &image, 399 | uint32 padded_height, uint32 padded_width, 400 | CompressedImage *padded_image) { 401 | DCHECK(padded_image); 402 | // No padding to do if the sizes are not larger. Just copy. 403 | const CompressedImage::Metadata &metadata = image.GetMetadata(); 404 | if (metadata.compressed_height >= padded_height && 405 | metadata.compressed_width >= padded_width) { 406 | padded_image->Duplicate(image); 407 | return true; 408 | } 409 | 410 | if (!SetUpCompressedImage(metadata.compressor_name, sizeof(BlockType), 411 | metadata.format, padded_height, padded_width, 412 | 0, padded_image)) { 413 | return false; 414 | } 415 | const int num_orig_block_rows = GetNumBlocks(metadata.compressed_height); 416 | const int num_orig_block_cols = GetNumBlocks(metadata.compressed_width); 417 | const int num_padded_block_rows = GetNumBlocks(padded_height); 418 | const int num_padded_block_cols = GetNumBlocks(padded_width); 419 | 420 | // Operate on blocks to save time. 421 | const BlockType *orig_block_start = 422 | reinterpret_cast(image.GetData()); 423 | BlockType *padded_block_start = 424 | reinterpret_cast(padded_image->GetMutableData()); 425 | 426 | // Copy unpadded blocks. 427 | const BlockType *orig_block = orig_block_start; 428 | BlockType *padded_block = padded_block_start; 429 | const int num_orig_row_bytes = num_orig_block_cols * sizeof(BlockType); 430 | for (int row = 0; row < num_orig_block_rows; ++row) { 431 | memcpy(padded_block, orig_block, num_orig_row_bytes); 432 | 433 | if (num_orig_block_cols < num_padded_block_cols) { 434 | // Create a block to replicate the last column and copy it in 435 | // the space to the right of the orig image. 436 | const BlockType *last_col_block = padded_block + num_orig_block_cols - 1; 437 | BlockType pad_block(get_column_pad_block(*last_col_block)); 438 | for (int col = num_orig_block_cols; col < num_padded_block_cols; ++col) 439 | padded_block[col] = pad_block; 440 | } 441 | 442 | orig_block += num_orig_block_cols; 443 | padded_block += num_padded_block_cols; 444 | } 445 | 446 | if (num_orig_block_rows < num_padded_block_rows) { 447 | // Create a row of blocks to replicate the last row (including 448 | // padding on the right) and copy it into the space below and to 449 | // the right of the original image. 450 | const BlockType *last_block_row = 451 | orig_block_start + (num_orig_block_rows - 1) * num_orig_block_cols; 452 | 453 | std::vector last_padded_row_blocks; 454 | last_padded_row_blocks.reserve(num_padded_block_cols); 455 | // Create row pad blocks for the left part of the row. 456 | for (int col = 0; col < num_orig_block_cols; ++col) 457 | last_padded_row_blocks.push_back(get_row_pad_block(last_block_row[col])); 458 | // Create a corner pad block for the rest of the row. 459 | if (num_orig_block_cols < num_padded_block_cols) { 460 | last_padded_row_blocks.insert( 461 | last_padded_row_blocks.end(), 462 | num_padded_block_cols - num_orig_block_cols, 463 | get_corner_pad_block(*(last_block_row + num_orig_block_cols - 1))); 464 | } 465 | // Copy the row into the padded buffer. 466 | BlockType *padded_block_row = 467 | padded_block_start + num_orig_block_rows * num_padded_block_cols; 468 | const int num_padded_row_bytes = num_padded_block_cols * sizeof(BlockType); 469 | for (int row = num_orig_block_rows; row < num_padded_block_rows; ++row) { 470 | memcpy(padded_block_row, &last_padded_row_blocks[0], 471 | num_padded_row_bytes); 472 | padded_block_row += num_padded_block_cols; 473 | } 474 | } 475 | 476 | return true; 477 | } 478 | 479 | template 480 | bool Compressor4x4Helper::CompressAndPad( 481 | EncodeFunctor encode, const std::string &compressor_name, 482 | CompressedImage::Format format, uint32 height, uint32 width, 483 | uint32 padded_height, uint32 padded_width, uint32 padding_bytes_per_row, 484 | const uint8 *buffer, CompressedImage *padded_image) { 485 | DCHECK(buffer); 486 | DCHECK(padded_image); 487 | const uint32 final_height = std::max(height, padded_height); 488 | const uint32 final_width = std::max(width, padded_width); 489 | 490 | if (!SetUpCompressedImage(compressor_name, sizeof(BlockType), 491 | format, final_height, final_width, 492 | padding_bytes_per_row, padded_image)) { 493 | return false; 494 | } 495 | 496 | const ColorType *uncompressed_pixels = 497 | reinterpret_cast(buffer); 498 | BlockType *block = 499 | reinterpret_cast(padded_image->GetMutableData()); 500 | const bool swap_red_and_blue = NeedsRedAndBlueSwapped(format); 501 | 502 | const uint32 num_block_rows = GetNumBlocks(final_height); 503 | const uint32 num_block_cols = GetNumBlocks(final_width); 504 | DCHECK_GT(num_block_rows, 0U); 505 | DCHECK_GT(num_block_cols, 0U); 506 | for (uint32 row = 0; row < num_block_rows; ++row) { 507 | for (uint32 col = 0; col < num_block_cols; ++col) { 508 | // Encode one 4x4 block of pixels. The Pixel4x4 constructor 509 | // handles the cases where the 4x4 window is no longer over 510 | // valid uncompressed data. 511 | DCHECK_LT(static_cast(block), 512 | static_cast(padded_image->GetData() + 513 | padded_image->GetDataSize())); 514 | *block++ = encode(Pixel4x4(uncompressed_pixels, height, width, 515 | padding_bytes_per_row, row * 4, col * 4), 516 | swap_red_and_blue); 517 | } 518 | } 519 | return true; 520 | } 521 | 522 | template 523 | bool Compressor4x4Helper::CreateSolidImage( 524 | const std::string &compressor_name, 525 | CompressedImage::Format format, uint32 height, uint32 width, 526 | const BlockType &block, CompressedImage *image) { 527 | if (!SetUpCompressedImage(compressor_name, sizeof(BlockType), 528 | format, height, width, 0, image)) { 529 | return false; 530 | } 531 | 532 | const uint32 num_block_rows = GetNumBlocks(height); 533 | const uint32 num_block_cols = GetNumBlocks(width); 534 | const uint32 num_blocks = num_block_rows * num_block_cols; 535 | 536 | // Copy block repeatedly. 537 | BlockType *block_start = 538 | reinterpret_cast(image->GetMutableData()); 539 | for (uint32 i = 0; i < num_blocks; ++i) 540 | block_start[i] = block; 541 | 542 | return true; 543 | } 544 | 545 | template 546 | bool Compressor4x4Helper::CopySubimage(const CompressedImage &image, 547 | uint32 start_row, uint32 start_column, 548 | uint32 height, uint32 width, 549 | CompressedImage *subimage) { 550 | DCHECK(subimage); 551 | 552 | // Verify that all values are multiples of 4 and the subregion is 553 | // fully contained within the original image. 554 | const CompressedImage::Metadata &metadata = image.GetMetadata(); 555 | if (start_row % 4 != 0 || start_column % 4 != 0 || 556 | height % 4 != 0 || width % 4 != 0 || 557 | start_row > metadata.compressed_height || 558 | start_column > metadata.compressed_width || 559 | start_row + height > metadata.compressed_height || 560 | start_column + width > metadata.compressed_width) { 561 | return false; 562 | } 563 | 564 | if (!SetUpCompressedImage(metadata.compressor_name, sizeof(BlockType), 565 | metadata.format, height, width, 0, subimage)) { 566 | return false; 567 | } 568 | 569 | const BlockType* orig_block_start = 570 | reinterpret_cast(image.GetData()); 571 | BlockType* subimage_block_start = 572 | reinterpret_cast(subimage->GetMutableData()); 573 | 574 | const int num_orig_block_cols = GetNumBlocks(metadata.compressed_width); 575 | const int orig_start_block_col = GetNumBlocks(start_column); 576 | const int orig_start_block_row = GetNumBlocks(start_row); 577 | const int num_subimage_block_rows = GetNumBlocks(height); 578 | const int num_subimage_block_cols = GetNumBlocks(width); 579 | 580 | const BlockType* orig_block = 581 | orig_block_start + orig_start_block_row * num_orig_block_cols + 582 | orig_start_block_col; 583 | BlockType* subimage_block = subimage_block_start; 584 | for (int row = 0; row < num_subimage_block_rows; ++row) { 585 | memcpy(subimage_block, orig_block, 586 | num_subimage_block_cols * sizeof(*subimage_block)); 587 | orig_block += num_orig_block_cols; 588 | subimage_block += num_subimage_block_cols; 589 | } 590 | 591 | return true; 592 | } 593 | 594 | template 596 | const BlockType Compressor4x4Helper::DownsampleBlocks2x2( 597 | EncodeFunctor encode, DecodeFunctor decode, const BlockType *blocks[2][2]) { 598 | ColorType decoded_pixels[4][4]; 599 | Pixel4x4 pixel4x4; 600 | for (int row = 0; row < 2; ++row) { 601 | for (int col = 0; col < 2; ++col) { 602 | decode(*blocks[row][col], false, decoded_pixels); 603 | StoreDownsampledPixels4x4(decoded_pixels, 2 * row, 2 * col, 604 | &pixel4x4); 605 | } 606 | } 607 | return encode(pixel4x4, false); 608 | } 609 | 610 | template 612 | const BlockType Compressor4x4Helper::DownsampleBlocks2x1( 613 | EncodeFunctor encode, DecodeFunctor decode, const BlockType *blocks[2]) { 614 | ColorType decoded_pixels[4][4]; 615 | Pixel4x4 pixel4x4; 616 | for (int row = 0; row < 2; ++row) { 617 | decode(*blocks[row], false, decoded_pixels); 618 | StoreDownsampledPixels4x4(decoded_pixels, 2 * row, 0, &pixel4x4); 619 | StoreDownsampledPixels4x4(decoded_pixels, 2 * row, 2, &pixel4x4); 620 | } 621 | return encode(pixel4x4, false); 622 | } 623 | 624 | template 626 | const BlockType Compressor4x4Helper::DownsampleBlocks1x2( 627 | EncodeFunctor encode, DecodeFunctor decode, const BlockType *blocks[2]) { 628 | ColorType decoded_pixels[4][4]; 629 | Pixel4x4 pixel4x4; 630 | for (int col = 0; col < 2; ++col) { 631 | decode(*blocks[col], false, decoded_pixels); 632 | StoreDownsampledPixels4x4(decoded_pixels, 0, 2 * col, &pixel4x4); 633 | StoreDownsampledPixels4x4(decoded_pixels, 2, 2 * col, &pixel4x4); 634 | } 635 | return encode(pixel4x4, false); 636 | } 637 | 638 | } // namespace image_codec_compression 639 | 640 | #endif // IMAGE_COMPRESSION_INTERNAL_COMPRESSOR4X4_HELPER_H_ 641 | -------------------------------------------------------------------------------- /image_compression/internal/dxtc_compressor.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "image_compression/public/dxtc_compressor.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "base/integral_types.h" 21 | #include "base/logging.h" 22 | #include "image_compression/internal/color_util.h" 23 | #include "image_compression/internal/compressor4x4_helper.h" 24 | #include "image_compression/internal/dxtc_const_color_table.h" 25 | #include "image_compression/internal/pixel4x4.h" 26 | 27 | namespace image_codec_compression { 28 | 29 | //----------------------------------------------------------------------------- 30 | 31 | // 32 | // DXT-specific data structures. 33 | // 34 | 35 | // DXT1 data representation for a compressed 4x4 block of RGB pixels. 36 | struct Dxt1Block { 37 | typedef Rgb888 ColorType; 38 | 39 | Dxt1Block() { 40 | } 41 | 42 | // Constructor for a solid-color block. 43 | explicit Dxt1Block(const Rgb565 &color) { 44 | uint16 c = ToUInt16(color); 45 | c1_lo = c0_lo = c & 0xff; 46 | c1_hi = c0_hi = c >> 8; 47 | for (int i = 0; i < 4; ++i) 48 | color_bits[i] = 0; 49 | } 50 | 51 | // Constructor for a general block. 52 | Dxt1Block(const Rgb565 &color0, const Rgb565 &color1, const uint8 bits[4]) { 53 | uint16 c0 = ToUInt16(color0); 54 | uint16 c1 = ToUInt16(color1); 55 | c0_lo = c0 & 0xff; 56 | c0_hi = c0 >> 8; 57 | c1_lo = c1 & 0xff; 58 | c1_hi = c1 >> 8; 59 | for (int i = 0; i < 4; ++i) 60 | color_bits[i] = bits[i]; 61 | } 62 | 63 | // Compressed data packed into 8 bytes. 64 | uint8 c0_lo, c0_hi; 65 | uint8 c1_lo, c1_hi; 66 | uint8 color_bits[4]; 67 | }; 68 | 69 | // DXT5 data representation for a compressed 4x4 block of RGBA pixels. 70 | struct Dxt5Block { 71 | typedef Rgba8888 ColorType; 72 | 73 | Dxt5Block() { 74 | } 75 | 76 | // Constructor for a block where all pixels have the same alpha value. 77 | Dxt5Block(const Dxt1Block &block, uint8 alpha) { 78 | dxt1_block = block; 79 | alpha1 = alpha0 = alpha; 80 | for (int i = 0; i < 6; ++i) 81 | alpha_bits[i] = 0; 82 | } 83 | 84 | // Constructor for a general block. 85 | Dxt5Block(const Dxt1Block &block, uint8 base_alphas[2], const uint8 bits[6]) { 86 | dxt1_block = block; 87 | alpha0 = base_alphas[0]; 88 | alpha1 = base_alphas[1]; 89 | for (int i = 0; i < 6; ++i) 90 | alpha_bits[i] = bits[i]; 91 | } 92 | 93 | // Compressed data packed into 8 bytes + a Dxt1Block. 94 | uint8 alpha0, alpha1; 95 | uint8 alpha_bits[6]; 96 | Dxt1Block dxt1_block; 97 | }; 98 | 99 | // This class is used for processing alpha code bits for DXT5. Alpha 100 | // codes are 3 bits for each of the 16 pixels in a 4x4 block and are 101 | // stored in 6 8-bit bytes. Therefore, we use a 48-bit bitset to make 102 | // the bit addressing easier. 103 | class Dxt5AlphaBits { 104 | public: 105 | // The default constructor sets all bits to 0. 106 | Dxt5AlphaBits() { 107 | } 108 | 109 | // Constructor that sets all bits from an array of 6 bytes. 110 | explicit Dxt5AlphaBits(const uint8 bytes[6]) { 111 | int cur_index = 0; 112 | for (int i = 0; i < 6; ++i) { 113 | uint8 cur_byte = bytes[i]; 114 | for (int j = 0; j < 8; ++j) { 115 | bits_.set(cur_index++, cur_byte & 1); 116 | cur_byte >>= 1; 117 | } 118 | } 119 | } 120 | 121 | // Sets the 3-bit alpha code for pixel n (where 0 is the lowest-order pixel). 122 | void SetCode(int n, int code) { 123 | DCHECK_GE(n, 0); 124 | DCHECK_LT(n, 16); 125 | DCHECK_GE(code, 0); 126 | DCHECK_LT(code, 8); 127 | const int lsb = n * 3; 128 | bits_.set(lsb, code & 1); 129 | bits_.set(lsb + 1, code & 2); 130 | bits_.set(lsb + 2, code & 4); 131 | } 132 | 133 | // Returns the 3-bit alpha code for pixel n (where 0 is the 134 | // lowest-order pixel). 135 | int GetCode(int n) const { 136 | DCHECK_GE(n, 0); 137 | DCHECK_LT(n, 16); 138 | const int lsb = n * 3; 139 | return (static_cast((bits_.test(lsb + 2)) << 2) | 140 | static_cast((bits_.test(lsb + 1)) << 1) | 141 | static_cast(bits_.test(lsb))); 142 | } 143 | 144 | // Converts the bitset back to an array of 6 bytes for use in a Dxt5Block. 145 | void GetBytes(uint8 bytes[6]) const { 146 | int cur_index = 0; 147 | for (int i = 0; i < 6; ++i) { 148 | uint8 cur_byte = 0; 149 | for (int j = 0; j < 8; ++j) { 150 | cur_byte |= (bits_.test(cur_index++) << j); 151 | } 152 | bytes[i] = cur_byte; 153 | } 154 | } 155 | 156 | private: 157 | std::bitset<48> bits_; 158 | }; 159 | 160 | //----------------------------------------------------------------------------- 161 | 162 | // 163 | // General DXT block decoding functions. 164 | // 165 | 166 | // Decodes the 4 possible colors for a Dxt1Block. 167 | static void DecodeColors(const Dxt1Block &block, Rgb888 colors[4], 168 | bool swap_red_and_blue, bool always_4_color_case) { 169 | const uint16 color0_as_uint16 = block.c0_lo + block.c0_hi * 256; 170 | const uint16 color1_as_uint16 = block.c1_lo + block.c1_hi * 256; 171 | 172 | const Rgb565 color0_as_565 = ToRgb565(color0_as_uint16); 173 | const Rgb565 color1_as_565 = ToRgb565(color1_as_uint16); 174 | 175 | colors[0] = ExtendToRgb888(color0_as_565); 176 | colors[1] = ExtendToRgb888(color1_as_565); 177 | 178 | if (swap_red_and_blue) { 179 | SwapRedAndBlue(&colors[0]); 180 | SwapRedAndBlue(&colors[1]); 181 | } 182 | 183 | if (color0_as_uint16 == color1_as_uint16) { 184 | colors[2] = colors[3] = colors[1]; 185 | } else if (always_4_color_case || color0_as_uint16 > color1_as_uint16) { 186 | colors[2] = CombineRgb888Fast<2, 1>(colors[0], colors[1]); 187 | colors[3] = CombineRgb888Fast<1, 2>(colors[0], colors[1]); 188 | } else { 189 | colors[2] = CombineRgb888Fast<1, 1>(colors[0], colors[1]); 190 | colors[3].r = colors[3].g = colors[3].b = 0x00; 191 | } 192 | } 193 | 194 | // Decodes the 8 possible alpha values for a Dxt5Block. 195 | static void DecodeAlphaValues(const Dxt5Block &dxt5_block, uint8 alpha[8]) { 196 | const uint8 alpha0 = dxt5_block.alpha0; 197 | const uint8 alpha1 = dxt5_block.alpha1; 198 | 199 | alpha[0] = alpha0; 200 | alpha[1] = alpha1; 201 | 202 | if (alpha0 > alpha1) { 203 | alpha[2] = CombineUint8Fast<6, 1>(alpha[0], alpha[1]); 204 | alpha[3] = CombineUint8Fast<5, 2>(alpha[0], alpha[1]); 205 | alpha[4] = CombineUint8Fast<4, 3>(alpha[0], alpha[1]); 206 | alpha[5] = CombineUint8Fast<3, 4>(alpha[0], alpha[1]); 207 | alpha[6] = CombineUint8Fast<2, 5>(alpha[0], alpha[1]); 208 | alpha[7] = CombineUint8Fast<1, 6>(alpha[0], alpha[1]); 209 | } else { 210 | alpha[2] = CombineUint8Fast<4, 1>(alpha[0], alpha[1]); 211 | alpha[3] = CombineUint8Fast<3, 2>(alpha[0], alpha[1]); 212 | alpha[4] = CombineUint8Fast<2, 3>(alpha[0], alpha[1]); 213 | alpha[5] = CombineUint8Fast<1, 4>(alpha[0], alpha[1]); 214 | alpha[6] = 0; 215 | alpha[7] = 255; 216 | } 217 | } 218 | 219 | // Decodes a single Dxt1Block into a 4x4 array of RGB pixels. 220 | void DecodeDxt1Block(const Dxt1Block &dxt1_block, bool swap_red_and_blue, 221 | Rgb888 decoded_pixels[4][4]) { 222 | // Decode the 4 colors defined by the block. 223 | Rgb888 colors[4]; 224 | DecodeColors(dxt1_block, colors, swap_red_and_blue, false); 225 | 226 | // Decode the color indices and use them to determine pixel colors. 227 | // The index of the color to use for pixel (0,0) is the lowest-order 228 | // 2 bits of the first entry in the bits[] array. The next 2 bits up 229 | // are for pixel (1,0), and so on. 230 | for (int y = 0; y < 4; ++y) { 231 | const uint8 bits = dxt1_block.color_bits[y]; 232 | for (int x = 0; x < 4; ++x) { 233 | uint8 code = (bits >> (2 * x)) & 3; 234 | decoded_pixels[y][x] = colors[code]; 235 | } 236 | } 237 | } 238 | 239 | // Decodes a single Dxt5Block into a 4x4 array of RGBA pixels. 240 | static void DecodeDxt5Block(const Dxt5Block &block, bool swap_red_and_blue, 241 | Rgba8888 decoded_pixels[4][4]) { 242 | // Decode the color part of the block a la DXT1, but always using 243 | // the 4-color case. 244 | Rgb888 colors[4]; 245 | DecodeColors(block.dxt1_block, colors, swap_red_and_blue, true); 246 | 247 | // Decode the alpha values in the block. 248 | uint8 alpha[8]; 249 | DecodeAlphaValues(block, alpha); 250 | 251 | // Decode the color indices and alpha codes. The color indexing is 252 | // the same as for DXT1. The alpha value to use for pixel (0,0) is 253 | // the lowest-order 3 bits of the first entry in the alpha_bits[] 254 | // array. The next 3 bits up are for pixel (1,0), and so on through 255 | // the other entries. The Dxt5AlphaBits class simplifies the loop. 256 | const Dxt5AlphaBits alpha_bits(block.alpha_bits); 257 | int cur_alpha_index = 0; 258 | for (int y = 0; y < 4; ++y) { 259 | const uint8 color_bits = block.dxt1_block.color_bits[y]; 260 | for (int x = 0; x < 4; ++x) { 261 | const uint8 color_code = (color_bits >> (2 * x)) & 3; 262 | const uint8 alpha_code = alpha_bits.GetCode(cur_alpha_index); 263 | decoded_pixels[y][x] = Rgba8888(colors[color_code], alpha[alpha_code]); 264 | cur_alpha_index++; 265 | } 266 | } 267 | } 268 | 269 | //----------------------------------------------------------------------------- 270 | 271 | // 272 | // General DXT block encoding functions. 273 | // 274 | 275 | // Returns the size of a single compressed block used to encode the 276 | // given format. 277 | static size_t GetBlockSize(CompressedImage::Format format) { 278 | return GetNumFormatComponents(format) == 3 ? 279 | sizeof(Dxt1Block) : sizeof(Dxt5Block); 280 | } 281 | 282 | // Computes the two base colors to use for compressing the given block 283 | // of pixels. The order of the two colors is arbitrary. 284 | static void ComputeBaseColors(const Pixel4x4 &pixel4x4, bool swap_red_and_blue, 285 | RgbInt base_colors[2]) { 286 | // Use the colors with the smallest and largest luminance. This is 287 | // a fast heuristic that produces reasonable results. 288 | RgbInt low_color = ToRgbOrBgrInt(pixel4x4.GetPixel(0, 0), swap_red_and_blue); 289 | RgbInt high_color = low_color; 290 | if (!pixel4x4.has_one_pixel()) { 291 | int low_luminance = kint32max; 292 | int high_luminance = 0; 293 | for (int y = 0; y < 4; ++y) { 294 | for (int x = 0; x < 4; ++x) { 295 | const RgbInt &color = ToRgbOrBgrInt(pixel4x4.GetPixel(y, x), 296 | swap_red_and_blue); 297 | const int luminance = ComputeLuminanceFast(color); 298 | if (luminance < low_luminance) { 299 | low_luminance = luminance; 300 | low_color = color; 301 | } 302 | if (luminance > high_luminance) { 303 | high_luminance = luminance; 304 | high_color = color; 305 | } 306 | } 307 | } 308 | } 309 | base_colors[0] = low_color; 310 | base_colors[1] = high_color; 311 | } 312 | 313 | // Sets the color_bits parameter to the encoding for the given pixels 314 | // using the two given base colors. 315 | static void ComputeColorBits(const Pixel4x4 &pixel4x4, bool swap_red_and_blue, 316 | const RgbInt base_colors[2], uint8 color_bits[4]) { 317 | // Special case for single-color blocks. 318 | if (pixel4x4.has_one_pixel()) { 319 | color_bits[0] = color_bits[1] = color_bits[2] = color_bits[3] = 0; 320 | return; 321 | } 322 | 323 | // Compute the 4 test colors. 324 | RgbInt test_colors[4]; 325 | test_colors[0] = base_colors[0]; 326 | test_colors[1] = base_colors[1]; 327 | test_colors[2] = CombineRgbIntFast<2, 1>(test_colors[0], test_colors[1]); 328 | test_colors[3] = CombineRgbIntFast<1, 2>(test_colors[0], test_colors[1]); 329 | 330 | for (int y = 0; y < 4; ++y) { 331 | color_bits[y] = 0; 332 | for (int x = 0; x < 4; ++x) { 333 | const RgbInt color = ToRgbOrBgrInt(pixel4x4.GetPixel(y, x), 334 | swap_red_and_blue); 335 | int which_color = 0; 336 | int smallest_luminance_error = 337 | ComputeSquaredLuminanceDistanceFast(color, test_colors[0]); 338 | for (int c = 1; c < 4; ++c) { 339 | const int luminance_error = 340 | ComputeSquaredLuminanceDistanceFast(color, test_colors[c]); 341 | if (luminance_error < smallest_luminance_error) { 342 | smallest_luminance_error = luminance_error; 343 | which_color = c; 344 | } 345 | } 346 | color_bits[y] |= which_color << (2 * x); 347 | } 348 | } 349 | } 350 | 351 | // Sets the color0, color1, and color_bits parameters to the best encoding for 352 | // the base_color. 353 | static void ComputeConstantColorBits(const Pixel4x4 &pixel4x4, 354 | bool swap_red_and_blue, 355 | bool always_4_color_case, 356 | const RgbInt& base_color, 357 | Rgb565* color0, 358 | Rgb565* color1, 359 | uint8 color_bits[4]) { 360 | RgbInt use_color = ToRgbOrBgrInt(base_color, swap_red_and_blue); 361 | int which_color = GetBestDxtcConstColors(use_color, color0, color1, 362 | always_4_color_case); 363 | // Replicate the 2 bit which_color for all the color_bits. 364 | uint8 which_color_byte = which_color | (which_color << 2); 365 | which_color_byte = which_color_byte | (which_color_byte << 4); 366 | for (int y = 0; y < 4; ++y) { 367 | color_bits[y] = which_color_byte; 368 | } 369 | } 370 | 371 | // Computes the two base alpha values to use for compressing the given 372 | // block of pixels. The two values are ordered to specify the correct 373 | // compression scheme according to the DXT5 spec. 374 | static void ComputeBaseAlphas(const Pixel4x4 &pixel4x4, uint8 base_alphas[2]) { 375 | // Special case for single-color blocks. 376 | if (pixel4x4.has_one_pixel()) { 377 | base_alphas[0] = base_alphas[1] = pixel4x4.GetAlpha(0, 0); 378 | return; 379 | } 380 | 381 | // Look for the smallest and largest alpha values, also keeping 382 | // track of how many fully transparent (0) and fully opaque (255) 383 | // values there are. 384 | int num_fully_transparent = 0; 385 | int num_fully_opaque = 0; 386 | int low_alpha = 255; 387 | int high_alpha = 0; 388 | for (int y = 0; y < 4; ++y) { 389 | for (int x = 0; x < 4; ++x) { 390 | const int alpha = pixel4x4.GetAlpha(y, x); 391 | if (alpha == 0) { 392 | num_fully_transparent++; 393 | } else if (alpha == 255) { 394 | num_fully_opaque++; 395 | } else { 396 | if (alpha < low_alpha) 397 | low_alpha = alpha; 398 | if (alpha > high_alpha) 399 | high_alpha = alpha; 400 | } 401 | } 402 | } 403 | 404 | // Make sure high and low alpha values are reasonable. This happens 405 | // only when all values are either 0 or 255. 406 | if (low_alpha > high_alpha) { 407 | low_alpha = 0; 408 | high_alpha = 255; 409 | } 410 | 411 | // If there are enough fully transparent or opaque pixels, use the 412 | // scheme that explicitly includes 0 and 255. 413 | if (num_fully_transparent > 1 || num_fully_opaque > 1) { 414 | base_alphas[0] = low_alpha; 415 | base_alphas[1] = high_alpha; 416 | } else { 417 | if (num_fully_transparent > 0) 418 | low_alpha = 0; 419 | if (num_fully_opaque > 0) 420 | high_alpha = 255; 421 | base_alphas[0] = high_alpha; 422 | base_alphas[1] = low_alpha; 423 | } 424 | } 425 | 426 | // Sets alpha_bits to the encoding for the given pixels. 427 | static void ComputeAlphaBits(const Pixel4x4 &pixel4x4, 428 | const uint8 base_alphas[2], uint8 alpha_bits[6]) { 429 | // Special case for single-color blocks. 430 | if (pixel4x4.has_one_pixel()) { 431 | for (int i = 0; i < 6; ++i) 432 | alpha_bits[i] = 0; 433 | return; 434 | } 435 | 436 | int test_alphas[8]; 437 | test_alphas[0] = base_alphas[0]; 438 | test_alphas[1] = base_alphas[1]; 439 | 440 | if (base_alphas[0] <= base_alphas[1]) { 441 | // Scheme that includes 0 and 255 explicitly: 442 | test_alphas[2] = CombineIntFast<4, 1>(base_alphas[0], base_alphas[1]); 443 | test_alphas[3] = CombineIntFast<3, 2>(base_alphas[0], base_alphas[1]); 444 | test_alphas[4] = CombineIntFast<2, 3>(base_alphas[0], base_alphas[1]); 445 | test_alphas[5] = CombineIntFast<1, 4>(base_alphas[0], base_alphas[1]); 446 | test_alphas[6] = 0; 447 | test_alphas[7] = 255; 448 | } else { 449 | // Scheme that just interpolates endpoint alphas: 450 | test_alphas[2] = CombineIntFast<6, 1>(base_alphas[0], base_alphas[1]); 451 | test_alphas[3] = CombineIntFast<5, 2>(base_alphas[0], base_alphas[1]); 452 | test_alphas[4] = CombineIntFast<4, 3>(base_alphas[0], base_alphas[1]); 453 | test_alphas[5] = CombineIntFast<3, 4>(base_alphas[0], base_alphas[1]); 454 | test_alphas[6] = CombineIntFast<2, 5>(base_alphas[0], base_alphas[1]); 455 | test_alphas[7] = CombineIntFast<1, 6>(base_alphas[0], base_alphas[1]); 456 | } 457 | 458 | // Choose the closest alpha for each pixel. 459 | Dxt5AlphaBits bits; 460 | int cur_alpha_index = 0; 461 | for (int y = 0; y < 4; ++y) { 462 | for (int x = 0; x < 4; ++x) { 463 | const int alpha = pixel4x4.GetAlpha(y, x); 464 | int which_alpha = 0; 465 | int smallest_error = ComputeSquaredComponentDistance(alpha, 466 | test_alphas[0]); 467 | for (int a = 1; a < 8; ++a) { 468 | const int error = ComputeSquaredComponentDistance(alpha, 469 | test_alphas[a]); 470 | if (error < smallest_error) { 471 | smallest_error = error; 472 | which_alpha = a; 473 | } 474 | } 475 | bits.SetCode(cur_alpha_index++, which_alpha); 476 | } 477 | } 478 | bits.GetBytes(alpha_bits); 479 | } 480 | 481 | // Encodes a 4x4 block of RGB pixels into a Dxt1Block, which is returned. 482 | static Dxt1Block EncodeDxt1Block(const Pixel4x4 &pixel4x4, 483 | bool swap_red_and_blue, 484 | bool always_4_color_case) { 485 | // Find the colors in the block to use as base colors for the encoding. 486 | RgbInt base_colors[2]; 487 | ComputeBaseColors(pixel4x4, swap_red_and_blue, base_colors); 488 | 489 | // Convert pixel colors to Rgb565 form and to uint16 for comparisons. 490 | Rgb565 color0 = QuantizeToRgb565(base_colors[0]); 491 | Rgb565 color1 = QuantizeToRgb565(base_colors[1]); 492 | const uint16 color0_16 = ToUInt16(color0); 493 | const uint16 color1_16 = ToUInt16(color1); 494 | 495 | uint8 color_bits[4]; 496 | if (color0_16 == color1_16) { 497 | // Shortcut for the case where the colors are the same, meaning the whole 498 | // block is the same color (or very nearly so). 499 | // This is common in some vector graphics images (like map tiles). 500 | ComputeConstantColorBits(pixel4x4, swap_red_and_blue, always_4_color_case, 501 | base_colors[0], &color0, &color1, color_bits); 502 | } else { 503 | // Otherwise, make sure the colors are ordered properly. 504 | if (color0_16 < color1_16) { 505 | std::swap(base_colors[0], base_colors[1]); 506 | std::swap(color0, color1); 507 | } 508 | 509 | // Compute the color bit codes. 510 | ComputeColorBits(pixel4x4, swap_red_and_blue, base_colors, color_bits); 511 | } 512 | return Dxt1Block(color0, color1, color_bits); 513 | } 514 | 515 | // Encodes a 4x4 block of RGBA pixels into a Dxt5Block, which is returned. 516 | static Dxt5Block EncodeDxt5Block(const Pixel4x4 &pixel4x4, 517 | bool swap_red_and_blue) { 518 | // Find the alpha values in the block to use as base values for the 519 | // encoding. 520 | uint8 base_alphas[2]; 521 | ComputeBaseAlphas(pixel4x4, base_alphas); 522 | 523 | // Compute the alpha bit codes. 524 | uint8 alpha_bits[6]; 525 | ComputeAlphaBits(pixel4x4, base_alphas, alpha_bits); 526 | return Dxt5Block(EncodeDxt1Block(pixel4x4, swap_red_and_blue, true), 527 | base_alphas, alpha_bits); 528 | } 529 | 530 | //----------------------------------------------------------------------------- 531 | 532 | // 533 | // Other helper functions. 534 | // 535 | 536 | // Copies colors from one Dxt1Block to another, leaving the code bits alone. 537 | static inline void CopyDxt1Colors(const Dxt1Block &from_block, 538 | Dxt1Block *to_block) { 539 | to_block->c0_lo = from_block.c0_lo; 540 | to_block->c0_hi = from_block.c0_hi; 541 | to_block->c1_lo = from_block.c1_lo; 542 | to_block->c1_hi = from_block.c1_hi; 543 | } 544 | 545 | // Given the color code bits for a row of a Dxt1Block, this returns 546 | // the code bits to use to copy the column 3 bits to the other 3 547 | // columns. 548 | static inline uint8 CopyColumn3ColorBits(uint8 row_bits) { 549 | // Get the 2 bits for column 3. 550 | const uint8 col3_bit_code = (row_bits >> 6) & 3; 551 | // Replicating those 2 bits to all 4 columns is the same as 552 | // multiplying them by the binary number 01010101 = 0x55. 553 | return col3_bit_code * 0x55; 554 | } 555 | 556 | //----------------------------------------------------------------------------- 557 | 558 | // 559 | // Functors for Compressor4x4Helper. These are all specialized for 560 | // Dxt1Block and Dxt5Block. 561 | // 562 | 563 | template struct DxtcEncode { 564 | const BlockType operator()(const Pixel4x4 &pixel4x4, bool swap_red_and_blue); 565 | }; 566 | 567 | template <> const Dxt1Block DxtcEncode::operator()( 568 | const Pixel4x4 &pixel4x4, bool swap_red_and_blue) { 569 | return EncodeDxt1Block(pixel4x4, swap_red_and_blue, false); 570 | } 571 | 572 | template <> const Dxt5Block DxtcEncode::operator()( 573 | const Pixel4x4 &pixel4x4, bool swap_red_and_blue) { 574 | return EncodeDxt5Block(pixel4x4, swap_red_and_blue); 575 | } 576 | 577 | template struct DxtcDecode { 578 | void operator()(const BlockType &block, bool swap_red_and_blue, 579 | typename BlockType::ColorType decoded_pixels[4][4]); 580 | }; 581 | 582 | template <> void DxtcDecode::operator()( 583 | const Dxt1Block &block, bool swap_red_and_blue, 584 | Rgb888 decoded_pixels[4][4]) { 585 | DecodeDxt1Block(block, swap_red_and_blue, decoded_pixels); 586 | } 587 | 588 | template <> void DxtcDecode::operator()( 589 | const Dxt5Block &block, bool swap_red_and_blue, 590 | Rgba8888 decoded_pixels[4][4]) { 591 | DecodeDxt5Block(block, swap_red_and_blue, decoded_pixels); 592 | } 593 | 594 | template struct DxtcGetColumnPadBlock { 595 | const BlockType operator()(const BlockType &last_column_block); 596 | }; 597 | 598 | template <> const Dxt1Block DxtcGetColumnPadBlock::operator()( 599 | const Dxt1Block &last_column_block) { 600 | // Do this the quick way by using the same colors and just modifying 601 | // the bits for the last 3 columns. 602 | Dxt1Block pad_block; 603 | CopyDxt1Colors(last_column_block, &pad_block); 604 | for (int row = 0; row < 4; ++row) 605 | pad_block.color_bits[row] = 606 | CopyColumn3ColorBits(last_column_block.color_bits[row]); 607 | return pad_block; 608 | } 609 | 610 | template <> const Dxt5Block DxtcGetColumnPadBlock::operator()( 611 | const Dxt5Block &last_column_block) { 612 | // This is essentially the same as the DXT1 version, with the 613 | // addition of dealing with the alpha bits. 614 | Dxt5Block pad_block; 615 | pad_block.dxt1_block = 616 | DxtcGetColumnPadBlock()(last_column_block.dxt1_block); 617 | pad_block.alpha0 = last_column_block.alpha0; 618 | pad_block.alpha1 = last_column_block.alpha1; 619 | Dxt5AlphaBits bits(last_column_block.alpha_bits); 620 | for (int row = 0; row < 4; ++row) { 621 | const int row_offset = 4 * row; 622 | const int col3_code = bits.GetCode(row_offset + 3); 623 | for (int col = 0; col < 3; ++col) 624 | bits.SetCode(row_offset + col, col3_code); 625 | } 626 | bits.GetBytes(pad_block.alpha_bits); 627 | return pad_block; 628 | } 629 | 630 | template struct DxtcGetRowPadBlock { 631 | const BlockType operator()(const BlockType &last_row_block); 632 | }; 633 | 634 | template <> const Dxt1Block DxtcGetRowPadBlock::operator()( 635 | const Dxt1Block &last_row_block) { 636 | // Do this the quick way by using the same colors and just modifying 637 | // the bits for the last 3 rows. 638 | Dxt1Block pad_block; 639 | CopyDxt1Colors(last_row_block, &pad_block); 640 | const uint8 last_row_bits = last_row_block.color_bits[3]; 641 | for (int row = 0; row < 4; ++row) 642 | pad_block.color_bits[row] = last_row_bits; 643 | return pad_block; 644 | } 645 | 646 | template <> const Dxt5Block DxtcGetRowPadBlock::operator()( 647 | const Dxt5Block &last_row_block) { 648 | // This is essentially the same as the DXT1 version, with the 649 | // addition of dealing with the alpha bits. 650 | Dxt5Block pad_block; 651 | pad_block.dxt1_block = 652 | DxtcGetRowPadBlock()(last_row_block.dxt1_block); 653 | pad_block.alpha0 = last_row_block.alpha0; 654 | pad_block.alpha1 = last_row_block.alpha1; 655 | Dxt5AlphaBits bits(last_row_block.alpha_bits); 656 | for (int col = 0; col < 4; ++col) { 657 | const int row3_code = bits.GetCode(4 * 3 + col); 658 | for (int row = 0; row < 3; ++row) 659 | bits.SetCode(4 * row + col, row3_code); 660 | } 661 | bits.GetBytes(pad_block.alpha_bits); 662 | return pad_block; 663 | } 664 | 665 | template struct DxtcGetCornerPadBlock { 666 | const BlockType operator()(const BlockType &last_block); 667 | }; 668 | 669 | template <> const Dxt1Block DxtcGetCornerPadBlock::operator()( 670 | const Dxt1Block &last_block) { 671 | // Do this the quick way by using the same colors and just modifying 672 | // the bits for all 16 pixels. 673 | Dxt1Block pad_block; 674 | CopyDxt1Colors(last_block, &pad_block); 675 | const uint8 last_bits = CopyColumn3ColorBits(last_block.color_bits[3]); 676 | for (int row = 0; row < 4; ++row) 677 | pad_block.color_bits[row] = last_bits; 678 | return pad_block; 679 | } 680 | 681 | template <> const Dxt5Block DxtcGetCornerPadBlock::operator()( 682 | const Dxt5Block &last_block) { 683 | // This is essentially the same as the DXT1 version, with the 684 | // addition of dealing with the alpha bits. 685 | Dxt5Block pad_block; 686 | pad_block.dxt1_block = 687 | DxtcGetCornerPadBlock()(last_block.dxt1_block); 688 | pad_block.alpha0 = last_block.alpha0; 689 | pad_block.alpha1 = last_block.alpha1; 690 | Dxt5AlphaBits bits(last_block.alpha_bits); 691 | uint8 corner_bits = bits.GetCode(15); 692 | for (int i = 0; i < 16; ++i) 693 | bits.SetCode(i, corner_bits); 694 | bits.GetBytes(pad_block.alpha_bits); 695 | return pad_block; 696 | } 697 | 698 | //----------------------------------------------------------------------------- 699 | 700 | // 701 | // Public functions. 702 | // 703 | 704 | DxtcCompressor::~DxtcCompressor() { 705 | } 706 | 707 | bool DxtcCompressor::SupportsFormat(CompressedImage::Format format) const { 708 | // DXTC supports all current formats. 709 | return true; 710 | } 711 | 712 | bool DxtcCompressor::IsValidCompressedImage(const CompressedImage &image) { 713 | const CompressedImage::Metadata &metadata = image.GetMetadata(); 714 | return 715 | metadata.compressor_name == "dxtc" && 716 | metadata.uncompressed_height > 0 && 717 | metadata.uncompressed_width > 0 && 718 | metadata.compressed_height >= metadata.uncompressed_height && 719 | metadata.compressed_width >= metadata.uncompressed_width && 720 | image.GetDataSize() == 721 | ComputeCompressedDataSize(metadata.format, metadata.compressed_height, 722 | metadata.compressed_width); 723 | } 724 | 725 | size_t DxtcCompressor::ComputeCompressedDataSize(CompressedImage::Format format, 726 | uint32 height, uint32 width) { 727 | if (height == 0 || width == 0) 728 | return 0; 729 | const uint32 num_block_rows = Compressor4x4Helper::GetNumBlocks(height); 730 | const uint32 num_block_cols = Compressor4x4Helper::GetNumBlocks(width); 731 | return std::max(1U, num_block_rows) * std::max(1U, num_block_cols) * 732 | GetBlockSize(format); 733 | } 734 | 735 | bool DxtcCompressor::Compress(CompressedImage::Format format, 736 | uint32 height, uint32 width, 737 | uint32 padding_bytes_per_row, 738 | const uint8 *buffer, CompressedImage *image) { 739 | if (!buffer || !image || height == 0 || width == 0) { 740 | return false; 741 | } else if (GetNumFormatComponents(format) == 3) { 742 | return Compressor4x4Helper::Compress( 743 | DxtcEncode(), "dxtc", format, height, width, 744 | padding_bytes_per_row, buffer, image); 745 | } else { 746 | return Compressor4x4Helper::Compress( 747 | DxtcEncode(), "dxtc", format, height, width, 748 | padding_bytes_per_row, buffer, image); 749 | } 750 | } 751 | 752 | bool DxtcCompressor::Decompress(const CompressedImage &image, 753 | std::vector *decompressed_buffer) { 754 | if (!IsValidCompressedImage(image) || !decompressed_buffer) { 755 | return false; 756 | } else if (GetNumFormatComponents(image.GetMetadata().format) == 3) { 757 | return Compressor4x4Helper::Decompress( 758 | DxtcDecode(), image, decompressed_buffer); 759 | } else { 760 | return Compressor4x4Helper::Decompress( 761 | DxtcDecode(), image, decompressed_buffer); 762 | } 763 | } 764 | 765 | bool DxtcCompressor::Downsample(const CompressedImage &image, 766 | CompressedImage *downsampled_image) { 767 | if (!IsValidCompressedImage(image) || !downsampled_image) { 768 | return false; 769 | } else if (GetNumFormatComponents(image.GetMetadata().format) == 3) { 770 | return Compressor4x4Helper::Downsample( 771 | DxtcEncode(), DxtcDecode(), 772 | image, downsampled_image); 773 | } else { 774 | return Compressor4x4Helper::Downsample( 775 | DxtcEncode(), DxtcDecode(), 776 | image, downsampled_image); 777 | } 778 | } 779 | 780 | bool DxtcCompressor::Pad(const CompressedImage &image, uint32 padded_height, 781 | uint32 padded_width, CompressedImage *padded_image) { 782 | if (!IsValidCompressedImage(image) || !padded_image) { 783 | return false; 784 | } else if (GetNumFormatComponents(image.GetMetadata().format) == 3) { 785 | return Compressor4x4Helper::Pad( 786 | DxtcGetColumnPadBlock(), 787 | DxtcGetRowPadBlock(), 788 | DxtcGetCornerPadBlock(), 789 | image, padded_height, padded_width, padded_image); 790 | } else { 791 | return Compressor4x4Helper::Pad( 792 | DxtcGetColumnPadBlock(), 793 | DxtcGetRowPadBlock(), 794 | DxtcGetCornerPadBlock(), 795 | image, padded_height, padded_width, padded_image); 796 | } 797 | } 798 | 799 | bool DxtcCompressor::CompressAndPad(CompressedImage::Format format, 800 | uint32 height, uint32 width, 801 | uint32 padded_height, uint32 padded_width, 802 | uint32 padding_bytes_per_row, 803 | const uint8 *buffer, 804 | CompressedImage *padded_image) { 805 | if (!buffer || !padded_image || height == 0 || width == 0) { 806 | return false; 807 | } else if (GetNumFormatComponents(format) == 3) { 808 | return Compressor4x4Helper::CompressAndPad( 809 | DxtcEncode(), "dxtc", format, height, width, 810 | padded_height, padded_width, padding_bytes_per_row, 811 | buffer, padded_image); 812 | } else { 813 | return Compressor4x4Helper::CompressAndPad( 814 | DxtcEncode(), "dxtc", format, height, width, 815 | padded_height, padded_width, padding_bytes_per_row, 816 | buffer, padded_image); 817 | } 818 | } 819 | 820 | bool DxtcCompressor::CreateSolidImage(CompressedImage::Format format, 821 | uint32 height, uint32 width, 822 | const uint8 *color, 823 | CompressedImage *image) { 824 | if (!image) { 825 | return false; 826 | } else if (GetNumFormatComponents(format) == 3) { 827 | return Compressor4x4Helper::CreateSolidImage( 828 | "dxtc", format, height, width, 829 | Dxt1Block(QuantizeToRgb565(RgbInt(color[0], color[1], color[2]))), 830 | image); 831 | } else { 832 | return Compressor4x4Helper::CreateSolidImage( 833 | "dxtc", format, height, width, 834 | Dxt5Block( 835 | Dxt1Block(QuantizeToRgb565(RgbInt(color[0], color[1], color[2]))), 836 | color[3]), 837 | image); 838 | } 839 | } 840 | 841 | bool DxtcCompressor::CopySubimage(const CompressedImage &image, 842 | uint32 start_row, uint32 start_column, 843 | uint32 height, uint32 width, 844 | CompressedImage *subimage) { 845 | if (!IsValidCompressedImage(image) || !subimage) { 846 | return false; 847 | } else if (GetNumFormatComponents(image.GetMetadata().format) == 3) { 848 | return Compressor4x4Helper::CopySubimage( 849 | image, start_row, start_column, height, width, subimage); 850 | } else { 851 | return Compressor4x4Helper::CopySubimage( 852 | image, start_row, start_column, height, width, subimage); 853 | } 854 | } 855 | 856 | } // namespace image_codec_compression 857 | -------------------------------------------------------------------------------- /image_compression/internal/dxtc_const_color_table.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "image_compression/internal/dxtc_const_color_table.h" 16 | 17 | #include "base/integral_types.h" 18 | 19 | namespace image_codec_compression { 20 | 21 | namespace { 22 | /* 23 | // There are 256 rows, one for each possible 8bit color channel value. 24 | // Each 8 bytes in a row are in the order: 25 | // red/blue 1/3 endpoints, red/blue 1/2 endpoints, 26 | // green 1/3 endpoints, and green 1/2 endpoints. 27 | // The 2/3 endpoints cases use the same colors as the 1/3 cases, just with the 28 | // order flipped. Whether to flip or not depends on the DXT rule which says the 29 | // 565 color0 value must be larger than the color1 value. 30 | // 31 | // The table was generated with the following bit of Python: 32 | 33 | def findEndpoints(uint8_value, channelBits, t): 34 | maxValue = 1 << channelBits 35 | minErr = 256 36 | minI, minJ = -1, -1 37 | float_value = uint8_value/255.0 38 | for i in xrange(maxValue): 39 | for j in xrange(maxValue): 40 | err = abs(float_value - ((1.-t)*i + t*j)/(maxValue-1.0)) 41 | if err < minErr: 42 | minErr = err 43 | minI, minJ = i, j 44 | return (minI, minJ) 45 | 46 | def main(): 47 | format = ', '.join(('%s',) * 8) 48 | for uint8_value in xrange(256): 49 | row = findEndpoints(uint8_value, 5, 1/3.) + \ 50 | findEndpoints(uint8_value, 5, 1/2.) + \ 51 | findEndpoints(uint8_value, 6, 1/3.) + \ 52 | findEndpoints(uint8_value, 6, 1/2.) 53 | print '{', 54 | print format % row, 55 | print '},' 56 | 57 | if __name__ == "__main__": 58 | main() 59 | */ 60 | uint8 best_endpoints_table[256][8] = { 61 | /* 0*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, 62 | /* 1*/ { 0, 0, 0, 0, 0, 1, 0, 0 }, 63 | /* 2*/ { 0, 1, 0, 0, 0, 1, 0, 1 }, 64 | /* 3*/ { 0, 1, 0, 1, 1, 0, 0, 1 }, 65 | /* 4*/ { 0, 1, 0, 1, 0, 3, 0, 2 }, 66 | /* 5*/ { 0, 2, 0, 1, 0, 4, 0, 2 }, 67 | /* 6*/ { 1, 0, 0, 1, 1, 2, 0, 3 }, 68 | /* 7*/ { 0, 3, 0, 2, 1, 3, 0, 3 }, 69 | /* 8*/ { 0, 3, 0, 2, 0, 6, 0, 4 }, 70 | /* 9*/ { 0, 3, 0, 2, 0, 7, 0, 4 }, 71 | /* 10*/ { 0, 4, 0, 2, 2, 3, 0, 5 }, 72 | /* 11*/ { 1, 2, 0, 3, 1, 6, 0, 5 }, 73 | /* 12*/ { 1, 2, 0, 3, 0, 9, 0, 6 }, 74 | /* 13*/ { 0, 5, 0, 3, 0, 10, 0, 6 }, 75 | /* 14*/ { 1, 3, 0, 3, 5, 0, 0, 7 }, 76 | /* 15*/ { 1, 3, 0, 4, 5, 1, 0, 7 }, 77 | /* 16*/ { 0, 6, 0, 4, 0, 12, 0, 8 }, 78 | /* 17*/ { 0, 6, 0, 4, 0, 13, 0, 8 }, 79 | /* 18*/ { 0, 7, 0, 4, 2, 9, 0, 9 }, 80 | /* 19*/ { 0, 7, 0, 5, 1, 12, 0, 9 }, 81 | /* 20*/ { 2, 3, 0, 5, 0, 15, 0, 10 }, 82 | /* 21*/ { 0, 8, 0, 5, 0, 16, 0, 10 }, 83 | /* 22*/ { 1, 6, 0, 5, 2, 12, 0, 11 }, 84 | /* 23*/ { 1, 6, 0, 6, 1, 15, 0, 11 }, 85 | /* 24*/ { 0, 9, 0, 6, 0, 18, 0, 12 }, 86 | /* 25*/ { 4, 1, 0, 6, 0, 19, 0, 12 }, 87 | /* 26*/ { 4, 1, 0, 6, 2, 15, 0, 13 }, 88 | /* 27*/ { 0, 10, 0, 7, 9, 2, 0, 13 }, 89 | /* 28*/ { 5, 0, 0, 7, 0, 21, 0, 14 }, 90 | /* 29*/ { 0, 11, 0, 7, 9, 3, 0, 14 }, 91 | /* 30*/ { 0, 11, 0, 7, 10, 2, 0, 15 }, 92 | /* 31*/ { 5, 1, 0, 8, 0, 23, 0, 15 }, 93 | /* 32*/ { 0, 12, 0, 8, 0, 24, 0, 16 }, 94 | /* 33*/ { 5, 2, 0, 8, 10, 4, 0, 16 }, 95 | /* 34*/ { 5, 2, 0, 8, 2, 21, 0, 17 }, 96 | /* 35*/ { 0, 13, 0, 9, 0, 26, 0, 17 }, 97 | /* 36*/ { 2, 9, 0, 9, 1, 25, 0, 18 }, 98 | /* 37*/ { 2, 9, 0, 9, 13, 1, 0, 18 }, 99 | /* 38*/ { 0, 14, 0, 9, 2, 24, 0, 19 }, 100 | /* 39*/ { 1, 12, 0, 9, 0, 29, 0, 19 }, 101 | /* 40*/ { 0, 15, 0, 10, 1, 28, 0, 20 }, 102 | /* 41*/ { 0, 15, 0, 10, 13, 4, 0, 20 }, 103 | /* 42*/ { 0, 15, 0, 10, 15, 1, 0, 21 }, 104 | /* 43*/ { 0, 16, 0, 10, 0, 32, 0, 21 }, 105 | /* 44*/ { 2, 12, 0, 11, 1, 31, 0, 22 }, 106 | /* 45*/ { 2, 12, 0, 11, 15, 3, 0, 22 }, 107 | /* 46*/ { 0, 17, 0, 11, 15, 4, 0, 23 }, 108 | /* 47*/ { 1, 15, 0, 11, 0, 35, 0, 23 }, 109 | /* 48*/ { 0, 18, 0, 12, 1, 34, 0, 24 }, 110 | /* 49*/ { 0, 18, 0, 12, 15, 6, 0, 24 }, 111 | /* 50*/ { 8, 2, 0, 12, 15, 7, 0, 25 }, 112 | /* 51*/ { 0, 19, 0, 12, 0, 38, 0, 25 }, 113 | /* 52*/ { 0, 19, 0, 13, 1, 37, 0, 26 }, 114 | /* 53*/ { 2, 15, 0, 13, 15, 9, 0, 26 }, 115 | /* 54*/ { 0, 20, 0, 13, 15, 10, 0, 27 }, 116 | /* 55*/ { 9, 2, 0, 13, 0, 41, 0, 27 }, 117 | /* 56*/ { 9, 2, 0, 14, 1, 40, 0, 28 }, 118 | /* 57*/ { 0, 21, 0, 14, 15, 12, 0, 28 }, 119 | /* 58*/ { 9, 3, 0, 14, 0, 43, 0, 29 }, 120 | /* 59*/ { 0, 22, 0, 14, 0, 44, 0, 29 }, 121 | /* 60*/ { 0, 22, 0, 15, 20, 4, 0, 30 }, 122 | /* 61*/ { 10, 2, 0, 15, 15, 15, 0, 30 }, 123 | /* 62*/ { 0, 23, 0, 15, 0, 46, 0, 31 }, 124 | /* 63*/ { 0, 23, 0, 15, 0, 47, 0, 31 }, 125 | /* 64*/ { 9, 5, 0, 16, 23, 1, 0, 32 }, 126 | /* 65*/ { 0, 24, 0, 16, 20, 8, 0, 32 }, 127 | /* 66*/ { 10, 4, 0, 16, 0, 49, 0, 33 }, 128 | /* 67*/ { 10, 4, 0, 16, 0, 50, 0, 33 }, 129 | /* 68*/ { 0, 25, 0, 17, 1, 48, 0, 34 }, 130 | /* 69*/ { 2, 21, 0, 17, 20, 11, 0, 34 }, 131 | /* 70*/ { 0, 26, 0, 17, 0, 52, 0, 35 }, 132 | /* 71*/ { 0, 26, 0, 17, 0, 53, 0, 35 }, 133 | /* 72*/ { 4, 18, 0, 18, 1, 51, 0, 36 }, 134 | /* 73*/ { 1, 25, 0, 18, 23, 8, 0, 36 }, 135 | /* 74*/ { 1, 25, 0, 18, 0, 55, 0, 37 }, 136 | /* 75*/ { 13, 1, 0, 18, 0, 56, 0, 37 }, 137 | /* 76*/ { 0, 28, 0, 18, 27, 2, 0, 38 }, 138 | /* 77*/ { 2, 24, 0, 19, 23, 11, 0, 38 }, 139 | /* 78*/ { 2, 24, 0, 19, 0, 58, 0, 39 }, 140 | /* 79*/ { 0, 29, 0, 19, 0, 59, 0, 39 }, 141 | /* 80*/ { 4, 21, 0, 19, 27, 5, 0, 40 }, 142 | /* 81*/ { 1, 28, 0, 20, 26, 8, 0, 40 }, 143 | /* 82*/ { 1, 28, 0, 20, 0, 61, 0, 41 }, 144 | /* 83*/ { 13, 4, 0, 20, 0, 62, 0, 41 }, 145 | /* 84*/ { 0, 31, 0, 20, 27, 8, 0, 42 }, 146 | /* 85*/ { 0, 31, 0, 21, 0, 63, 0, 42 }, 147 | /* 86*/ { 15, 1, 0, 21, 1, 62, 0, 42 }, 148 | /* 87*/ { 1, 30, 0, 21, 5, 54, 0, 43 }, 149 | /* 88*/ { 4, 24, 0, 21, 27, 11, 0, 43 }, 150 | /* 89*/ { 4, 24, 0, 22, 2, 62, 0, 44 }, 151 | /* 90*/ { 1, 31, 0, 22, 2, 63, 0, 44 }, 152 | /* 91*/ { 15, 3, 0, 22, 5, 57, 0, 45 }, 153 | /* 92*/ { 3, 28, 0, 22, 27, 14, 0, 45 }, 154 | /* 93*/ { 3, 28, 0, 23, 3, 63, 0, 46 }, 155 | /* 94*/ { 15, 4, 0, 23, 4, 62, 0, 46 }, 156 | /* 95*/ { 2, 31, 0, 23, 5, 60, 0, 47 }, 157 | /* 96*/ { 4, 27, 0, 23, 27, 17, 0, 47 }, 158 | /* 97*/ { 4, 27, 0, 24, 5, 62, 0, 48 }, 159 | /* 98*/ { 3, 30, 0, 24, 6, 61, 0, 48 }, 160 | /* 99*/ { 15, 6, 0, 24, 5, 63, 0, 49 }, 161 | /*100*/ { 15, 6, 0, 24, 27, 20, 0, 49 }, 162 | /*101*/ { 3, 31, 0, 25, 6, 63, 0, 50 }, 163 | /*102*/ { 15, 7, 0, 25, 7, 62, 0, 50 }, 164 | /*103*/ { 5, 28, 0, 25, 8, 60, 0, 51 }, 165 | /*104*/ { 5, 28, 0, 25, 27, 23, 0, 51 }, 166 | /*105*/ { 4, 30, 0, 26, 8, 62, 0, 52 }, 167 | /*106*/ { 4, 31, 0, 26, 9, 61, 0, 52 }, 168 | /*107*/ { 15, 9, 0, 26, 8, 63, 0, 53 }, 169 | /*108*/ { 15, 9, 0, 26, 27, 26, 0, 53 }, 170 | /*109*/ { 6, 28, 0, 27, 9, 63, 0, 54 }, 171 | /*110*/ { 15, 10, 0, 27, 10, 62, 0, 54 }, 172 | /*111*/ { 15, 10, 0, 27, 40, 2, 0, 55 }, 173 | /*112*/ { 5, 31, 0, 27, 27, 29, 0, 55 }, 174 | /*113*/ { 20, 1, 0, 27, 11, 62, 0, 56 }, 175 | /*114*/ { 6, 30, 0, 28, 27, 30, 0, 56 }, 176 | /*115*/ { 6, 30, 0, 28, 40, 5, 0, 57 }, 177 | /*116*/ { 15, 12, 0, 28, 12, 62, 0, 57 }, 178 | /*117*/ { 6, 31, 0, 28, 12, 63, 0, 58 }, 179 | /*118*/ { 15, 13, 0, 29, 27, 33, 0, 58 }, 180 | /*119*/ { 15, 13, 0, 29, 40, 8, 0, 59 }, 181 | /*120*/ { 8, 28, 0, 29, 14, 61, 0, 59 }, 182 | /*121*/ { 20, 4, 0, 29, 14, 62, 0, 60 }, 183 | /*122*/ { 20, 4, 0, 30, 27, 36, 0, 60 }, 184 | /*123*/ { 7, 31, 0, 30, 40, 11, 0, 61 }, 185 | /*124*/ { 15, 15, 0, 30, 15, 62, 0, 61 }, 186 | /*125*/ { 9, 28, 0, 30, 15, 63, 0, 62 }, 187 | /*126*/ { 9, 28, 0, 31, 27, 39, 0, 62 }, 188 | /*127*/ { 15, 16, 0, 31, 43, 8, 0, 63 }, 189 | /*128*/ { 8, 31, 0, 31, 17, 61, 0, 63 }, 190 | /*129*/ { 23, 1, 0, 31, 17, 62, 1, 63 }, 191 | /*130*/ { 23, 1, 1, 31, 40, 16, 1, 63 }, 192 | /*131*/ { 9, 30, 1, 31, 17, 63, 2, 63 }, 193 | /*132*/ { 20, 8, 1, 31, 18, 62, 2, 63 }, 194 | /*133*/ { 9, 31, 1, 31, 18, 63, 3, 63 }, 195 | /*134*/ { 9, 31, 2, 31, 40, 19, 3, 63 }, 196 | /*135*/ { 10, 29, 2, 31, 20, 60, 4, 63 }, 197 | /*136*/ { 11, 28, 2, 31, 21, 59, 4, 63 }, 198 | /*137*/ { 11, 28, 2, 31, 20, 62, 5, 63 }, 199 | /*138*/ { 10, 30, 3, 31, 40, 22, 5, 63 }, 200 | /*139*/ { 10, 31, 3, 31, 51, 1, 6, 63 }, 201 | /*140*/ { 20, 11, 3, 31, 21, 62, 6, 63 }, 202 | /*141*/ { 20, 11, 3, 31, 21, 63, 7, 63 }, 203 | /*142*/ { 12, 28, 4, 31, 43, 19, 7, 63 }, 204 | /*143*/ { 11, 30, 4, 31, 22, 62, 8, 63 }, 205 | /*144*/ { 11, 31, 4, 31, 24, 59, 8, 63 }, 206 | /*145*/ { 11, 31, 4, 31, 22, 63, 9, 63 }, 207 | /*146*/ { 13, 27, 4, 31, 43, 22, 9, 63 }, 208 | /*147*/ { 12, 30, 5, 31, 24, 61, 10, 63 }, 209 | /*148*/ { 12, 30, 5, 31, 24, 62, 10, 63 }, 210 | /*149*/ { 23, 8, 5, 31, 25, 60, 11, 63 }, 211 | /*150*/ { 12, 31, 5, 31, 46, 19, 11, 63 }, 212 | /*151*/ { 13, 29, 6, 31, 25, 62, 12, 63 }, 213 | /*152*/ { 13, 29, 6, 31, 48, 17, 12, 63 }, 214 | /*153*/ { 14, 28, 6, 31, 25, 63, 13, 63 }, 215 | /*154*/ { 27, 2, 6, 31, 46, 22, 13, 63 }, 216 | /*155*/ { 13, 31, 7, 31, 28, 59, 14, 63 }, 217 | /*156*/ { 13, 31, 7, 31, 48, 20, 14, 63 }, 218 | /*157*/ { 23, 11, 7, 31, 27, 62, 15, 63 }, 219 | /*158*/ { 24, 10, 7, 31, 49, 19, 15, 63 }, 220 | /*159*/ { 24, 10, 8, 31, 28, 62, 16, 63 }, 221 | /*160*/ { 14, 30, 8, 31, 48, 23, 16, 63 }, 222 | /*161*/ { 14, 31, 8, 31, 28, 63, 17, 63 }, 223 | /*162*/ { 27, 5, 8, 31, 49, 22, 17, 63 }, 224 | /*163*/ { 27, 5, 9, 31, 31, 59, 18, 63 }, 225 | /*164*/ { 15, 30, 9, 31, 48, 26, 18, 63 }, 226 | /*165*/ { 26, 8, 9, 31, 30, 62, 19, 63 }, 227 | /*166*/ { 24, 13, 9, 31, 51, 21, 19, 63 }, 228 | /*167*/ { 24, 13, 10, 31, 31, 62, 20, 63 }, 229 | /*168*/ { 15, 31, 10, 31, 48, 29, 20, 63 }, 230 | /*169*/ { 17, 28, 10, 31, 31, 63, 21, 63 }, 231 | /*170*/ { 17, 28, 10, 31, 32, 62, 21, 63 }, 232 | /*171*/ { 27, 8, 11, 31, 34, 59, 21, 63 }, 233 | /*172*/ { 16, 31, 11, 31, 51, 25, 22, 63 }, 234 | /*173*/ { 26, 11, 11, 31, 33, 62, 22, 63 }, 235 | /*174*/ { 26, 11, 11, 31, 33, 63, 23, 63 }, 236 | /*175*/ { 24, 16, 12, 31, 34, 62, 23, 63 }, 237 | /*176*/ { 17, 30, 12, 31, 51, 28, 24, 63 }, 238 | /*177*/ { 17, 31, 12, 31, 34, 63, 24, 63 }, 239 | /*178*/ { 17, 31, 12, 31, 35, 62, 25, 63 }, 240 | /*179*/ { 27, 11, 13, 31, 37, 59, 25, 63 }, 241 | /*180*/ { 18, 30, 13, 31, 51, 31, 26, 63 }, 242 | /*181*/ { 27, 12, 13, 31, 36, 62, 26, 63 }, 243 | /*182*/ { 27, 12, 13, 31, 36, 63, 27, 63 }, 244 | /*183*/ { 24, 19, 13, 31, 37, 62, 27, 63 }, 245 | /*184*/ { 18, 31, 14, 31, 51, 34, 28, 63 }, 246 | /*185*/ { 18, 31, 14, 31, 37, 63, 28, 63 }, 247 | /*186*/ { 24, 20, 14, 31, 38, 62, 29, 63 }, 248 | /*187*/ { 27, 14, 14, 31, 48, 43, 29, 63 }, 249 | /*188*/ { 19, 31, 15, 31, 51, 37, 30, 63 }, 250 | /*189*/ { 19, 31, 15, 31, 39, 62, 30, 63 }, 251 | /*190*/ { 27, 15, 15, 31, 39, 63, 31, 63 }, 252 | /*191*/ { 24, 22, 15, 31, 48, 46, 31, 63 }, 253 | /*192*/ { 20, 30, 16, 31, 51, 40, 32, 63 }, 254 | /*193*/ { 20, 30, 16, 31, 40, 63, 32, 63 }, 255 | /*194*/ { 24, 23, 16, 31, 41, 62, 33, 63 }, 256 | /*195*/ { 27, 17, 16, 31, 48, 49, 33, 63 }, 257 | /*196*/ { 27, 17, 17, 31, 51, 43, 34, 63 }, 258 | /*197*/ { 21, 30, 17, 31, 42, 62, 34, 63 }, 259 | /*198*/ { 27, 18, 17, 31, 42, 63, 35, 63 }, 260 | /*199*/ { 24, 25, 17, 31, 51, 45, 35, 63 }, 261 | /*200*/ { 24, 25, 18, 31, 51, 46, 36, 63 }, 262 | /*201*/ { 21, 31, 18, 31, 48, 53, 36, 63 }, 263 | /*202*/ { 24, 26, 18, 31, 44, 62, 37, 63 }, 264 | /*203*/ { 27, 20, 18, 31, 51, 48, 37, 63 }, 265 | /*204*/ { 27, 20, 19, 31, 51, 49, 38, 63 }, 266 | /*205*/ { 22, 31, 19, 31, 48, 56, 38, 63 }, 267 | /*206*/ { 27, 21, 19, 31, 45, 63, 39, 63 }, 268 | /*207*/ { 27, 21, 19, 31, 51, 51, 39, 63 }, 269 | /*208*/ { 24, 28, 20, 31, 51, 52, 40, 63 }, 270 | /*209*/ { 23, 30, 20, 31, 48, 59, 40, 63 }, 271 | /*210*/ { 24, 29, 20, 31, 47, 62, 41, 63 }, 272 | /*211*/ { 24, 29, 20, 31, 51, 54, 41, 63 }, 273 | /*212*/ { 27, 23, 21, 31, 51, 55, 42, 63 }, 274 | /*213*/ { 24, 30, 21, 31, 48, 62, 42, 63 }, 275 | /*214*/ { 27, 24, 21, 31, 48, 63, 43, 63 }, 276 | /*215*/ { 27, 24, 21, 31, 51, 57, 43, 63 }, 277 | /*216*/ { 24, 31, 22, 31, 51, 58, 44, 63 }, 278 | /*217*/ { 25, 29, 22, 31, 49, 63, 44, 63 }, 279 | /*218*/ { 25, 30, 22, 31, 50, 62, 45, 63 }, 280 | /*219*/ { 25, 30, 22, 31, 51, 60, 45, 63 }, 281 | /*220*/ { 27, 26, 22, 31, 51, 61, 46, 63 }, 282 | /*221*/ { 25, 31, 23, 31, 51, 62, 46, 63 }, 283 | /*222*/ { 25, 31, 23, 31, 52, 61, 47, 63 }, 284 | /*223*/ { 27, 27, 23, 31, 51, 63, 47, 63 }, 285 | /*224*/ { 26, 30, 23, 31, 54, 58, 48, 63 }, 286 | /*225*/ { 26, 30, 24, 31, 52, 63, 48, 63 }, 287 | /*226*/ { 26, 30, 24, 31, 53, 62, 49, 63 }, 288 | /*227*/ { 26, 31, 24, 31, 54, 60, 49, 63 }, 289 | /*228*/ { 27, 29, 24, 31, 53, 63, 50, 63 }, 290 | /*229*/ { 28, 28, 25, 31, 54, 62, 50, 63 }, 291 | /*230*/ { 28, 28, 25, 31, 54, 62, 51, 63 }, 292 | /*231*/ { 27, 30, 25, 31, 54, 63, 51, 63 }, 293 | /*232*/ { 27, 31, 25, 31, 55, 62, 52, 63 }, 294 | /*233*/ { 27, 31, 26, 31, 55, 63, 52, 63 }, 295 | /*234*/ { 27, 31, 26, 31, 55, 63, 53, 63 }, 296 | /*235*/ { 28, 30, 26, 31, 57, 60, 53, 63 }, 297 | /*236*/ { 30, 26, 26, 31, 56, 63, 54, 63 }, 298 | /*237*/ { 30, 26, 27, 31, 57, 62, 54, 63 }, 299 | /*238*/ { 28, 31, 27, 31, 57, 62, 55, 63 }, 300 | /*239*/ { 30, 27, 27, 31, 57, 63, 55, 63 }, 301 | /*240*/ { 29, 30, 27, 31, 58, 62, 56, 63 }, 302 | /*241*/ { 29, 30, 28, 31, 58, 63, 56, 63 }, 303 | /*242*/ { 29, 30, 28, 31, 58, 63, 57, 63 }, 304 | /*243*/ { 29, 31, 28, 31, 60, 60, 57, 63 }, 305 | /*244*/ { 29, 31, 28, 31, 59, 63, 58, 63 }, 306 | /*245*/ { 30, 29, 29, 31, 60, 62, 58, 63 }, 307 | /*246*/ { 31, 28, 29, 31, 60, 62, 59, 63 }, 308 | /*247*/ { 30, 30, 29, 31, 60, 63, 59, 63 }, 309 | /*248*/ { 30, 30, 29, 31, 61, 62, 60, 63 }, 310 | /*249*/ { 30, 31, 30, 31, 61, 63, 60, 63 }, 311 | /*250*/ { 30, 31, 30, 31, 61, 63, 61, 63 }, 312 | /*251*/ { 31, 30, 30, 31, 63, 60, 61, 63 }, 313 | /*252*/ { 31, 30, 30, 31, 62, 63, 62, 63 }, 314 | /*253*/ { 31, 30, 31, 31, 63, 62, 62, 63 }, 315 | /*254*/ { 31, 31, 31, 31, 63, 62, 63, 63 }, 316 | /*255*/ { 31, 31, 31, 31, 63, 63, 63, 63 }, 317 | }; 318 | 319 | } // namespace 320 | 321 | 322 | int GetBestDxtcConstColors(const RgbInt& target_color, 323 | Rgb565* color0, Rgb565* color1, 324 | bool always_4_color_case) { 325 | Rgb565 single_color = QuantizeToRgb565(target_color); 326 | uint8 which_bits = 0; 327 | RgbInt color_int = ExtendToRgbInt(single_color); 328 | int min_error = ComputeDifferenceLuminanceFast(target_color, color_int); 329 | 330 | *color0 = single_color; 331 | *color1 = single_color; 332 | 333 | RgbInt color_int0, color_int1; 334 | int error; 335 | 336 | if (!always_4_color_case) { 337 | Rgb565 one_halves[2]; 338 | one_halves[0].r = best_endpoints_table[target_color.r][2]; 339 | one_halves[1].r = best_endpoints_table[target_color.r][3]; 340 | one_halves[0].g = best_endpoints_table[target_color.g][6]; 341 | one_halves[1].g = best_endpoints_table[target_color.g][7]; 342 | one_halves[0].b = best_endpoints_table[target_color.b][2]; 343 | one_halves[1].b = best_endpoints_table[target_color.b][3]; 344 | 345 | // Try halves first, because they have more consistent reconstruction 346 | // on a variety of graphics hardware than thirds. (Some hardware uses 3/8 347 | // instead of 1/3). 348 | color_int0 = ExtendToRgbInt(one_halves[0]); 349 | color_int1 = ExtendToRgbInt(one_halves[1]); 350 | color_int = CombineRgbIntFast<1, 1>(color_int0, color_int1); 351 | error = ComputeDifferenceLuminanceFast(target_color, color_int); 352 | if (error < min_error) { 353 | which_bits = 2; 354 | // For halves mode in DXT, color0 must be less than color1. 355 | if (ToUInt16(one_halves[0]) < ToUInt16(one_halves[1])) { 356 | *color0 = one_halves[0]; 357 | *color1 = one_halves[1]; 358 | } else { 359 | *color0 = one_halves[1]; 360 | *color1 = one_halves[0]; 361 | } 362 | min_error = error; 363 | } 364 | } 365 | 366 | Rgb565 one_thirds[2]; 367 | one_thirds[0].r = best_endpoints_table[target_color.r][0]; 368 | one_thirds[1].r = best_endpoints_table[target_color.r][1]; 369 | one_thirds[0].g = best_endpoints_table[target_color.g][4]; 370 | one_thirds[1].g = best_endpoints_table[target_color.g][5]; 371 | one_thirds[0].b = best_endpoints_table[target_color.b][0]; 372 | one_thirds[1].b = best_endpoints_table[target_color.b][1]; 373 | color_int0 = ExtendToRgbInt(one_thirds[0]); 374 | color_int1 = ExtendToRgbInt(one_thirds[1]); 375 | color_int = CombineRgbIntFast<2, 1>(color_int0, color_int1); 376 | error = ComputeDifferenceLuminanceFast(target_color, color_int); 377 | if (error < min_error) { 378 | min_error = error; 379 | // For thirds mode in DXT, color0 must be greater than color1. 380 | if (ToUInt16(one_thirds[0]) > ToUInt16(one_thirds[1])) { 381 | which_bits = 2; 382 | *color0 = one_thirds[0]; 383 | *color1 = one_thirds[1]; 384 | } else { 385 | which_bits = 3; 386 | *color0 = one_thirds[1]; 387 | *color1 = one_thirds[0]; 388 | } 389 | } 390 | 391 | return which_bits; 392 | } 393 | 394 | } // namespace image_codec_compression 395 | -------------------------------------------------------------------------------- /image_compression/internal/dxtc_const_color_table.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef IMAGE_COMPRESSION_INTERNAL_DXTC_CONST_COLOR_TABLE_H_ 16 | #define IMAGE_COMPRESSION_INTERNAL_DXTC_CONST_COLOR_TABLE_H_ 17 | 18 | #include "image_compression/internal/color_util.h" 19 | 20 | namespace image_codec_compression { 21 | 22 | // Sets color0 and color1 to the best end point colors to use to represent the 23 | // constant block color, target_color. Returns the 2-bit value to use to encode 24 | // the pixels in the block. 25 | // If always_4_color_case is false, will also use 1/2-way interpolation rules 26 | // to reduce errors further. 27 | int GetBestDxtcConstColors(const RgbInt& target_color, 28 | Rgb565* color0, Rgb565* color1, 29 | bool always_4_color_case); 30 | 31 | } // namespace image_codec_compression 32 | 33 | #endif // IMAGE_COMPRESSION_INTERNAL_DXTC_CONST_COLOR_TABLE_H_ 34 | -------------------------------------------------------------------------------- /image_compression/internal/dxtc_to_etc_transcoder.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "image_compression/internal/color_util.h" 16 | #include "image_compression/internal/color_types.h" 17 | #include "image_compression/internal/pixel4x4.h" 18 | #include "image_compression/public/etc_compressor.h" 19 | 20 | namespace image_codec_compression { 21 | 22 | // Forward declarations of objects only seen in .cc files. 23 | struct Dxt1Block; 24 | void DecodeDxt1Block(const Dxt1Block &dxt1_block, bool swap_red_and_blue, 25 | Rgb888 decoded_pixels[4][4]); 26 | uint64 EncodeEtc1Block(const Pixel4x4 &pixel4x4, 27 | EtcCompressor::CompressionStrategy strategy); 28 | 29 | void TranscodeDxt1ToEtc1(CompressedImage* image) { 30 | uint8* imgbytes = image->GetMutableData(); 31 | Rgb888 rgb4x4[4][4]; 32 | for (size_t i = 0, n = image->GetDataSize(); i < n; i += 8) { 33 | Dxt1Block* dxt1_block = reinterpret_cast(imgbytes); 34 | DecodeDxt1Block(*dxt1_block, false, rgb4x4); 35 | Pixel4x4 pixel4x4(&rgb4x4[0][0], 4, 4, 0, 0, 0); 36 | *(reinterpret_cast(imgbytes)) = 37 | EncodeEtc1Block(pixel4x4, EtcCompressor::kHeuristic); 38 | imgbytes += 8; 39 | } 40 | } 41 | 42 | } // namespace image_codec_compression 43 | -------------------------------------------------------------------------------- /image_compression/internal/pixel4x4.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "image_compression/internal/pixel4x4.h" 16 | 17 | #include 18 | 19 | #include "base/integral_types.h" 20 | 21 | namespace image_codec_compression { 22 | 23 | template 24 | void Pixel4x4::ConstructOutsideImage(const ColorType *pixels, 25 | uint32 image_height, uint32 image_width, 26 | uint32 bytes_per_row, 27 | int row, int column) { 28 | // 29 | // There are 4 potential rectangular pixel regions to fill in. 30 | // Region 1: Within the image. 31 | // Region 2: To the right of the image; filled in with replicated 32 | // rightmost column of pixels. 33 | // Region 3: Below the image; filled in with replicated last row 34 | // of pixels. 35 | // Region 4: Below and to right of the image; filled in with 36 | // replicated lower-rightmost corner pixel. 37 | // 38 | int height = image_height; 39 | int width = image_width; 40 | int rows_within_image = std::max(0, height - row); 41 | int columns_within_image = std::max(0, width - column); 42 | int x_max = std::min(4, columns_within_image); 43 | 44 | for (int y = 0; y < 4; ++y) { 45 | // Get the row of pixels to copy from. 46 | const ColorType *row_pixels = 47 | GetImagePixel(pixels, bytes_per_row, std::min(row + y, height - 1), 0); 48 | // Regions 1 and 3: 49 | for (int x = 0; x < x_max; ++x) 50 | SetPixel(y, x, row_pixels[column + x]); 51 | // Regions 2 and 4: 52 | for (int x = columns_within_image; x < 4; ++x) 53 | SetPixel(y, x, row_pixels[std::min(column + x, width - 1)]); 54 | } 55 | 56 | // Set this for special case of single-color 4x4 block, which helps 57 | // optimize compression. 58 | has_one_pixel_ = (columns_within_image == 0 && rows_within_image == 0); 59 | } 60 | 61 | // Instantiate the function for supported pixel types. 62 | template void Pixel4x4::ConstructOutsideImage( 63 | const Rgb888 *pixels, uint32 image_height, uint32 image_width, 64 | uint32 bytes_per_row, int row, int column); 65 | template void Pixel4x4::ConstructOutsideImage( 66 | const Rgba8888 *pixels, uint32 image_height, uint32 image_width, 67 | uint32 bytes_per_row, int row, int column); 68 | 69 | } // namespace image_codec_compression 70 | -------------------------------------------------------------------------------- /image_compression/internal/pixel4x4.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef IMAGE_COMPRESSION_INTERNAL_PIXEL4X4_H_ 16 | #define IMAGE_COMPRESSION_INTERNAL_PIXEL4X4_H_ 17 | 18 | #include "base/integral_types.h" 19 | #include "base/logging.h" 20 | #include "image_compression/internal/color_util.h" 21 | 22 | namespace image_codec_compression { 23 | 24 | // 25 | // The Pixel4x4 class is used to simplify access to a 4x4 block of 26 | // pixels in an RGB or RGBA source image. The pixels in the block are 27 | // first converted to RgbInts to provide a consistent and simple 28 | // interface. 29 | // 30 | class Pixel4x4 { 31 | public: 32 | // The default constructor is used when clients will set pixel 33 | // values in a block directly. 34 | Pixel4x4() 35 | : has_one_pixel_(false) { 36 | } 37 | 38 | // Constructs a Pixel4x4 from Rgb888 or Rgba8888 image data. A 39 | // pointer to the image data is given along with the image size and 40 | // stride. The row and column parameters indicate the top-left 41 | // corner of the block, with rows increasing from top to bottom and 42 | // columns increasing from left to right. The Rgb888 version leaves 43 | // the alpha_ member undefined. 44 | template 45 | inline Pixel4x4(const ColorType *pixels, uint32 height, uint32 width, 46 | uint32 padding_bytes_per_row, int row, int column) { 47 | const uint32 bytes_per_row = width * sizeof(pixels[0]) + 48 | padding_bytes_per_row; 49 | 50 | // In the common case where the 4x4 lies completely within the 51 | // image, do all the construction work here. 52 | if (static_cast(height) - row >= 4 && 53 | static_cast(width) - column >= 4) { 54 | for (int y = 0; y < 4; ++y) { 55 | const ColorType *row_pixels = GetImagePixel(pixels, bytes_per_row, 56 | row + y, column); 57 | for (int x = 0; x < 4; ++x) { 58 | pixels_[y][x] = ToRgbInt(row_pixels[x]); 59 | SetAlpha(y, x, row_pixels[x]); 60 | } 61 | } 62 | has_one_pixel_ = false; 63 | } else { 64 | ConstructOutsideImage(pixels, height, width, bytes_per_row, 65 | row, column); 66 | } 67 | } 68 | 69 | // Stores a pixel color at the given row/column location. 70 | template 71 | inline void SetPixel(int row, int column, const ColorType &color) { 72 | pixels_[row][column] = ToRgbInt(color); 73 | SetAlpha(row, column, color); 74 | } 75 | 76 | // Stores an alpha value. The default implementation does nothing. 77 | // A template specialization below is used to store alpha if the 78 | // block contains RGBA pixels. 79 | template 80 | inline void SetAlpha(int row, int column, const ColorType &color) { 81 | } 82 | 83 | // Returns true if all pixels in the block are known to have the 84 | // same color value. This is a special case that is used to optimize 85 | // when compressing into an image with larger dimensions than the 86 | // original. 87 | inline bool has_one_pixel() const { 88 | return has_one_pixel_; 89 | } 90 | 91 | // Returns the color of a pixel. 92 | inline const RgbInt& GetPixel(int row, int column) const { 93 | DCHECK_GE(row, 0); 94 | DCHECK_GE(column, 0); 95 | DCHECK_LT(row, 4); 96 | DCHECK_LT(column, 4); 97 | return pixels_[row][column]; 98 | } 99 | 100 | // Accesses an alpha value. Values range from 0 to 255 for RGBA 101 | // images, and are likely to be undefined for RGB images. 102 | inline int GetAlpha(int row, int column) const { 103 | DCHECK_GE(row, 0); 104 | DCHECK_GE(column, 0); 105 | DCHECK_LT(row, 4); 106 | DCHECK_LT(column, 4); 107 | return alpha_[row][column]; 108 | } 109 | 110 | private: 111 | // Returns a pointer to the pixel at a given row and column in an 112 | // image whose first pixel and row size are provided. This is 113 | // templated to work with RGB and RGBA images. 114 | template 115 | static inline const ColorType* GetImagePixel(const ColorType *pixels, 116 | int bytes_per_row, 117 | int row, int column) { 118 | const uint8 *row_start = 119 | reinterpret_cast(pixels) + (row * bytes_per_row); 120 | return reinterpret_cast(row_start) + column; 121 | } 122 | 123 | // Does the work of the constructor when the 4x4 region is not 124 | // totally contained within the image, replicating pixels where 125 | // necessary. 126 | template 127 | void ConstructOutsideImage(const ColorType *pixels, 128 | uint32 image_height, uint32 image_width, 129 | uint32 bytes_per_row, int row, int column); 130 | 131 | // Pixels as colors with integer components. 132 | RgbInt pixels_[4][4]; 133 | 134 | // Alpha values of all pixels (for RGBA only). These are stored 135 | // separately because they are accessed separately. 136 | int alpha_[4][4]; 137 | 138 | // True if the block is known to consist of a single pixel value, 139 | // which makes it trivial (and fast) to encode. 140 | bool has_one_pixel_; 141 | }; 142 | 143 | // Specialization of SetAlpha() for Rgba8888. 144 | template <> 145 | inline void Pixel4x4::SetAlpha(int row, int column, const Rgba8888 &color) { 146 | alpha_[row][column] = color.a; 147 | } 148 | 149 | // Given a 4x4 block of pixels, downsamples them to a 2x2 and stores 150 | // the results in one 2x2 corner (whose upper left pixel is at 151 | // target_row, target_col) of the given Pixel4x4. 152 | template 153 | inline void StoreDownsampledPixels4x4(const ColorType pixels[4][4], 154 | int target_row, int target_col, 155 | Pixel4x4 *pixel4x4) { 156 | for (int row = 0; row < 2; ++row) { 157 | for (int col = 0; col < 2; ++col) { 158 | pixel4x4->SetPixel(target_row + row, target_col + col, 159 | ComputeAveragePixel2x2(pixels, 2 * row, 2 * col)); 160 | } 161 | } 162 | } 163 | 164 | } // namespace image_codec_compression 165 | 166 | #endif // IMAGE_COMPRESSION_INTERNAL_PIXEL4X4_H_ 167 | -------------------------------------------------------------------------------- /image_compression/internal/pvrtc_compressor.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "image_compression/public/pvrtc_compressor.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "base/integral_types.h" 22 | #include "base/logging.h" 23 | #include "image_compression/internal/bit_util.h" 24 | #include "image_compression/internal/color_util.h" 25 | #include "image_compression/public/compressed_image.h" 26 | 27 | namespace image_codec_compression { 28 | 29 | // Four methods of encoding modulation bits for a block of pixels used by this 30 | // compressor. 31 | // See section 6. in "Texture Compression using Low-Frequency Signal 32 | // Modulation" http://dl.acm.org/citation.cfm?id=844187 . 33 | // Note that the PVRTC format supports a further modulation mode for 34 | // punch-through alpha, which is not used here. 35 | enum ModulationMode { 36 | // 1 bit per pixel for each pixel. 37 | kModulationMode1BPP, 38 | // The following three modes have 2 bits per pixel for every other pixel in 39 | // the block, in a checkerboard pattern. The mode specifies how to infer color 40 | // for the intervening pixels. 41 | kModulationModeAverage4, // Average the 4 orthoganally connected neighbors. 42 | kModulationModeVertical, // Average the 2 vertical neighbors. 43 | kModulationModeHorizontal // Average the 2 horizontal neighbors. 44 | }; 45 | 46 | // Block width and height as for 2BPP PVRTC. 47 | static const uint32 kLog2BlockWidth = 3; 48 | static const uint32 kLog2BlockHeight = 2; 49 | static const uint32 kBlockWidth = (1 << kLog2BlockWidth); 50 | static const uint32 kBlockHeight = (1 << kLog2BlockHeight); 51 | 52 | //----------------------------------------------------------------------------- 53 | 54 | // 55 | // General helper functions. 56 | // 57 | 58 | // Little-endian write. 59 | static unsigned char *Append32(uint32 value, uint8 *output) { 60 | *output++ = value >> 0; 61 | *output++ = value >> 8; 62 | *output++ = value >> 16; 63 | *output++ = value >> 24; 64 | return output; 65 | } 66 | 67 | // Returns true if |x| is a power of two. 68 | static bool IsPowerOfTwo(uint32 x) { 69 | return (x != 0) && !(x & (x - 1)); 70 | } 71 | 72 | // A quick measure of how different are two colors for the human eye. 73 | // The bigger the return value, the more different. 74 | static uint32 ColorDiff(Rgba8888 color0, Rgba8888 color1) { 75 | return std::abs(color0.r - color1.r) + std::abs(color0.g - color1.g) 76 | + std::abs(color0.b - color1.b) + std::abs(color0.a - color1.a); 77 | } 78 | 79 | // Calculates *|x| and *|y| for the Z-order curve value |z|. 80 | static void FromZOrder(uint32 z, uint32 *x, uint32 *y) { 81 | *x = *y = 0; 82 | for (size_t j = 0; j < 16; j++) { 83 | *x |= ((z >> (j * 2 + 1)) & 1) << j; 84 | *y |= ((z >> (j * 2 + 0)) & 1) << j; 85 | } 86 | } 87 | 88 | // Returns the result of encoding the 8 bits of input down as |bit_depth| bits 89 | // and then decoding back up to 8 bits. 90 | // Encoding will simply preserve only the top |bit_depth| bits. 91 | // Decoding will bitwise-or these bits repeatedly into the result, so that the 92 | // output values range as smoothly as possible from 0 to 255. 93 | static inline uint8 ApplyBitDepthReduction(uint8 input, uint32 bit_depth) { 94 | uint8 encoding_mask = GetMask(bit_depth) << (8 - bit_depth); 95 | uint8 encoded_bits = input & encoding_mask; 96 | 97 | DCHECK_GE(bit_depth, 3U); // Not yet implemented for a bit_depth of 1 or 2. 98 | 99 | uint8 result = encoded_bits | (encoded_bits >> bit_depth); 100 | if (bit_depth <= 3) { 101 | // The encoded bits will have to be repeated again for the least significant 102 | // output bits. 103 | result |= encoded_bits >> (bit_depth * 2); 104 | } 105 | return result; 106 | } 107 | 108 | //----------------------------------------------------------------------------- 109 | 110 | // 111 | // PVRTC-specific helper functions operating on pixels and blocks of pixels. 112 | // 113 | 114 | // Returns the color interpolated between |color0| and |color1| as specified by 115 | // |mod| which can range from 0 to 3: 116 | // 0 = color0 117 | // 1 = 5/8ths color0, 3/8ths color1 118 | // 1 = 3/8ths color0, 5/8ths color1 119 | // 3 = color1 120 | static Rgba8888 ApplyModulation(Rgba8888 color0, Rgba8888 color1, uint32 mod) { 121 | Rgba8888 result(color0); 122 | DCHECK_GE(4U, mod); 123 | switch (mod) { 124 | case 0: 125 | // Do nothing; keep result = color0. 126 | break; 127 | case 1: 128 | result.r = (5 * color0.r + 3 * color1.r) / 8; 129 | result.g = (5 * color0.g + 3 * color1.g) / 8; 130 | result.b = (5 * color0.b + 3 * color1.b) / 8; 131 | result.a = (5 * color0.a + 3 * color1.a) / 8; 132 | break; 133 | case 2: 134 | result.r = (3 * color0.r + 5 * color1.r) / 8; 135 | result.g = (3 * color0.g + 5 * color1.g) / 8; 136 | result.b = (3 * color0.b + 5 * color1.b) / 8; 137 | result.a = (3 * color0.a + 5 * color1.a) / 8; 138 | break; 139 | case 3: 140 | result = color1; 141 | break; 142 | } 143 | return result; 144 | } 145 | 146 | // Returns which modulation (from 0 through 3) best represents |color| given 147 | // the color palette |color0| and |color1|. 148 | static uint32 BestModulation(Rgba8888 color, Rgba8888 color0, Rgba8888 color1) { 149 | uint32 diff = ColorDiff(color, color0); 150 | uint32 best_diff = diff; 151 | uint32 best_mod = 0; 152 | 153 | for (unsigned int current_mod = 1; current_mod < 4; ++current_mod) { 154 | Rgba8888 current_color = ApplyModulation(color0, color1, current_mod); 155 | diff = ColorDiff(color, current_color); 156 | if (diff < best_diff) { 157 | best_diff = diff; 158 | best_mod = current_mod; 159 | } else { 160 | // If it's not getting better here, it won't get better later. 161 | return best_mod; 162 | } 163 | } 164 | 165 | return best_mod; 166 | } 167 | 168 | // Returns a color bilinearly interpolated between the four input colors. 169 | // |px| ranges from 0 (pure |color00| or |color01|) to 170 | // kBlockWidth (pure |color10| or color11|). 171 | // |py| ranges from 0 (pure |color00| or |color10|) to 172 | // kBlockHeight (pure |color01| or |color11|). 173 | static Rgba8888 Interpolate4_2BPP(Rgba8888 color00, Rgba8888 color01, 174 | Rgba8888 color10, Rgba8888 color11, 175 | uint32 px, uint32 py) { 176 | // Calculate the weights that should be applied to the four input colors. 177 | const uint32 a = (kBlockHeight - py) * (kBlockWidth - px); 178 | const uint32 b = (kBlockHeight - py) * px; 179 | const uint32 c = py * (kBlockWidth - px); 180 | const uint32 d = py * px; 181 | // Apply these weights. 182 | const uint32 downscale = kBlockWidth * kBlockHeight; 183 | return Rgba8888( 184 | (a * color00.r + b * color01.r + c * color10.r + d * color11.r) / 185 | downscale, 186 | (a * color00.g + b * color01.g + c * color10.g + d * color11.g) / 187 | downscale, 188 | (a * color00.b + b * color01.b + c * color10.b + d * color11.b) / 189 | downscale, 190 | (a * color00.a + b * color01.a + c * color10.a + d * color11.a) / 191 | downscale); 192 | } 193 | 194 | // Returns the color for a pixel in a bilinearly upscaled version of an input 195 | // image. The input image is upscaled kBlockWidth in width and kBlockHeight in 196 | // height. The bilinear interpolation wraps on all four edges of the image. 197 | // For every block of pixels of size (kBlockWidth * kBlockHeight) in the 198 | // upscaled image, where the top left is (0,0), the pixel at position 199 | // (kBlockWidth / 2, kBlockHeight / 2) will use the uninterpolated 200 | // low-frequency image colors, and the rest will be interpolated. 201 | // |source| the raw pixel data for the input image. 202 | // |width| width of the upscaled image. 203 | // |height| height of the upscaled image. 204 | // |x| and |y| the position of the pixel in the upscaled image. 205 | // According to: 206 | // https://www.khronos.org/registry/gles/extensions/IMG/IMG_texture_compression_pvrtc.txt 207 | // width and height must be power-of-two. 208 | static Rgba8888 GetInterpolatedColor2BPP(const Rgba8888 *source, 209 | unsigned int width, 210 | unsigned int height, 211 | unsigned x, unsigned y) { 212 | // The left, top, right and bottom edges of the 2x2 pixel block in the source 213 | // image that will be used to interpolate. Note that through wrapping (for 214 | // example) source_left may be to the right of source_right. 215 | // width and height are power-of-two, so we can use '&' instead of '%'. 216 | const uint32 source_left = 217 | ((x - kBlockWidth / 2) & (width - 1)) >> kLog2BlockWidth; 218 | const uint32 source_top = 219 | ((y - kBlockHeight / 2) & (height - 1)) >> kLog2BlockHeight; 220 | const uint32 source_right = 221 | (source_left + 1) & ((width >> kLog2BlockWidth) - 1); 222 | const uint32 source_bottom = 223 | (source_top + 1) & ((height >> kLog2BlockHeight) - 1); 224 | 225 | // The bilinear weights to be used for interpolation. 226 | const uint32 x_weight = (x + kBlockWidth / 2) & (kBlockWidth - 1); 227 | const uint32 y_weight = (y + kBlockHeight / 2) & (kBlockHeight - 1); 228 | 229 | const uint32 source_width = width / kBlockWidth; 230 | const Rgba8888 color00 = source[source_top * source_width + source_left]; 231 | const Rgba8888 color01 = source[source_top * source_width + source_right]; 232 | const Rgba8888 color10 = source[source_bottom * source_width + source_left]; 233 | const Rgba8888 color11 = source[source_bottom * source_width + source_right]; 234 | 235 | return Interpolate4_2BPP(color00, color01, color10, color11, 236 | x_weight, y_weight); 237 | } 238 | 239 | // An ordering for colors roughly based on brightness. 240 | static uint32 ColorBrightnessOrder(Rgba8888 color) { 241 | return static_cast(color.r) + static_cast(color.g) + 242 | static_cast(color.b) + static_cast(color.a); 243 | } 244 | 245 | // Gets two colors that represent extremes of the range of colors within a block 246 | // in a source image. A fast alternative to principal component analysis. 247 | // This function also takes care of the wrapping of the coordinates, i.e. |x0| 248 | // and |y0| can be outside the bounds of the image. 249 | // |image| the source image pixel data. 250 | // |width| source image width (must be a power of two). 251 | // |height| source image height (must be a power of two). 252 | // |x0| left edge of the block to be considered in pixels. 253 | // |y0| top edge of the block to be considered in pixels. 254 | // |out_index_0|, |out_index_1| output colors as indices into |image|. 255 | static void GetExtremesFast(const Rgba8888 *image, uint32 width, uint32 height, 256 | uint32 x0, uint32 y0, 257 | uint32 *out_index_0, uint32 *out_index_1) { 258 | // Consider 5 different pairs; lightness, then R, G, B, A axes. 259 | #define PAIRS 5 260 | uint32 best_fitness[PAIRS][2]; 261 | uint32 best_index[PAIRS][2]; 262 | for (uint32 i = 0; i < PAIRS; i++) { 263 | // For each pair of colors, the first must have the lowest possible value 264 | // for the tested fitness, the second the highest possible; hence 265 | // initialize "best" with extreme high and low values. 266 | best_fitness[i][0] = static_cast(-1); 267 | best_fitness[i][1] = 0; 268 | best_index[i][0] = 0; 269 | best_index[i][1] = 0; 270 | } 271 | 272 | for (uint32 y = y0; y < y0 + kBlockHeight; y++) { 273 | for (uint32 x = x0; x < x0 + kBlockWidth; x++) { 274 | uint32 x_wrapped = (x + width) & (width - 1); 275 | uint32 y_wrapped = (y + height) & (height - 1); 276 | uint32 index = y_wrapped * width + x_wrapped; 277 | Rgba8888 color = image[index]; 278 | 279 | // For the first pair, use the lightness. 280 | uint32 lightness = (77 * color.r + 150 * color.g + 28 * color.b) / 256; 281 | if (lightness < best_fitness[0][0]) { 282 | best_fitness[0][0] = lightness; 283 | best_index[0][0] = index; 284 | } 285 | if (lightness > best_fitness[0][1]) { 286 | best_fitness[0][1] = lightness; 287 | best_index[0][1] = index; 288 | } 289 | 290 | // For the next 4 axes, use the R, G, B or A axis. 291 | for (int component = 0; component < 4; component++) { 292 | int output_pair = component + 1; 293 | const uint8 c = color[component]; 294 | if (c < best_fitness[output_pair][0]) { 295 | best_fitness[output_pair][0] = c; 296 | best_index[output_pair][0] = index; 297 | } 298 | if (c > best_fitness[output_pair][1]) { 299 | best_fitness[output_pair][1] = c; 300 | best_index[output_pair][1] = index; 301 | } 302 | } 303 | } 304 | } 305 | 306 | // Choose the pair for which the color difference is biggest. This makes the 307 | // algorithm somewhat principal component-ish. 308 | uint32 best_pair_diff = 0; 309 | uint32 best_pair = 0; 310 | for (uint32 i = 0; i < PAIRS; i++) { 311 | uint32 diff = ColorDiff(image[best_index[i][0]], image[best_index[i][1]]); 312 | if (diff > best_pair_diff) { 313 | best_pair = i; 314 | best_pair_diff = diff; 315 | } 316 | } 317 | 318 | *out_index_0 = best_index[best_pair][0]; 319 | *out_index_1 = best_index[best_pair][1]; 320 | 321 | // *out_index_0 should be darker than *out_index_1 for consistency; swap if 322 | // not. 323 | if (ColorBrightnessOrder(image[*out_index_1]) < 324 | ColorBrightnessOrder(image[*out_index_0])) { 325 | uint32 temp = *out_index_0; 326 | *out_index_0 = *out_index_1; 327 | *out_index_1 = temp; 328 | } 329 | } 330 | 331 | // Returns the color that the input color will become after encoding as an "A" 332 | // or "B" color in a PVRTC compressed image (where they are converted to 333 | // 16-bit), and then decoding back to 32-bit. 334 | // This helps the compressor choose correct modulation values once the "A" and 335 | // "B" colors are chosen. 336 | // |is_b| is true if this is the "B" color; "A" and "B" are encoded differently. 337 | static Rgba8888 ApplyColorChannelReduction(Rgba8888 color, bool is_b) { 338 | if (color.a == 255) { 339 | color.r = ApplyBitDepthReduction(color.r, 5); 340 | color.g = ApplyBitDepthReduction(color.g, 5); 341 | color.b = ApplyBitDepthReduction(color.b, is_b ? 5 : 4); 342 | } else { 343 | color.r = ApplyBitDepthReduction(color.r, 4); 344 | color.g = ApplyBitDepthReduction(color.g, 4); 345 | color.b = ApplyBitDepthReduction(color.b, is_b ? 4 : 3); 346 | color.a = ApplyBitDepthReduction(color.a, 3); 347 | } 348 | return color; 349 | } 350 | 351 | // Encode two colors and a modulation mode into an unsigned int. 352 | // The encoding is as follows, in the direction from MSB to LSB: 353 | // 16 bit |colora|, 15 bit |colorb|, 1 bit |mod_mode|. 354 | // Opaque colors are: 1 bit 1, 5 bit R, 5 bit G, 4/5 bit B. 355 | // Translucent colors are: 1 bit 0, 3 bit A, 4 bit R, 4 bit G, 3/4 bit B. 356 | static unsigned EncodeColors(Rgba8888 colora, Rgba8888 colorb, 357 | ModulationMode mode) { 358 | unsigned value = 0; 359 | 360 | if (colora.a == 255) { 361 | SetBits(15, 1, 1, &value); 362 | SetBits(1, 4, colora.b >> 4, &value); 363 | SetBits(5, 5, colora.g >> 3, &value); 364 | SetBits(10, 5, colora.r >> 3, &value); 365 | } else { 366 | SetBits(15, 1, 0, &value); 367 | SetBits(1, 3, colora.b >> 5, &value); 368 | SetBits(4, 4, colora.g >> 4, &value); 369 | SetBits(8, 4, colora.r >> 4, &value); 370 | SetBits(12, 3, colora.a >> 5, &value); 371 | } 372 | 373 | if (colorb.a == 255) { 374 | SetBits(31, 1, 1, &value); 375 | SetBits(16, 5, colorb.b >> 3, &value); 376 | SetBits(21, 5, colorb.g >> 3, &value); 377 | SetBits(26, 5, colorb.r >> 3, &value); 378 | } else { 379 | SetBits(31, 1, 0, &value); 380 | SetBits(16, 4, colorb.b >> 4, &value); 381 | SetBits(20, 4, colorb.g >> 4, &value); 382 | SetBits(24, 4, colorb.r >> 4, &value); 383 | SetBits(28, 3, colorb.a >> 5, &value); 384 | } 385 | 386 | SetBits(0, 1, mode == kModulationMode1BPP ? 0 : 1, &value); 387 | return value; 388 | } 389 | 390 | // Works out which modulation mode to use for a given block in an image. 391 | // |image_mod| the modulation information for the image. 392 | // |width| and |height| image_mod pixel dimensions (must be a power of two). 393 | // |block_x| block x coordinate, i.e. ranging from 0 to |width| / kBlockWidth. 394 | // |block_y| block y coordinate, i.e. ranging from 0 to |height| / kBlockHeight. 395 | static ModulationMode CalculateBlockModulationMode( 396 | const uint8 *image_mod, uint32 width, uint32 height, 397 | uint32 block_x, uint32 block_y) { 398 | // A count of how many pixels are best served by modulation values 2 or 3, 399 | // i.e. intermediate between the extremes of one color or the other. 400 | uint32 intermediate_value_count = 0; 401 | 402 | // A measure of how much variation between pixels there is horizontally. 403 | uint32 horizontal_count = 0; 404 | 405 | // A measure of how much variation between pixels there is vertically. 406 | uint32 vertical_count = 0; 407 | 408 | for (uint32 y = 0; y < kBlockHeight; y++) { 409 | for (uint32 x = 0; x < kBlockWidth; x++) { 410 | uint32 index = 411 | (block_y * kBlockHeight + y) * width + (block_x * kBlockWidth + x); 412 | 413 | if (image_mod[index] == 1 || image_mod[index] == 2) 414 | intermediate_value_count++; 415 | 416 | // Index of adjacent horizontal pixel in |image_mod|. 417 | uint32 index_adjacent_horizontal = 418 | (block_y * kBlockHeight + y) * width + 419 | ((block_x * kBlockWidth + x + 1) & (width - 1)); 420 | 421 | // Index of adjacent vertical pixel in |image_mod|. 422 | uint32 index_adjacent_vertical = 423 | ((block_y * kBlockHeight + y + 1) & (height - 1)) * width + 424 | (block_x * kBlockWidth + x); 425 | 426 | horizontal_count += 427 | std::abs(image_mod[index] - image_mod[index_adjacent_vertical]); 428 | vertical_count += 429 | std::abs(image_mod[index] - image_mod[index_adjacent_horizontal]); 430 | } 431 | } 432 | 433 | if (intermediate_value_count <= 4) 434 | return kModulationMode1BPP; 435 | 436 | static const uint32 absolute_threshold = 10; 437 | static const uint32 ratio_threshold = 2; 438 | 439 | if (vertical_count > absolute_threshold && 440 | vertical_count > horizontal_count * ratio_threshold) 441 | return kModulationModeVertical; 442 | else if (horizontal_count > absolute_threshold && 443 | horizontal_count > vertical_count * ratio_threshold) 444 | return kModulationModeHorizontal; 445 | 446 | return kModulationModeAverage4; 447 | } 448 | 449 | // Calculates the 32 bits of modulation information to store for a given block 450 | // in an image. 451 | // |image_mod| the modulation information for the image. 452 | // |width| and |height| image_mod pixel dimensions. 453 | // |block_x| block x coordinate, i.e. ranging from 0 to |width| / kBlockWidth. 454 | // |block_y| block y coordinate, i.e. ranging from 0 to |height| / kBlockHeight. 455 | // |mode| which modulation mode to use. 456 | uint32 CalculateBlockModulationData(const uint8 *image_mod, 457 | uint32 width, uint32 height, 458 | uint32 block_x, uint32 block_y, 459 | ModulationMode mode) { 460 | uint32 result = 0; 461 | uint32 bitpos = 0; 462 | for (unsigned y = 0; y < 4; y++) { 463 | for (unsigned x = 0; x < 8; x++) { 464 | size_t index = (block_y * 4 + y) * width + (block_x * 8 + x); 465 | 466 | if (mode == kModulationMode1BPP) { 467 | uint32 bit = image_mod[index] / 2; 468 | SetBits(bitpos, 1, bit, &result); 469 | bitpos++; 470 | } else { 471 | if ((x ^ y) & 1) continue; // checkerboard 472 | uint32 bit = image_mod[index]; 473 | // The bits at position 0 (0,0) and at position 20 (4,2) are the ones 474 | // that use only a single bit for the modulation value, and the other 475 | // bit for selecting the sub-mode. 476 | if (bitpos == 0) { 477 | // The saved bit chooses average-of-4 or "other". 478 | if (mode == kModulationModeAverage4) 479 | bit &= 2; 480 | else 481 | bit |= 1; 482 | } else if (bitpos == 20) { 483 | // The saved bit chooses vertical versus horizontal. 484 | if (mode == kModulationModeVertical) 485 | bit |= 1; 486 | else 487 | bit &= 2; 488 | } 489 | 490 | SetBits(bitpos, 2, bit, &result); 491 | bitpos += 2; 492 | } 493 | } 494 | } 495 | return result; 496 | } 497 | 498 | //----------------------------------------------------------------------------- 499 | 500 | // 501 | // Helper functions operating on entire images. 502 | // 503 | 504 | // Fills in the two low-resolution images representing the "a" and "b" colors in 505 | // the source |image|. 506 | static void Morph(const Rgba8888 *image, uint32 width, uint32 height, 507 | Rgba8888 *outa, Rgba8888 *outb) { 508 | for (uint32 y = 0; y < height; y += kBlockHeight) { 509 | for (uint32 x = 0; x < width; x += kBlockWidth) { 510 | uint32 indexa = 0; 511 | uint32 indexb = 0; 512 | GetExtremesFast(&image[0], width, height, x, y, &indexa, &indexb); 513 | 514 | uint32 index_out = 515 | (y / kBlockHeight) * (width / kBlockWidth) + (x / kBlockWidth); 516 | 517 | outa[index_out] = ApplyColorChannelReduction(image[indexa], false); 518 | outb[index_out] = ApplyColorChannelReduction(image[indexb], true); 519 | } 520 | } 521 | } 522 | 523 | // Given a source |image| and two low-resolution "a" and "b" images, creates a 524 | // 2-bits-per-pixel "mod" image, i.e. values between 0 and 3 for each pixel in 525 | // |image|. Each output pixel is stored in a byte in |mod|, which is assumed 526 | // to be pre-allocated. 527 | static void Modulate(const Rgba8888 *image, uint32 width, uint32 height, 528 | const Rgba8888 *imagea, 529 | const Rgba8888 *imageb, 530 | unsigned char *mod) { 531 | for (uint32 y = 0; y < height; y++) { 532 | for (uint32 x = 0; x < width; x++) { 533 | Rgba8888 colora = 534 | GetInterpolatedColor2BPP(&imagea[0], width, height, x, y); 535 | Rgba8888 colorb = 536 | GetInterpolatedColor2BPP(&imageb[0], width, height, x, y); 537 | *mod++ = BestModulation(*image++, colora, colorb); 538 | } 539 | } 540 | } 541 | 542 | // Takes the calculated "A" and "B" images, and the modulation information, and 543 | // writes out the data in PVRTC format. Though the input modulation information 544 | // has 2 bits per pixel of the uncompressed original image, this function will 545 | // choose on a per-block basis which of the 4 modulation modes to use. 546 | // |width| and |height| image dimensions. 547 | // |imagea| the "A" image as described in pvrtc_compressor.h. 548 | // |imageb| the "B" image as also described. 549 | // |imagemod| One byte per pixel modulation data, containing 2-bit values. 550 | // |pvr| output pvrtc data, assumed preallocated. 551 | static void Encode(uint32 width, uint32 height, 552 | const Rgba8888 *imagea, const Rgba8888 *imageb, 553 | const uint8 *image_mod, uint8 *pvr) { 554 | // Loop through all output blocks. 555 | for (uint32 i = 0; i < width * height / (kBlockWidth * kBlockHeight); i++) { 556 | // The blocks are stored in Z-order; calculate the block x and y. 557 | uint32 block_x = 0; 558 | uint32 block_y = 0; 559 | FromZOrder(i, &block_x, &block_y); 560 | 561 | // Calculate which kind of encoding is worth doing for this block. 562 | ModulationMode mode = CalculateBlockModulationMode(image_mod, width, height, 563 | block_x, block_y); 564 | 565 | // Given this mode, calculate the 32 bits that represent the block's 566 | // modulation information. 567 | uint32 mod_data = CalculateBlockModulationData(image_mod, width, height, 568 | block_x, block_y, mode); 569 | 570 | // The 32 bits that represent the 2-color palette for this block and mode. 571 | uint32 color_data = EncodeColors( 572 | imagea[block_y * (width / kBlockWidth) + block_x], 573 | imageb[block_y * (width / kBlockWidth) + block_x], 574 | mode); 575 | 576 | // Write out this information. 577 | pvr = Append32(mod_data, pvr); 578 | pvr = Append32(color_data, pvr); 579 | } 580 | } 581 | 582 | // Compresses a given RGBA8888 image to 2BPP PVRTC RGBA. 583 | // |image| source image data. 584 | // |width| and |height| image dimensions. 585 | // |pvr| output pvrtc data, assumed preallocated. 586 | static void CompressPVRTC_RGBA_2BPP(const Rgba8888 *image, 587 | uint32 width, uint32 height, 588 | uint8 *pvr) { 589 | uint32 low_image_size = (width * height) / (kBlockWidth * kBlockHeight); 590 | std::vector imagea(low_image_size); 591 | std::vector imageb(low_image_size); 592 | std::vector imagemod(width * height); 593 | 594 | Morph(image, width, height, &imagea[0], &imageb[0]); 595 | Modulate(image, width, height, &imagea[0], &imageb[0], &imagemod[0]); 596 | Encode(width, height, &imagea[0], &imageb[0], &imagemod[0], pvr); 597 | } 598 | 599 | //----------------------------------------------------------------------------- 600 | 601 | // 602 | // Public functions. 603 | // 604 | 605 | PvrtcCompressor::PvrtcCompressor() { 606 | } 607 | 608 | PvrtcCompressor::~PvrtcCompressor() { 609 | } 610 | 611 | bool PvrtcCompressor::SupportsFormat(CompressedImage::Format format) const { 612 | return format == CompressedImage::kRGBA; 613 | } 614 | 615 | bool PvrtcCompressor::IsValidCompressedImage(const CompressedImage &image) { 616 | const CompressedImage::Metadata &metadata = image.GetMetadata(); 617 | return 618 | metadata.format == CompressedImage::kRGBA && 619 | metadata.compressor_name == "pvrtc" && 620 | metadata.uncompressed_height >= kBlockHeight && 621 | metadata.uncompressed_width >= kBlockWidth && 622 | metadata.compressed_width == metadata.compressed_height && 623 | IsPowerOfTwo(metadata.uncompressed_height) && 624 | IsPowerOfTwo(metadata.uncompressed_width) && 625 | metadata.compressed_height == metadata.uncompressed_height && 626 | metadata.compressed_width == metadata.uncompressed_width && 627 | image.GetDataSize() == ComputeCompressedDataSize(metadata.format, 628 | metadata.uncompressed_height, metadata.uncompressed_width); 629 | } 630 | 631 | size_t PvrtcCompressor::ComputeCompressedDataSize( 632 | CompressedImage::Format format, uint32 height, uint32 width) { 633 | return width * height / 4; 634 | } 635 | 636 | bool PvrtcCompressor::Compress(CompressedImage::Format format, 637 | uint32 height, uint32 width, 638 | uint32 padding_bytes_per_row, 639 | const uint8 *buffer, CompressedImage *image) { 640 | if (!buffer || !image || height == 0 || width == 0) 641 | return false; 642 | 643 | if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height) || width != height) 644 | return false; 645 | 646 | if (padding_bytes_per_row != 0) 647 | return false; 648 | 649 | if (width % kBlockWidth != 0 || height % kBlockHeight != 0) 650 | return false; 651 | 652 | size_t data_size = ComputeCompressedDataSize(format, height, width); 653 | const CompressedImage::Metadata metadata(format, "pvrtc", height, width, 654 | height, width, 0); 655 | if (image->OwnsData()) { 656 | image->CreateOwnedData(metadata, data_size); 657 | } else { 658 | // Make sure the external storage has the correct size. 659 | if (image->GetDataSize() != data_size) 660 | return false; 661 | image->SetMetadata(metadata); 662 | } 663 | 664 | CompressPVRTC_RGBA_2BPP((const Rgba8888*)buffer, width, height, 665 | image->GetMutableData()); 666 | return true; 667 | } 668 | 669 | bool PvrtcCompressor::Decompress(const CompressedImage &image, 670 | std::vector *decompressed_buffer) { 671 | return false; 672 | } 673 | 674 | bool PvrtcCompressor::Downsample(const CompressedImage &image, 675 | CompressedImage *downsampled_image) { 676 | return false; 677 | } 678 | 679 | bool PvrtcCompressor::Pad(const CompressedImage &image, uint32 padded_height, 680 | uint32 padded_width, CompressedImage *padded_image) { 681 | return false; 682 | } 683 | 684 | bool PvrtcCompressor::CompressAndPad(CompressedImage::Format format, 685 | uint32 height, uint32 width, 686 | uint32 padded_height, uint32 padded_width, 687 | uint32 padding_bytes_per_row, 688 | const uint8 *buffer, 689 | CompressedImage *padded_image) { 690 | return false; 691 | } 692 | 693 | bool PvrtcCompressor::CreateSolidImage(CompressedImage::Format format, 694 | uint32 height, uint32 width, 695 | const uint8 *color, 696 | CompressedImage *image) { 697 | return false; 698 | } 699 | 700 | bool PvrtcCompressor::CopySubimage(const CompressedImage &image, 701 | uint32 start_row, uint32 start_column, 702 | uint32 height, uint32 width, 703 | CompressedImage *subimage) { 704 | return false; 705 | } 706 | 707 | } // namespace image_codec_compression 708 | -------------------------------------------------------------------------------- /image_compression/public/compressed_image.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef IMAGE_COMPRESSION_PUBLIC_COMPRESSED_IMAGE_H_ 16 | #define IMAGE_COMPRESSION_PUBLIC_COMPRESSED_IMAGE_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "base/integral_types.h" 23 | #include "base/logging.h" 24 | 25 | namespace image_codec_compression { 26 | 27 | // This class is used to represent an image that has been compressed 28 | // using a Compressor. It encapsulates all information about the image 29 | // to make decompression and other operations possible. An instance 30 | // can be created either with internally- or externally-managed 31 | // storage. 32 | class CompressedImage { 33 | public: 34 | // Supported image format types. 35 | enum Format { 36 | kRGB, // Red, green, blue. 37 | kBGR, // Blue, green, red (used by DirectX). 38 | kRGBA, // Red, green, blue, alpha. 39 | kBGRA, // Blue, green, red, alpha (used by DirectX). 40 | }; 41 | 42 | // This struct encapsulates all the metadata of a compressed image. 43 | struct Metadata { 44 | // The constructor is passed all values. 45 | Metadata(Format format_in, 46 | const std::string &compressor_name_in, 47 | uint32 uncompressed_height_in, 48 | uint32 uncompressed_width_in, 49 | uint32 compressed_height_in, 50 | uint32 compressed_width_in, 51 | uint32 padding_bytes_per_row_in) 52 | : format(format_in), 53 | compressor_name(compressor_name_in), 54 | uncompressed_height(uncompressed_height_in), 55 | uncompressed_width(uncompressed_width_in), 56 | compressed_height(compressed_height_in), 57 | compressed_width(compressed_width_in), 58 | padding_bytes_per_row(padding_bytes_per_row_in) { 59 | } 60 | 61 | // Format of the compressed image. 62 | Format format; 63 | 64 | // Name of the compressor used to compress it. 65 | std::string compressor_name; 66 | 67 | // Height and width in pixels of the original uncompressed image. 68 | uint32 uncompressed_height; 69 | uint32 uncompressed_width; 70 | 71 | // Height and width in pixels of the compressed image. These may 72 | // differ from the originals due to size requirements imposed by 73 | // the compression technique. 74 | uint32 compressed_height; 75 | uint32 compressed_width; 76 | 77 | // Extra padding bytes in each row of the original uncompressed 78 | // image. This is assumed to be the same for the target image 79 | // during decompression. 80 | uint32 padding_bytes_per_row; 81 | }; 82 | 83 | // The default constructor creates an empty image. 84 | CompressedImage() 85 | : metadata_(kRGB, "", 0, 0, 0, 0, 0), 86 | data_size_(0), 87 | data_(NULL), 88 | owns_data_(true) { 89 | } 90 | 91 | // This constructor can be used when data storage is managed 92 | // externally. It is assumed that the lifetime of the data is at 93 | // least as long as that of this instance. 94 | CompressedImage(size_t data_size, uint8 *external_data) 95 | : metadata_(kRGB, "", 0, 0, 0, 0, 0), 96 | data_size_(data_size), 97 | data_(external_data), 98 | owns_data_(false) { 99 | DCHECK(external_data); 100 | } 101 | 102 | // The destructor frees up the data storage if it is owned by this instance. 103 | ~CompressedImage() { 104 | if (OwnsData()) { 105 | delete[] data_; 106 | } 107 | } 108 | 109 | // Copies metadata and data from another instance, which must have 110 | // data. This instance will own its data regardless of the ownership 111 | // status of the other instance. 112 | void Duplicate(const CompressedImage &from) { 113 | // Duplicating from an instance to itself is permissible, but is a no-op if 114 | // the instance already owns its own data. 115 | if (&from != this || !OwnsData()) { 116 | // Save the current data pointer in case creating owned data changes it. 117 | const uint8 *source_data = from.data_; 118 | DCHECK(source_data); 119 | CreateOwnedData(from.metadata_, from.data_size_); 120 | std::memcpy(data_, source_data, data_size_); 121 | } 122 | } 123 | 124 | // Sets the instance to contain the given metadata and a data area 125 | // of the given size. The data will be freed when the instance is 126 | // destroyed. 127 | void CreateOwnedData(const Metadata &metadata, size_t data_size) { 128 | if (OwnsData()) 129 | delete[] data_; 130 | metadata_ = metadata; 131 | data_size_ = data_size; 132 | data_ = new uint8[data_size_]; 133 | owns_data_ = true; 134 | } 135 | 136 | // Sets the metadata in the instance. This should be called only in 137 | // the case where the instance was constructed with external data. 138 | // Otherwise, CreateOwnedData() should be used. 139 | void SetMetadata(const Metadata &metadata) { 140 | DCHECK(!OwnsData()); 141 | metadata_ = metadata; 142 | } 143 | 144 | // Returns the metadata for the compressed image. 145 | const Metadata& GetMetadata() const { 146 | return metadata_; 147 | } 148 | 149 | // Returns true if the data is owned by this instance. 150 | bool OwnsData() const { 151 | return owns_data_; 152 | } 153 | 154 | // Returns the size of the data. 155 | size_t GetDataSize() const { 156 | return data_size_; 157 | } 158 | 159 | // Returns a const pointer to the data. 160 | const uint8* GetData() const { 161 | return data_; 162 | } 163 | 164 | // Returns a pointer to mutable data. 165 | uint8* GetMutableData() { 166 | return data_; 167 | } 168 | 169 | private: 170 | Metadata metadata_; 171 | 172 | // Size of the compressed data. 173 | size_t data_size_; 174 | 175 | // Pointer to compressed image data. The contents and interpretation 176 | // of the data is compressor-specific. 177 | uint8 *data_; 178 | 179 | // Indicates whether the data is managed by this instance. 180 | bool owns_data_; 181 | 182 | // Only the provided Copy functions should be used for copying. 183 | CompressedImage(const CompressedImage&); 184 | void operator=(const CompressedImage&); 185 | }; 186 | 187 | // Returns the number of components in the given format. 188 | inline int GetNumFormatComponents(CompressedImage::Format format) { 189 | switch (format) { 190 | case CompressedImage::kRGB: 191 | case CompressedImage::kBGR: 192 | return 3; 193 | case CompressedImage::kRGBA: 194 | case CompressedImage::kBGRA: 195 | return 4; 196 | default: // To shut up the compiler. 197 | return 0; 198 | } 199 | } 200 | 201 | // Returns true if a format needs to have red and blue components swapped. 202 | inline bool NeedsRedAndBlueSwapped(CompressedImage::Format format) { 203 | return format == CompressedImage::kBGR || format == CompressedImage::kBGRA; 204 | } 205 | 206 | } // namespace image_codec_compression 207 | 208 | #endif // IMAGE_COMPRESSION_PUBLIC_COMPRESSED_IMAGE_H_ 209 | -------------------------------------------------------------------------------- /image_compression/public/compressor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Compressor is a base interface class for classes that implement 16 | // some form of block-based image compression and decompression, such 17 | // as DXTC or ETC. 18 | // 19 | // Input images are restricted to the following: 20 | // - 8 bits per color component (0-255). 21 | // - RGB (24 bits per pixel) or RGBA (32 bits per pixel) format. 22 | // - Row-major order with interleaved color components. 23 | // 24 | // For example, the first 3 bytes of an RGB image are the RGB color 25 | // values for the pixel in the top row and left-most column, the next 26 | // 3 bytes are for the pixel just to the right of that one, and so on. 27 | // 28 | // Several of the functions return a CompressedImage through an 29 | // out-parameter. For each of these, the caller can pass a pointer to 30 | // a default-constructed instance, in which the function will allocate 31 | // the compressed data inside the instance. Alternatively, the pointer 32 | // can be to an instance constructed using external storage, in which 33 | // case that storage will be used for the result. The 34 | // ComputeCompressedDataSize() function can be used to determine how 35 | // much storage to allocate when using this approach. 36 | 37 | #ifndef IMAGE_COMPRESSION_PUBLIC_COMPRESSOR_H_ 38 | #define IMAGE_COMPRESSION_PUBLIC_COMPRESSOR_H_ 39 | 40 | #include 41 | #include 42 | 43 | #include "base/integral_types.h" 44 | #include "image_compression/public/compressed_image.h" 45 | 46 | namespace image_codec_compression { 47 | 48 | class Compressor { 49 | public: 50 | virtual ~Compressor() {} 51 | 52 | // Returns true if the compressor supports compressing images with 53 | // the given format. 54 | virtual bool SupportsFormat(CompressedImage::Format format) const = 0; 55 | 56 | // This can be used to validate a CompressedImage instance to ensure 57 | // that it can be processed by a derived instance. It returns false 58 | // if the compressor type does not match, if the sizes are 59 | // inconsistent for a compressed image of that type, or if the data 60 | // vector has the wrong size. 61 | virtual bool IsValidCompressedImage(const CompressedImage &image) = 0; 62 | 63 | // Returns the size in bytes of the image data that would result 64 | // from creating a compressed image with the given format and 65 | // dimensions. This allows callers to construct a CompressedImage 66 | // with external storage of the correct size to pass to various 67 | // functions. 68 | virtual size_t ComputeCompressedDataSize(CompressedImage::Format format, 69 | uint32 height, uint32 width) = 0; 70 | 71 | // Compresses an image of the given format represented by the given 72 | // buffer data and image sizes, storing the results in the given 73 | // CompressedImage instance. The padding_bytes_per_row parameter 74 | // indicates how much additional padding each row of the original 75 | // image contains; 0 means the rows are contiguous. Returns false on 76 | // error. 77 | virtual bool Compress(CompressedImage::Format format, 78 | uint32 height, uint32 width, 79 | uint32 padding_bytes_per_row, 80 | const uint8 *buffer, CompressedImage *image) = 0; 81 | 82 | // Decompresses a compressed image. The destination buffer vector 83 | // will be resized correctly to contain the decompressed data. 84 | // Returns false on error. 85 | virtual bool Decompress(const CompressedImage &image, 86 | std::vector *decompressed_buffer) = 0; 87 | 88 | // This function downsamples a compressed image to half its height 89 | // and half its width; the size of each resulting dimension will be 90 | // (oldsize + 1)/2. It may be used for example to generate mipmaps 91 | // of a compressed image without having to decompress it first. 92 | // Derived classes may impose restrictions on the types and sizes of 93 | // images that can be downsampled. Returns false if downsampling is 94 | // not possible or if an error occurs. 95 | virtual bool Downsample(const CompressedImage &image, 96 | CompressedImage *downsampled_image) = 0; 97 | 98 | // This function can be used to pad a compressed image to a new 99 | // height and width, replicating the last (bottom) row and last 100 | // (right) column of pixels as necessary. Derived classes may 101 | // impose restrictions on the types and sizes of images that can be 102 | // padded. If a padded size is not larger than the original size, no 103 | // padding is done in that dimension. Returns false if padding is 104 | // not possible or if an error occurs. 105 | virtual bool Pad(const CompressedImage &image, 106 | uint32 padded_height, uint32 padded_width, 107 | CompressedImage *padded_image) = 0; 108 | 109 | // Compresses and pads in a single operation. Some compressors may 110 | // implement these more efficiently when done together. The same 111 | // rules and restrictions for Compress() and Pad() apply here. 112 | // Images resulting from this operation may vary slightly from those 113 | // from applying compression and padding separately. 114 | virtual bool CompressAndPad(CompressedImage::Format format, 115 | uint32 height, uint32 width, 116 | uint32 padded_height, uint32 padded_width, 117 | uint32 padding_bytes_per_row, 118 | const uint8 *buffer, 119 | CompressedImage *padded_image) = 0; 120 | 121 | // This function can be used to create a compressed image of the 122 | // given format and size with a solid color represented by the given 123 | // color (a single-pixel buffer). Returns false if this is not 124 | // possible. 125 | virtual bool CreateSolidImage(CompressedImage::Format format, 126 | uint32 height, uint32 width, const uint8 *color, 127 | CompressedImage *image) = 0; 128 | 129 | // Given a compressed image, this copies a subimage region defined 130 | // by starting locations and sizes. The region must be completely 131 | // contained within the original image. Derived classes may impose 132 | // other restrictions on the locations and sizes. Returns false on 133 | // error. 134 | virtual bool CopySubimage(const CompressedImage &image, 135 | uint32 start_row, uint32 start_column, 136 | uint32 height, uint32 width, 137 | CompressedImage *subimage) = 0; 138 | }; 139 | 140 | } // namespace image_codec_compression 141 | 142 | #endif // IMAGE_COMPRESSION_PUBLIC_COMPRESSOR_H_ 143 | -------------------------------------------------------------------------------- /image_compression/public/dxtc_compressor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // DxtcCompressor is a derived Compressor class that implements DXTC 16 | // image compression and decompression. It supports DXT1 compression 17 | // for RGB images and DXT5 compression for RGBA images. 18 | // 19 | // Compression should work correctly on source images of any size. 20 | // Since DXT compression operates on 4x4 blocks of pixels, the 21 | // compressed output image always has dimensions that are multiples of 22 | // 4. When compressing, if either of the original image dimensions is 23 | // not a multiple of 4, the last row or column of pixels in the 24 | // original image is replicated to simulate the correct size in the 25 | // compressed output. 26 | // 27 | // The compressed_height and compressed_width members of the 28 | // CompressionSpec can be used to specify a different size for the 29 | // resulting compressed image. For example, they can be used to 30 | // compress to an image whose dimensions are (larger) powers of 2. 31 | // Results in such cases are similar to those of the Pad() function. 32 | // 33 | // Decompression has similar sizing rules, allowing a subimage of a 34 | // compressed image to be decompressed into an image of the 35 | // appropriate size. 36 | // 37 | // Reduction of a compressed image requires both image dimensions be a 38 | // multiple of 8. 39 | 40 | #ifndef IMAGE_COMPRESSION_PUBLIC_DXTC_COMPRESSOR_H_ 41 | #define IMAGE_COMPRESSION_PUBLIC_DXTC_COMPRESSOR_H_ 42 | 43 | #include 44 | #include 45 | 46 | #include "base/integral_types.h" 47 | #include "image_compression/public/compressed_image.h" 48 | #include "image_compression/public/compressor.h" 49 | 50 | namespace image_codec_compression { 51 | 52 | class DxtcCompressor : public Compressor { 53 | public: 54 | virtual ~DxtcCompressor(); 55 | virtual bool SupportsFormat(CompressedImage::Format format) const; 56 | virtual bool IsValidCompressedImage(const CompressedImage &image); 57 | virtual size_t ComputeCompressedDataSize(CompressedImage::Format format, 58 | uint32 height, uint32 width); 59 | virtual bool Compress(CompressedImage::Format format, 60 | uint32 height, uint32 width, 61 | uint32 padding_bytes_per_row, 62 | const uint8 *buffer, CompressedImage *image); 63 | virtual bool Decompress(const CompressedImage &image, 64 | std::vector *decompressed_buffer); 65 | virtual bool Downsample(const CompressedImage &image, 66 | CompressedImage *downsampled_image); 67 | virtual bool Pad(const CompressedImage &image, 68 | uint32 padded_height, uint32 padded_width, 69 | CompressedImage *padded_image); 70 | virtual bool CompressAndPad(CompressedImage::Format format, 71 | uint32 height, uint32 width, 72 | uint32 padded_height, uint32 padded_width, 73 | uint32 padding_bytes_per_row, 74 | const uint8 *buffer, 75 | CompressedImage *padded_image); 76 | virtual bool CreateSolidImage(CompressedImage::Format format, 77 | uint32 height, uint32 width, const uint8 *color, 78 | CompressedImage *image); 79 | virtual bool CopySubimage(const CompressedImage &image, 80 | uint32 start_row, uint32 start_column, 81 | uint32 height, uint32 width, 82 | CompressedImage *subimage); 83 | }; 84 | 85 | } // namespace image_codec_compression 86 | 87 | #endif // IMAGE_COMPRESSION_PUBLIC_DXTC_COMPRESSOR_H_ 88 | -------------------------------------------------------------------------------- /image_compression/public/dxtc_to_etc_transcoder.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef IMAGE_COMPRESSION_PUBLIC_DXTC_TO_ETC_TRANSCODER 16 | #define IMAGE_COMPRESSION_PUBLIC_DXTC_TO_ETC_TRANSCODER 17 | 18 | #include "image_compression/public/compressed_image.h" 19 | 20 | namespace image_codec_compression { 21 | 22 | // This function transcodes the given image in-place from DXT1 format to ETC1 23 | // format. Only the data section of the image is modified. 24 | void TranscodeDxt1ToEtc1(CompressedImage* image); 25 | 26 | } // namespace image_codec_compression 27 | 28 | #endif // IMAGE_COMPRESSION_PUBLIC_DXTC_TO_ETC_TRANSCODER 29 | -------------------------------------------------------------------------------- /image_compression/public/etc_compressor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // EtcCompressor is a derived Compressor class that implements ETC 16 | // (Ericsson Texture Compression) image compression and decompression. 17 | // It supports ETC1 compression for RGB images. It does not support 18 | // RGBA images at present. 19 | // 20 | // Compression should work correctly on source images of any size. 21 | // Since ETC compression operates on 4x4 blocks of pixels, the 22 | // compressed output image always has dimensions that are multiples of 23 | // 4. When compressing, if either of the original image dimensions is 24 | // not a multiple of 4, the last row or column of pixels in the 25 | // original image is replicated to simulate the correct size in the 26 | // compressed output. 27 | // 28 | // The compressed_height and compressed_width members of the 29 | // CompressionSpec can be used to specify a different size for the 30 | // resulting compressed image. For example, they can be used to 31 | // compress to an image whose dimensions are (larger) powers of 2. 32 | // Results in such cases are similar to those of the Pad() function. 33 | // 34 | // Decompression has similar sizing rules, allowing a subimage of a 35 | // compressed image to be decompressed into an image of the 36 | // appropriate size. 37 | // 38 | // Reduction of a compressed image requires both image dimensions be a 39 | // multiple of 8. 40 | 41 | #ifndef IMAGE_COMPRESSION_PUBLIC_ETC_COMPRESSOR_H_ 42 | #define IMAGE_COMPRESSION_PUBLIC_ETC_COMPRESSOR_H_ 43 | 44 | #include 45 | #include 46 | 47 | #include "base/integral_types.h" 48 | #include "image_compression/public/compressed_image.h" 49 | #include "image_compression/public/compressor.h" 50 | 51 | namespace image_codec_compression { 52 | 53 | class EtcCompressor : public Compressor { 54 | public: 55 | // This enum is used to indicate what strategy to use when compressing 56 | // 4x4 blocks. 57 | enum CompressionStrategy { 58 | kSplitHorizontally, // Always split into 2 rows x 4 columns. 59 | kSplitVertically, // Always split into 4 rows x 2 columns. 60 | kSmallerError, // Choose the split that produces the smaller error. 61 | kHeuristic, // Use a faster heuristic instead of exhaustive search. 62 | }; 63 | 64 | EtcCompressor(); 65 | virtual ~EtcCompressor(); 66 | 67 | // Sets/returns the compression strategy. This specifies what to do when 68 | // compressing each 4x4 block of pixels. ETC operates on a 2x4 or 4x2 69 | // subblock at a time, so it can split a 4x4 block either vertically or 70 | // horizontally. The default is kSmallerError. 71 | void SetCompressionStrategy(CompressionStrategy strategy) { 72 | compression_strategy_ = strategy; 73 | } 74 | CompressionStrategy GetCompressionStrategy() const { 75 | return compression_strategy_; 76 | } 77 | 78 | virtual bool SupportsFormat(CompressedImage::Format format) const; 79 | virtual bool IsValidCompressedImage(const CompressedImage &image); 80 | virtual size_t ComputeCompressedDataSize(CompressedImage::Format format, 81 | uint32 height, uint32 width); 82 | virtual bool Compress(CompressedImage::Format format, 83 | uint32 height, uint32 width, 84 | uint32 padding_bytes_per_row, 85 | const uint8 *buffer, CompressedImage *image); 86 | virtual bool Decompress(const CompressedImage &image, 87 | std::vector *decompressed_buffer); 88 | virtual bool Downsample(const CompressedImage &image, 89 | CompressedImage *downsampled_image); 90 | virtual bool Pad(const CompressedImage &image, 91 | uint32 padded_height, uint32 padded_width, 92 | CompressedImage *padded_image); 93 | virtual bool CompressAndPad(CompressedImage::Format format, 94 | uint32 height, uint32 width, 95 | uint32 padded_height, uint32 padded_width, 96 | uint32 padding_bytes_per_row, 97 | const uint8 *buffer, 98 | CompressedImage *padded_image); 99 | virtual bool CreateSolidImage(CompressedImage::Format format, 100 | uint32 height, uint32 width, const uint8 *color, 101 | CompressedImage *image); 102 | virtual bool CopySubimage(const CompressedImage &image, 103 | uint32 start_row, uint32 start_column, 104 | uint32 height, uint32 width, 105 | CompressedImage *subimage); 106 | 107 | private: 108 | CompressionStrategy compression_strategy_; 109 | }; 110 | 111 | } // namespace image_codec_compression 112 | 113 | #endif // IMAGE_COMPRESSION_PUBLIC_ETC_COMPRESSOR_H_ 114 | -------------------------------------------------------------------------------- /image_compression/public/pvrtc_compressor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // PvrtcCompressor implements PVRTC (PowerVR texture compression). It currently 16 | // only supports compression of RGBA8888 to 2 bits per pixel PVRTC version 1 17 | // RGBA, and will return false for most other method calls (such as 18 | // decompression, padding or downsampling). 19 | // 20 | // PVRTC has two versions, and can compress to 2 or 4 bits per pixel, RGB or 21 | // RGBA. The following is a description of the 2BPP RGBA format used here: 22 | // 23 | // The compressed image can be thought of as containing two 16 BPP images with 24 | // 8x lower width and 4x lower height than the original uncompressed image. 25 | // These images are bilinearly upscaled, where each pixel (x,y) in the lower 26 | // resolution image is copied exactly into pixel (x * 8 + 4, y * 4 + 2) of the 27 | // full resolution image, and the remaining full resolution pixels are 28 | // interpolated. Here these are called the "A" and "B" images. 29 | // 30 | // The compressed image also contains a "modulation" image. For each block of 31 | // 8x4 pixels in the original image, there is either: 32 | // - 1 bit per pixel which tells the decompressor to either use the pixel from 33 | // the A or B image. 34 | // - 2 bits for every other pixel (in a checkerboard pattern) which tells the 35 | // decompressor to either use 100% of the A image, 5/8ths A and 3/8ths B, 36 | // 3/8ths A and 5/8ths B, or 100% B image. The remaining pixels in the 37 | // checkerboard are interpolated, either by averaging adjacent horizontal 38 | // pixels, or vertical pixels, or an average of all 4 orthogonally adjacent 39 | // pixels. 40 | // 41 | // The A, B, and modulation images are stored by writing 64-bit words which 42 | // are an A image pixel, a B image pixel, and a block's worth of modulation 43 | // information, and these are all stored in Z-order-curve order. The choice 44 | // of 1bpp or 2bpp modulation and interplotion direction is encoded in these 45 | // words also. 46 | // 47 | // The exact bit format of the 64-bit word is out of scope here; see the 48 | // implementation. 49 | // 50 | // The compression's main speedup is that it chooses colors for each pixel of 51 | // the A and B images by only considering the 8x4 block around the pixel, even 52 | // though the A and B image pixels - through the interpolation - will affect 53 | // 16x8 pixels in the output uncompressed image, and therefore would better be 54 | // chosen in parallel with their neighbors. It also doesn't use principal 55 | // component analysis to choose the A and B pixels, but an approximation. 56 | 57 | #ifndef IMAGE_COMPRESSION_PUBLIC_PVRTC_COMPRESSOR_H_ 58 | #define IMAGE_COMPRESSION_PUBLIC_PVRTC_COMPRESSOR_H_ 59 | 60 | #include 61 | #include 62 | 63 | #include "base/integral_types.h" 64 | #include "image_compression/public/compressed_image.h" 65 | #include "image_compression/public/compressor.h" 66 | 67 | namespace image_codec_compression { 68 | 69 | // This compressor only supports compression from RGBA8888 to 2BPP PVRTC RGBA. 70 | // All unsupported methods will simply return false. 71 | class PvrtcCompressor : public Compressor { 72 | public: 73 | PvrtcCompressor(); 74 | virtual ~PvrtcCompressor(); 75 | 76 | virtual bool SupportsFormat(CompressedImage::Format format) const; 77 | virtual bool IsValidCompressedImage(const CompressedImage &image); 78 | virtual size_t ComputeCompressedDataSize(CompressedImage::Format format, 79 | uint32 height, uint32 width); 80 | virtual bool Compress(CompressedImage::Format format, 81 | uint32 height, uint32 width, 82 | uint32 padding_bytes_per_row, 83 | const uint8 *buffer, CompressedImage *image); 84 | virtual bool Decompress(const CompressedImage &image, 85 | std::vector *decompressed_buffer); 86 | virtual bool Downsample(const CompressedImage &image, 87 | CompressedImage *downsampled_image); 88 | virtual bool Pad(const CompressedImage &image, 89 | uint32 padded_height, uint32 padded_width, 90 | CompressedImage *padded_image); 91 | virtual bool CompressAndPad(CompressedImage::Format format, 92 | uint32 height, uint32 width, 93 | uint32 padded_height, uint32 padded_width, 94 | uint32 padding_bytes_per_row, 95 | const uint8 *buffer, 96 | CompressedImage *padded_image); 97 | virtual bool CreateSolidImage(CompressedImage::Format format, 98 | uint32 height, uint32 width, const uint8 *color, 99 | CompressedImage *image); 100 | virtual bool CopySubimage(const CompressedImage &image, 101 | uint32 start_row, uint32 start_column, 102 | uint32 height, uint32 width, 103 | CompressedImage *subimage); 104 | }; 105 | 106 | } // namespace image_codec_compression 107 | 108 | #endif // IMAGE_COMPRESSION_PUBLIC_PVRTC_COMPRESSOR_H_ 109 | --------------------------------------------------------------------------------