| 10 | { |
| 11 | |
| 12 | AlphaStats ClassifyAlpha(const uint8_t* rgba, size_t pixelCount, double partialThreshold, double clearThreshold) |
| 13 | { |
| 14 | AlphaStats s; |
| 15 | if (!rgba || pixelCount == 0) |
| 16 | return s; |
| 17 | size_t clear = 0, opaque = 0, partial = 0; |
| 18 | int aMin = 255, aMax = 0; |
| 19 | unsigned long long aSum = 0; |
| 20 | for (size_t i = 0; i < pixelCount; i++) |
| 21 | { |
| 22 | const int a = rgba[i * 4 + 3]; |
| 23 | aSum += static_cast<unsigned long long>(a); |
| 24 | if (a < aMin) |
| 25 | aMin = a; |
| 26 | if (a > aMax) |
| 27 | aMax = a; |
| 28 | if (a == 0) |
| 29 | clear++; |
| 30 | else if (a == 255) |
| 31 | opaque++; |
| 32 | else |
| 33 | partial++; |
| 34 | } |
| 35 | s.aMin = aMin; |
| 36 | s.aMax = aMax; |
| 37 | s.aMean = static_cast<int>(aSum / pixelCount); |
| 38 | s.pctClear = 100.0 * static_cast<double>(clear) / static_cast<double>(pixelCount); |
| 39 | s.pctOpaque = 100.0 * static_cast<double>(opaque) / static_cast<double>(pixelCount); |
| 40 | s.pctPartial = 100.0 * static_cast<double>(partial) / static_cast<double>(pixelCount); |
| 41 | // Partial alpha → genuine blend (must be drawn back-to-front, no depth-write). |
| 42 | // Otherwise fully-transparent holes → alpha-test cutout. Otherwise opaque. |
| 43 | // Both Cutout and Opaque occlude (write depth); the holes are handled by the test. |
| 44 | if (s.pctPartial >= partialThreshold) |
| 45 | s.kind = AlphaStats::Blend; |
| 46 | else if (s.pctClear >= clearThreshold) |
| 47 | s.kind = AlphaStats::Cutout; |
| 48 | else |
| 49 | s.kind = AlphaStats::Opaque; |
| 50 | return s; |
| 51 | } |
| 52 | |
| 53 | AlphaStats::Kind ClassifyTextureAlpha(bool hasAlpha, bool isChromaKey, bool oneBitAlphaFormat, |
| 54 | const AlphaStats* decoded) |
no outgoing calls
no test coverage detected