1 /*
2  * Copyright 2015 The Etc2Comp Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *  http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18 EtcImage.cpp
19 
20 Image is an array of 4x4 blocks that represent the encoding of the source image
21 
22 */
23 
24 #include "EtcConfig.h"
25 
26 #include <stdlib.h>
27 
28 #include "EtcImage.h"
29 
30 #include "Etc.h"
31 #include "EtcBlock4x4.h"
32 #include "EtcBlock4x4EncodingBits.h"
33 #include "EtcSortedBlockList.h"
34 
35 #if ETC_WINDOWS
36 #include <windows.h>
37 #endif
38 #include <ctime>
39 #include <chrono>
40 #include <future>
41 #include <stdio.h>
42 #include <string.h>
43 #include <assert.h>
44 
45 // fix conflict with Block4x4::AlphaMix
46 #ifdef OPAQUE
47 #undef OPAQUE
48 #endif
49 #ifdef TRANSPARENT
50 #undef TRANSPARENT
51 #endif
52 
53 namespace Etc
54 {
55 
56 	// ----------------------------------------------------------------------------------------------------
57 	//
Image(void)58 	Image::Image(void)
59 	{
60 		m_encodingStatus = EncodingStatus::SUCCESS;
61 		m_warningsToCapture = EncodingStatus::SUCCESS;
62 		m_pafrgbaSource = nullptr;
63 
64 		m_pablock = nullptr;
65 
66 		m_encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN;
67 		m_uiEncodingBitsBytes = 0;
68 		m_paucEncodingBits = nullptr;
69 
70 		m_format = Format::UNKNOWN;
71 		m_iNumOpaquePixels = 0;
72 		m_iNumTranslucentPixels = 0;
73 		m_iNumTransparentPixels = 0;
74 	}
75 
76 	// ----------------------------------------------------------------------------------------------------
77 	// constructor using source image
78 	// used to set state before Encode() is called
79 	//
Image(float * a_pafSourceRGBA,unsigned int a_uiSourceWidth,unsigned int a_uiSourceHeight,ErrorMetric a_errormetric)80 	Image::Image(float *a_pafSourceRGBA, unsigned int a_uiSourceWidth,
81 					unsigned int a_uiSourceHeight,
82 					ErrorMetric a_errormetric)
83 	{
84 		m_encodingStatus = EncodingStatus::SUCCESS;
85 		m_warningsToCapture = EncodingStatus::SUCCESS;
86 		m_pafrgbaSource = (ColorFloatRGBA *) a_pafSourceRGBA;
87 		m_uiSourceWidth = a_uiSourceWidth;
88 		m_uiSourceHeight = a_uiSourceHeight;
89 
90 		m_uiExtendedWidth = CalcExtendedDimension((unsigned short)m_uiSourceWidth);
91 		m_uiExtendedHeight = CalcExtendedDimension((unsigned short)m_uiSourceHeight);
92 
93 		m_uiBlockColumns = m_uiExtendedWidth >> 2;
94 		m_uiBlockRows = m_uiExtendedHeight >> 2;
95 
96 		m_pablock = new Block4x4[GetNumberOfBlocks()];
97 		assert(m_pablock);
98 
99 		m_format = Format::UNKNOWN;
100 
101 		m_encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN;
102 		m_uiEncodingBitsBytes = 0;
103 		m_paucEncodingBits = nullptr;
104 
105 		m_errormetric = a_errormetric;
106 		m_fEffort = 0.0f;
107 
108 		m_iEncodeTime_ms = -1;
109 
110 		m_iNumOpaquePixels = 0;
111 		m_iNumTranslucentPixels = 0;
112 		m_iNumTransparentPixels = 0;
113 		m_bVerboseOutput = false;
114 
115 	}
116 
117 	// ----------------------------------------------------------------------------------------------------
118 	// constructor using encoding bits
119 	// recreates encoding state using a previously encoded image
120 	//
Image(Format a_format,unsigned int a_uiSourceWidth,unsigned int a_uiSourceHeight,unsigned char * a_paucEncidingBits,unsigned int a_uiEncodingBitsBytes,Image * a_pimageSource,ErrorMetric a_errormetric)121 	Image::Image(Format a_format,
122 					unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight,
123 					unsigned char *a_paucEncidingBits, unsigned int a_uiEncodingBitsBytes,
124 					Image *a_pimageSource, ErrorMetric a_errormetric)
125 	{
126 		m_encodingStatus = EncodingStatus::SUCCESS;
127 		m_pafrgbaSource = nullptr;
128 		m_uiSourceWidth = a_uiSourceWidth;
129 		m_uiSourceHeight = a_uiSourceHeight;
130 
131 		m_uiExtendedWidth = CalcExtendedDimension((unsigned short)m_uiSourceWidth);
132 		m_uiExtendedHeight = CalcExtendedDimension((unsigned short)m_uiSourceHeight);
133 
134 		m_uiBlockColumns = m_uiExtendedWidth >> 2;
135 		m_uiBlockRows = m_uiExtendedHeight >> 2;
136 
137 		unsigned int uiBlocks = GetNumberOfBlocks();
138 
139 		m_pablock = new Block4x4[uiBlocks];
140 		assert(m_pablock);
141 
142 		m_format = a_format;
143 
144 		m_iNumOpaquePixels = 0;
145 		m_iNumTranslucentPixels = 0;
146 		m_iNumTransparentPixels = 0;
147 
148 		m_encodingbitsformat = DetermineEncodingBitsFormat(m_format);
149 		if (m_encodingbitsformat == Block4x4EncodingBits::Format::UNKNOWN)
150 		{
151 			AddToEncodingStatus(ERROR_UNKNOWN_FORMAT);
152 			return;
153 		}
154 		m_uiEncodingBitsBytes = a_uiEncodingBitsBytes;
155 		m_paucEncodingBits = a_paucEncidingBits;
156 
157 		m_errormetric = a_errormetric;
158 		m_fEffort = 0.0f;
159 		m_bVerboseOutput = false;
160 		m_iEncodeTime_ms = -1;
161 
162 		unsigned char *paucEncodingBits = m_paucEncodingBits;
163 		unsigned int uiEncodingBitsBytesPerBlock = Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat);
164 
165 		unsigned int uiH = 0;
166 		unsigned int uiV = 0;
167 		for (unsigned int uiBlock = 0; uiBlock < uiBlocks; uiBlock++)
168 		{
169 			m_pablock[uiBlock].InitFromEtcEncodingBits(a_format, uiH, uiV, paucEncodingBits,
170 														a_pimageSource, a_errormetric);
171 			paucEncodingBits += uiEncodingBitsBytesPerBlock;
172 			uiH += 4;
173 			if (uiH >= m_uiSourceWidth)
174 			{
175 				uiH = 0;
176 				uiV += 4;
177 			}
178 		}
179 
180 	}
181 
182 	// ----------------------------------------------------------------------------------------------------
183 	//
~Image(void)184 	Image::~Image(void)
185 	{
186 		if (m_pablock != nullptr)
187 		{
188 			delete[] m_pablock;
189 			m_pablock = nullptr;
190 		}
191 
192 		/*if (m_paucEncodingBits != nullptr)
193 		{
194 			delete[] m_paucEncodingBits;
195 			m_paucEncodingBits = nullptr;
196 		}*/
197 	}
198 
199 	// ----------------------------------------------------------------------------------------------------
200 	// encode an image
201 	// create a set of encoding bits that conforms to a_format
202 	// find best fit using a_errormetric
203 	// explore a range of possible encodings based on a_fEffort (range = [0:100])
204 	// speed up process using a_uiJobs as the number of process threads (a_uiJobs must not excede a_uiMaxJobs)
205 	//
Encode(Format a_format,ErrorMetric a_errormetric,float a_fEffort,unsigned int a_uiJobs,unsigned int a_uiMaxJobs)206 	Image::EncodingStatus Image::Encode(Format a_format, ErrorMetric a_errormetric, float a_fEffort, unsigned int a_uiJobs, unsigned int a_uiMaxJobs)
207 	{
208 
209 		auto start = std::chrono::steady_clock::now();
210 
211 		m_encodingStatus = EncodingStatus::SUCCESS;
212 
213 		m_format = a_format;
214 		m_errormetric = a_errormetric;
215 		m_fEffort = a_fEffort;
216 
217 		if (m_errormetric < 0 || m_errormetric > ERROR_METRICS)
218 		{
219 			AddToEncodingStatus(ERROR_UNKNOWN_ERROR_METRIC);
220 			return m_encodingStatus;
221 		}
222 
223 		if (m_fEffort < ETCCOMP_MIN_EFFORT_LEVEL)
224 		{
225 			AddToEncodingStatus(WARNING_EFFORT_OUT_OF_RANGE);
226 			m_fEffort = ETCCOMP_MIN_EFFORT_LEVEL;
227 		}
228 		else if (m_fEffort > ETCCOMP_MAX_EFFORT_LEVEL)
229 		{
230 			AddToEncodingStatus(WARNING_EFFORT_OUT_OF_RANGE);
231 			m_fEffort = ETCCOMP_MAX_EFFORT_LEVEL;
232 		}
233 		if (a_uiJobs < 1)
234 		{
235 			a_uiJobs = 1;
236 			AddToEncodingStatus(WARNING_JOBS_OUT_OF_RANGE);
237 		}
238 		else if (a_uiJobs > a_uiMaxJobs)
239 		{
240 			a_uiJobs = a_uiMaxJobs;
241 			AddToEncodingStatus(WARNING_JOBS_OUT_OF_RANGE);
242 		}
243 
244 		m_encodingbitsformat = DetermineEncodingBitsFormat(m_format);
245 
246 		if (m_encodingbitsformat == Block4x4EncodingBits::Format::UNKNOWN)
247 		{
248 			AddToEncodingStatus(ERROR_UNKNOWN_FORMAT);
249 			return m_encodingStatus;
250 		}
251 
252 		assert(m_paucEncodingBits == nullptr);
253 		m_uiEncodingBitsBytes = GetNumberOfBlocks() * Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat);
254 		m_paucEncodingBits = new unsigned char[m_uiEncodingBitsBytes];
255 
256 		InitBlocksAndBlockSorter();
257 
258 
259 		std::future<void> *handle = new std::future<void>[a_uiMaxJobs];
260 
261 		unsigned int uiNumThreadsNeeded = 0;
262 		unsigned int uiUnfinishedBlocks = GetNumberOfBlocks();
263 
264 		uiNumThreadsNeeded = (uiUnfinishedBlocks < a_uiJobs) ? uiUnfinishedBlocks : a_uiJobs;
265 
266 		for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
267 		{
268 			handle[i] = async(std::launch::async, &Image::RunFirstPass, this, i, uiNumThreadsNeeded);
269 		}
270 
271 		RunFirstPass(uiNumThreadsNeeded - 1, uiNumThreadsNeeded);
272 
273 		for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
274 		{
275 			handle[i].get();
276 		}
277 
278 		// perform effort-based encoding
279 		if (m_fEffort > ETCCOMP_MIN_EFFORT_LEVEL)
280 		{
281 			unsigned int uiFinishedBlocks = 0;
282 			unsigned int uiTotalEffortBlocks = static_cast<unsigned int>(roundf(0.01f * m_fEffort  * GetNumberOfBlocks()));
283 
284 			if (m_bVerboseOutput)
285 			{
286 				printf("effortblocks = %d\n", uiTotalEffortBlocks);
287 			}
288 			unsigned int uiPass = 0;
289 			while (1)
290 			{
291 				if (m_bVerboseOutput)
292 				{
293 					uiPass++;
294 					printf("pass %u\n", uiPass);
295 				}
296 				m_psortedblocklist->Sort();
297 				uiUnfinishedBlocks = m_psortedblocklist->GetNumberOfSortedBlocks();
298 				uiFinishedBlocks = GetNumberOfBlocks() - uiUnfinishedBlocks;
299 				if (m_bVerboseOutput)
300 				{
301 					printf("    %u unfinished blocks\n", uiUnfinishedBlocks);
302 					// m_psortedblocklist->Print();
303 				}
304 
305 
306 
307 				//stop enocding when we did enough to satify the effort percentage
308 				if (uiFinishedBlocks >= uiTotalEffortBlocks)
309 				{
310 					if (m_bVerboseOutput)
311 					{
312 						printf("Finished %d Blocks out of %d\n", uiFinishedBlocks, uiTotalEffortBlocks);
313 					}
314 					break;
315 				}
316 
317 				unsigned int uiIteratedBlocks = 0;
318 				unsigned int blocksToIterateThisPass = (uiTotalEffortBlocks - uiFinishedBlocks);
319 				uiNumThreadsNeeded = (uiUnfinishedBlocks < a_uiJobs) ? uiUnfinishedBlocks : a_uiJobs;
320 
321 				if (uiNumThreadsNeeded <= 1)
322 				{
323 					//since we already how many blocks each thread will process
324 					//cap the thread limit to do the proper amount of work, and not more
325 					uiIteratedBlocks = IterateThroughWorstBlocks(blocksToIterateThisPass, 0, 1);
326 				}
327 				else
328 				{
329 					//we have a lot of work to do, so lets multi thread it
330 					std::future<unsigned int> *handleToBlockEncoders = new std::future<unsigned int>[uiNumThreadsNeeded-1];
331 
332 					for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
333 					{
334 						handleToBlockEncoders[i] = async(std::launch::async, &Image::IterateThroughWorstBlocks, this, blocksToIterateThisPass, i, uiNumThreadsNeeded);
335 					}
336 					uiIteratedBlocks = IterateThroughWorstBlocks(blocksToIterateThisPass, uiNumThreadsNeeded - 1, uiNumThreadsNeeded);
337 
338 					for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
339 					{
340 						uiIteratedBlocks += handleToBlockEncoders[i].get();
341 					}
342 
343 					delete[] handleToBlockEncoders;
344 				}
345 
346 				if (m_bVerboseOutput)
347 				{
348 					printf("    %u iterated blocks\n", uiIteratedBlocks);
349 				}
350 			}
351 		}
352 
353 		// generate Etc2-compatible bit-format 4x4 blocks
354 		for (int i = 0; i < (int)a_uiJobs - 1; i++)
355 		{
356 			handle[i] = async(std::launch::async, &Image::SetEncodingBits, this, i, a_uiJobs);
357 		}
358 		SetEncodingBits(a_uiJobs - 1, a_uiJobs);
359 
360 		for (int i = 0; i < (int)a_uiJobs - 1; i++)
361 		{
362 			handle[i].get();
363 		}
364 
365 		auto end = std::chrono::steady_clock::now();
366 		std::chrono::milliseconds elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
367 		m_iEncodeTime_ms = (int)elapsed.count();
368 
369 		delete[] handle;
370 		delete m_psortedblocklist;
371 		return m_encodingStatus;
372 	}
373 
374 	// ----------------------------------------------------------------------------------------------------
375 	// iterate the encoding thru the blocks with the worst error
376 	// stop when a_uiMaxBlocks blocks have been iterated
377 	// split the blocks between the process threads using a_uiMultithreadingOffset and a_uiMultithreadingStride
378 	//
IterateThroughWorstBlocks(unsigned int a_uiMaxBlocks,unsigned int a_uiMultithreadingOffset,unsigned int a_uiMultithreadingStride)379 	unsigned int Image::IterateThroughWorstBlocks(unsigned int a_uiMaxBlocks,
380 													unsigned int a_uiMultithreadingOffset,
381 													unsigned int a_uiMultithreadingStride)
382 	{
383 		assert(a_uiMultithreadingStride > 0);
384 		unsigned int uiIteratedBlocks = a_uiMultithreadingOffset;
385 
386 		SortedBlockList::Link *plink = m_psortedblocklist->GetLinkToFirstBlock();
387 		for (plink = plink->Advance(a_uiMultithreadingOffset);
388 				plink != nullptr;
389 				plink = plink->Advance(a_uiMultithreadingStride) )
390 		{
391 			if (uiIteratedBlocks >= a_uiMaxBlocks)
392 			{
393 				break;
394 			}
395 
396 			plink->GetBlock()->PerformEncodingIteration(m_fEffort);
397 
398 			uiIteratedBlocks += a_uiMultithreadingStride;
399 		}
400 
401 		return uiIteratedBlocks;
402 	}
403 
404 	// ----------------------------------------------------------------------------------------------------
405 	// determine which warnings to check for during Encode() based on encoding format
406 	//
FindEncodingWarningTypesForCurFormat()407 	void Image::FindEncodingWarningTypesForCurFormat()
408 	{
409 		TrackEncodingWarning(WARNING_ALL_TRANSPARENT_PIXELS);
410 		TrackEncodingWarning(WARNING_SOME_RGBA_NOT_0_TO_1);
411 		switch (m_format)
412 		{
413 		case Image::Format::ETC1:
414 		case Image::Format::RGB8:
415 		case Image::Format::SRGB8:
416 			TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS);
417 			TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
418 			break;
419 
420 		case Image::Format::RGB8A1:
421 		case Image::Format::SRGB8A1:
422 			TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
423 			TrackEncodingWarning(WARNING_ALL_OPAQUE_PIXELS);
424 			break;
425 		case Image::Format::RGBA8:
426 		case Image::Format::SRGBA8:
427 			TrackEncodingWarning(WARNING_ALL_OPAQUE_PIXELS);
428 			break;
429 
430 		case Image::Format::R11:
431 		case Image::Format::SIGNED_R11:
432 			TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS);
433 			TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
434 			TrackEncodingWarning(WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO);
435 			TrackEncodingWarning(WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO);
436 			break;
437 
438 		case Image::Format::RG11:
439 		case Image::Format::SIGNED_RG11:
440 			TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS);
441 			TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
442 			TrackEncodingWarning(WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO);
443 			break;
444 		case Image::Format::FORMATS:
445 		case Image::Format::UNKNOWN:
446 		default:
447 			assert(0);
448 			break;
449 		}
450 	}
451 
452 	// ----------------------------------------------------------------------------------------------------
453 	// examine source pixels to check for warnings
454 	//
FindAndSetEncodingWarnings()455 	void Image::FindAndSetEncodingWarnings()
456 	{
457 		int numPixels = (m_uiBlockRows * 4) * (m_uiBlockColumns * 4);
458 		if (m_iNumOpaquePixels == numPixels)
459 		{
460 			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_ALL_OPAQUE_PIXELS);
461 		}
462 		if (m_iNumOpaquePixels < numPixels)
463 		{
464 			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_NON_OPAQUE_PIXELS);
465 		}
466 		if (m_iNumTranslucentPixels > 0)
467 		{
468 			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_TRANSLUCENT_PIXELS);
469 		}
470 		if (m_iNumTransparentPixels == numPixels)
471 		{
472 			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_ALL_TRANSPARENT_PIXELS);
473 		}
474 		if (m_numColorValues.fB > 0.0f)
475 		{
476 			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO);
477 		}
478 		if (m_numColorValues.fG > 0.0f)
479 		{
480 			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO);
481 		}
482 
483 		if (m_numOutOfRangeValues.fR > 0.0f || m_numOutOfRangeValues.fG > 0.0f)
484 		{
485 			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_RGBA_NOT_0_TO_1);
486 		}
487 		if (m_numOutOfRangeValues.fB > 0.0f || m_numOutOfRangeValues.fA > 0.0f)
488 		{
489 			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_RGBA_NOT_0_TO_1);
490 		}
491 	}
492 
493 	// ----------------------------------------------------------------------------------------------------
494 	// return a string name for a given image format
495 	//
EncodingFormatToString(Image::Format a_format)496 	const char * Image::EncodingFormatToString(Image::Format a_format)
497 	{
498 		switch (a_format)
499 		{
500 		case Image::Format::ETC1:
501 			return "ETC1";
502 		case Image::Format::RGB8:
503 			return "RGB8";
504 		case Image::Format::SRGB8:
505 			return "SRGB8";
506 
507 		case Image::Format::RGB8A1:
508 			return "RGB8A1";
509 		case Image::Format::SRGB8A1:
510 			return "SRGB8A1";
511 		case Image::Format::RGBA8:
512 			return "RGBA8";
513 		case Image::Format::SRGBA8:
514 			return "SRGBA8";
515 
516 		case Image::Format::R11:
517 			return "R11";
518 		case Image::Format::SIGNED_R11:
519 			return "SIGNED_R11";
520 
521 		case Image::Format::RG11:
522 			return "RG11";
523 		case Image::Format::SIGNED_RG11:
524 			return "SIGNED_RG11";
525 		case Image::Format::FORMATS:
526 		case Image::Format::UNKNOWN:
527 		default:
528 			return "UNKNOWN";
529 		}
530 	}
531 
532 	// ----------------------------------------------------------------------------------------------------
533 	// return a string name for the image's format
534 	//
EncodingFormatToString(void)535 	const char * Image::EncodingFormatToString(void)
536 	{
537 		return EncodingFormatToString(m_format);
538 	}
539 
540 	// ----------------------------------------------------------------------------------------------------
541 	// init image blocks prior to encoding
542 	// init block sorter for subsequent sortings
543 	// check for encoding warnings
544 	//
InitBlocksAndBlockSorter(void)545 	void Image::InitBlocksAndBlockSorter(void)
546 	{
547 
548 		FindEncodingWarningTypesForCurFormat();
549 
550 		// init each block
551 		Block4x4 *pblock = m_pablock;
552 		unsigned char *paucEncodingBits = m_paucEncodingBits;
553 		for (unsigned int uiBlockRow = 0; uiBlockRow < m_uiBlockRows; uiBlockRow++)
554 		{
555 			unsigned int uiBlockV = uiBlockRow * 4;
556 
557 			for (unsigned int uiBlockColumn = 0; uiBlockColumn < m_uiBlockColumns; uiBlockColumn++)
558 			{
559 				unsigned int uiBlockH = uiBlockColumn * 4;
560 
561 				pblock->InitFromSource(this, uiBlockH, uiBlockV, paucEncodingBits, m_errormetric);
562 
563 				paucEncodingBits += Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat);
564 
565 				pblock++;
566 			}
567 		}
568 
569 		FindAndSetEncodingWarnings();
570 
571 		// init block sorter
572 		{
573 			m_psortedblocklist = new SortedBlockList(GetNumberOfBlocks(), 100);
574 
575 			for (unsigned int uiBlock = 0; uiBlock < GetNumberOfBlocks(); uiBlock++)
576 			{
577 				pblock = &m_pablock[uiBlock];
578 				m_psortedblocklist->AddBlock(pblock);
579 			}
580 		}
581 
582 	}
583 
584 	// ----------------------------------------------------------------------------------------------------
585 	// run the first pass of the encoder
586 	// the encoder generally finds a reasonable, fast encoding
587 	// this is run on all blocks regardless of effort to ensure that all blocks have a valid encoding
588 	//
RunFirstPass(unsigned int a_uiMultithreadingOffset,unsigned int a_uiMultithreadingStride)589 	void Image::RunFirstPass(unsigned int a_uiMultithreadingOffset, unsigned int a_uiMultithreadingStride)
590 	{
591 		assert(a_uiMultithreadingStride > 0);
592 
593 		for (unsigned int uiBlock = a_uiMultithreadingOffset;
594 				uiBlock < GetNumberOfBlocks();
595 				uiBlock += a_uiMultithreadingStride)
596 		{
597 			Block4x4 *pblock = &m_pablock[uiBlock];
598 			pblock->PerformEncodingIteration(m_fEffort);
599 		}
600 	}
601 
602     // ----------------------------------------------------------------------------------------------------
603 	// set the encoding bits (for the output file) based on the best encoding for each block
604 	//
SetEncodingBits(unsigned int a_uiMultithreadingOffset,unsigned int a_uiMultithreadingStride)605 	void Image::SetEncodingBits(unsigned int a_uiMultithreadingOffset,
606 								unsigned int a_uiMultithreadingStride)
607 	{
608 		assert(a_uiMultithreadingStride > 0);
609 
610 		for (unsigned int uiBlock = a_uiMultithreadingOffset;
611 				uiBlock < GetNumberOfBlocks();
612 				uiBlock += a_uiMultithreadingStride)
613 		{
614 			Block4x4 *pblock = &m_pablock[uiBlock];
615 			pblock->SetEncodingBitsFromEncoding();
616 		}
617 
618 	}
619 
620 	// ----------------------------------------------------------------------------------------------------
621 	// return the image error
622 	// image error is the sum of all block errors
623 	//
GetError(void)624 	float Image::GetError(void)
625 	{
626 		float fError = 0.0f;
627 
628 		for (unsigned int uiBlock = 0; uiBlock < GetNumberOfBlocks(); uiBlock++)
629 		{
630 			Block4x4 *pblock = &m_pablock[uiBlock];
631 			fError += pblock->GetError();
632 		}
633 
634 		return fError;
635 	}
636 
637 	// ----------------------------------------------------------------------------------------------------
638 	// determine the encoding bits format based on the encoding format
639 	// the encoding bits format is a family of bit encodings that are shared across various encoding formats
640 	//
DetermineEncodingBitsFormat(Format a_format)641 	Block4x4EncodingBits::Format Image::DetermineEncodingBitsFormat(Format a_format)
642 	{
643 		Block4x4EncodingBits::Format encodingbitsformat;
644 
645 		// determine encoding bits format from image format
646 		switch (a_format)
647 		{
648 		case Format::ETC1:
649 		case Format::RGB8:
650 		case Format::SRGB8:
651 			encodingbitsformat = Block4x4EncodingBits::Format::RGB8;
652 			break;
653 
654 		case Format::RGBA8:
655 		case Format::SRGBA8:
656 			encodingbitsformat = Block4x4EncodingBits::Format::RGBA8;
657 			break;
658 
659 		case Format::R11:
660 		case Format::SIGNED_R11:
661 			encodingbitsformat = Block4x4EncodingBits::Format::R11;
662 			break;
663 
664 		case Format::RG11:
665 		case Format::SIGNED_RG11:
666 			encodingbitsformat = Block4x4EncodingBits::Format::RG11;
667 			break;
668 
669 		case Format::RGB8A1:
670 		case Format::SRGB8A1:
671 			encodingbitsformat = Block4x4EncodingBits::Format::RGB8A1;
672 			break;
673 
674 		default:
675 			encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN;
676 			break;
677 		}
678 
679 		return encodingbitsformat;
680 	}
681 
682 	// ----------------------------------------------------------------------------------------------------
683 	//
684 
685 }	// namespace Etc
686