1 /* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkBitmap.h" 9 #include "SkData.h" 10 #include "SkEndian.h" 11 #include "SkImageInfo.h" 12 #include "SkTemplates.h" 13 #include "SkTextureCompressor.h" 14 #include "Test.h" 15 16 // TODO: Create separate tests for RGB and RGBA data once 17 // ASTC and ETC1 decompression is implemented. 18 19 static bool decompresses_a8(SkTextureCompressor::Format fmt) { 20 switch (fmt) { 21 case SkTextureCompressor::kLATC_Format: 22 case SkTextureCompressor::kR11_EAC_Format: 23 return true; 24 25 default: 26 return false; 27 } 28 } 29 30 static bool compresses_a8(SkTextureCompressor::Format fmt) { 31 switch (fmt) { 32 case SkTextureCompressor::kLATC_Format: 33 case SkTextureCompressor::kR11_EAC_Format: 34 case SkTextureCompressor::kASTC_12x12_Format: 35 return true; 36 37 default: 38 return false; 39 } 40 } 41 42 /** 43 * Make sure that we properly fail when we don't have multiple of four image dimensions. 44 */ 45 DEF_TEST(CompressAlphaFailDimensions, reporter) { 46 static const int kWidth = 17; 47 static const int kHeight = 17; 48 49 // R11_EAC and LATC are both dimensions of 4, so we need to make sure that we 50 // are violating those assumptions. And if we are, then we're also violating the 51 // assumptions of ASTC, which is 12x12 since any number not divisible by 4 is 52 // also not divisible by 12. Our dimensions are prime, so any block dimension 53 // larger than 1 should fail. 54 REPORTER_ASSERT(reporter, kWidth % 4 != 0); 55 REPORTER_ASSERT(reporter, kHeight % 4 != 0); 56 57 SkAutoPixmapStorage pixmap; 58 pixmap.alloc(SkImageInfo::MakeA8(kWidth, kHeight)); 59 // leaving the pixels uninitialized, as they don't affect the test... 60 61 for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) { 62 const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i); 63 if (!compresses_a8(fmt)) { 64 continue; 65 } 66 SkAutoDataUnref data(SkTextureCompressor::CompressBitmapToFormat(pixmap, fmt)); 67 REPORTER_ASSERT(reporter, nullptr == data); 68 } 69 } 70 71 /** 72 * Make sure that we properly fail when we don't have the correct bitmap type. 73 * compressed textures can (currently) only be created from A8 bitmaps. 74 */ 75 DEF_TEST(CompressAlphaFailColorType, reporter) { 76 static const int kWidth = 12; 77 static const int kHeight = 12; 78 79 // ASTC is at most 12x12, and any dimension divisible by 12 is also divisible 80 // by 4, which is the dimensions of R11_EAC and LATC. In the future, we might 81 // support additional variants of ASTC, such as 5x6 and 8x8, in which case this would 82 // need to be updated. 83 REPORTER_ASSERT(reporter, kWidth % 12 == 0); 84 REPORTER_ASSERT(reporter, kHeight % 12 == 0); 85 86 SkAutoPixmapStorage pixmap; 87 pixmap.alloc(SkImageInfo::MakeN32Premul(kWidth, kHeight)); 88 // leaving the pixels uninitialized, as they don't affect the test... 89 90 for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) { 91 const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i); 92 if (!compresses_a8(fmt)) { 93 continue; 94 } 95 SkAutoDataUnref data(SkTextureCompressor::CompressBitmapToFormat(pixmap, fmt)); 96 REPORTER_ASSERT(reporter, nullptr == data); 97 } 98 } 99 100 /** 101 * Make sure that if you compress a texture with alternating black/white pixels, and 102 * then decompress it, you get what you started with. 103 */ 104 DEF_TEST(CompressCheckerboard, reporter) { 105 static const int kWidth = 48; // We need the number to be divisible by both 106 static const int kHeight = 48; // 12 (ASTC) and 16 (ARM NEON R11 EAC). 107 108 // ASTC is at most 12x12, and any dimension divisible by 12 is also divisible 109 // by 4, which is the dimensions of R11_EAC and LATC. In the future, we might 110 // support additional variants of ASTC, such as 5x6 and 8x8, in which case this would 111 // need to be updated. Additionally, ARM NEON and SSE code paths support up to 112 // four blocks of R11 EAC at once, so they operate on 16-wide blocks. Hence, the 113 // valid width and height is going to be the LCM of 12 and 16 which is 4*4*3 = 48 114 REPORTER_ASSERT(reporter, kWidth % 48 == 0); 115 REPORTER_ASSERT(reporter, kHeight % 48 == 0); 116 117 SkAutoPixmapStorage pixmap; 118 pixmap.alloc(SkImageInfo::MakeA8(kWidth, kHeight)); 119 120 // Populate the pixels 121 { 122 uint8_t* pixels = reinterpret_cast<uint8_t*>(pixmap.writable_addr()); 123 REPORTER_ASSERT(reporter, pixels); 124 if (nullptr == pixels) { 125 return; 126 } 127 128 for (int y = 0; y < kHeight; ++y) { 129 for (int x = 0; x < kWidth; ++x) { 130 if ((x ^ y) & 1) { 131 pixels[x] = 0xFF; 132 } else { 133 pixels[x] = 0; 134 } 135 } 136 pixels += pixmap.rowBytes(); 137 } 138 } 139 140 SkAutoTMalloc<uint8_t> decompMemory(kWidth*kHeight); 141 uint8_t* decompBuffer = decompMemory.get(); 142 REPORTER_ASSERT(reporter, decompBuffer); 143 if (nullptr == decompBuffer) { 144 return; 145 } 146 147 for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) { 148 const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i); 149 150 // Ignore formats for RGBA data, since the decompressed buffer 151 // won't match the size and contents of the original. 152 if (!decompresses_a8(fmt) || !compresses_a8(fmt)) { 153 continue; 154 } 155 156 SkAutoDataUnref data(SkTextureCompressor::CompressBitmapToFormat(pixmap, fmt)); 157 REPORTER_ASSERT(reporter, data); 158 if (nullptr == data) { 159 continue; 160 } 161 162 bool decompResult = 163 SkTextureCompressor::DecompressBufferFromFormat( 164 decompBuffer, kWidth, 165 data->bytes(), 166 kWidth, kHeight, fmt); 167 REPORTER_ASSERT(reporter, decompResult); 168 169 const uint8_t* pixels = reinterpret_cast<const uint8_t*>(pixmap.addr()); 170 REPORTER_ASSERT(reporter, pixels); 171 if (nullptr == pixels) { 172 continue; 173 } 174 175 for (int y = 0; y < kHeight; ++y) { 176 for (int x = 0; x < kWidth; ++x) { 177 bool ok = pixels[y*pixmap.rowBytes() + x] == decompBuffer[y*kWidth + x]; 178 REPORTER_ASSERT(reporter, ok); 179 } 180 } 181 } 182 } 183 184 /** 185 * Make sure that if we pass in a solid color bitmap that we get the appropriate results 186 */ 187 DEF_TEST(CompressLATC, reporter) { 188 189 const SkTextureCompressor::Format kLATCFormat = SkTextureCompressor::kLATC_Format; 190 static const int kLATCEncodedBlockSize = 8; 191 192 static const int kWidth = 8; 193 static const int kHeight = 8; 194 195 SkAutoPixmapStorage pixmap; 196 pixmap.alloc(SkImageInfo::MakeA8(kWidth, kHeight)); 197 198 int latcDimX, latcDimY; 199 SkTextureCompressor::GetBlockDimensions(kLATCFormat, &latcDimX, &latcDimY); 200 201 REPORTER_ASSERT(reporter, kWidth % latcDimX == 0); 202 REPORTER_ASSERT(reporter, kHeight % latcDimY == 0); 203 const size_t kSizeToBe = 204 SkTextureCompressor::GetCompressedDataSize(kLATCFormat, kWidth, kHeight); 205 REPORTER_ASSERT(reporter, kSizeToBe == ((kWidth*kHeight*kLATCEncodedBlockSize)/16)); 206 REPORTER_ASSERT(reporter, (kSizeToBe % kLATCEncodedBlockSize) == 0); 207 208 for (int lum = 0; lum < 256; ++lum) { 209 uint8_t* pixels = reinterpret_cast<uint8_t*>(pixmap.writable_addr()); 210 for (int i = 0; i < kWidth*kHeight; ++i) { 211 pixels[i] = lum; 212 } 213 214 SkAutoDataUnref latcData( 215 SkTextureCompressor::CompressBitmapToFormat(pixmap, kLATCFormat)); 216 REPORTER_ASSERT(reporter, latcData); 217 if (nullptr == latcData) { 218 continue; 219 } 220 221 REPORTER_ASSERT(reporter, kSizeToBe == latcData->size()); 222 223 // Make sure that it all matches a given block encoding. Since we have 224 // COMPRESS_LATC_FAST defined in SkTextureCompressor_LATC.cpp, we are using 225 // an approximation scheme that optimizes for speed against coverage maps. 226 // That means that each palette in the encoded block is exactly the same, 227 // and that the three bits saved per pixel are computed from the top three 228 // bits of the luminance value. 229 const uint64_t kIndexEncodingMap[8] = { 1, 7, 6, 5, 4, 3, 2, 0 }; 230 231 // Quantize to three bits in the same way that we do our LATC compression: 232 // 1. Divide by two 233 // 2. Add 9 234 // 3. Divide by two 235 // 4. Approximate division by three twice 236 uint32_t quant = static_cast<uint32_t>(lum); 237 quant >>= 1; // 1 238 quant += 9; // 2 239 quant >>= 1; // 3 240 241 uint32_t a, b, c, ar, br, cr; 242 243 // First division by three 244 a = quant >> 2; 245 ar = (quant & 0x3) << 4; 246 b = quant >> 4; 247 br = (quant & 0xF) << 2; 248 c = quant >> 6; 249 cr = (quant & 0x3F); 250 quant = (a + b + c) + ((ar + br + cr) >> 6); 251 252 // Second division by three 253 a = quant >> 2; 254 ar = (quant & 0x3) << 4; 255 b = quant >> 4; 256 br = (quant & 0xF) << 2; 257 c = quant >> 6; 258 cr = (quant & 0x3F); 259 quant = (a + b + c) + ((ar + br + cr) >> 6); 260 261 const uint64_t kIndex = kIndexEncodingMap[quant]; 262 263 const uint64_t kConstColorEncoding = 264 SkEndian_SwapLE64( 265 255 | 266 (kIndex << 16) | (kIndex << 19) | (kIndex << 22) | (kIndex << 25) | 267 (kIndex << 28) | (kIndex << 31) | (kIndex << 34) | (kIndex << 37) | 268 (kIndex << 40) | (kIndex << 43) | (kIndex << 46) | (kIndex << 49) | 269 (kIndex << 52) | (kIndex << 55) | (kIndex << 58) | (kIndex << 61)); 270 271 const uint64_t* blockPtr = reinterpret_cast<const uint64_t*>(latcData->data()); 272 for (size_t i = 0; i < (kSizeToBe/8); ++i) { 273 REPORTER_ASSERT(reporter, blockPtr[i] == kConstColorEncoding); 274 } 275 } 276 } 277