1 /* 2 * Copyright (C) 2023 The Android Open Source Project 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 package android.graphics.cts; 18 19 import static org.junit.Assert.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertNotNull; 23 import static org.junit.Assert.assertNotSame; 24 import static org.junit.Assert.assertNull; 25 import static org.junit.Assert.assertSame; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assert.fail; 28 29 import android.content.Context; 30 import android.graphics.Bitmap; 31 import android.graphics.BitmapFactory; 32 import android.graphics.BitmapRegionDecoder; 33 import android.graphics.Color; 34 import android.graphics.ColorSpace; 35 import android.graphics.Gainmap; 36 import android.graphics.ImageDecoder; 37 import android.graphics.Rect; 38 import android.hardware.HardwareBuffer; 39 import android.os.Parcel; 40 41 import androidx.test.filters.SmallTest; 42 import androidx.test.platform.app.InstrumentationRegistry; 43 44 import junitparams.JUnitParamsRunner; 45 import junitparams.Parameters; 46 47 import org.junit.Assert; 48 import org.junit.BeforeClass; 49 import org.junit.Ignore; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 53 import java.io.ByteArrayOutputStream; 54 import java.io.InputStream; 55 import java.util.function.Function; 56 57 @SmallTest 58 @RunWith(JUnitParamsRunner.class) 59 public class GainmapTest { 60 private static final float EPSILON = 0.0001f; 61 private static final int TILE_SIZE = 256; 62 63 private static Context sContext; 64 65 static final Bitmap sScalingRedA8; 66 static final Bitmap sScalingRed8888; 67 68 static { 69 sScalingRedA8 = Bitmap.createBitmap(new int[] { 70 Color.RED, 71 Color.RED, 72 Color.RED, 73 Color.RED 74 }, 4, 1, Bitmap.Config.ARGB_8888); sScalingRedA8.setGainmap(new Gainmap(Bitmap.createBitmap(new int[] { 0x00000000, 0x40000000, 0x80000000, 0xFF000000 }, 4, 1, Bitmap.Config.ALPHA_8)))75 sScalingRedA8.setGainmap(new Gainmap(Bitmap.createBitmap(new int[] { 76 0x00000000, 77 0x40000000, 78 0x80000000, 79 0xFF000000 80 }, 4, 1, Bitmap.Config.ALPHA_8))); 81 82 sScalingRed8888 = Bitmap.createBitmap(new int[] { 83 Color.RED, 84 Color.RED, 85 Color.RED, 86 Color.RED 87 }, 4, 1, Bitmap.Config.ARGB_8888); sScalingRed8888.setGainmap(new Gainmap(Bitmap.createBitmap(new int[] { 0xFF000000, 0xFF404040, 0xFF808080, 0xFFFFFFFF }, 4, 1, Bitmap.Config.ARGB_8888)))88 sScalingRed8888.setGainmap(new Gainmap(Bitmap.createBitmap(new int[] { 89 0xFF000000, 90 0xFF404040, 91 0xFF808080, 92 0xFFFFFFFF 93 }, 4, 1, Bitmap.Config.ARGB_8888))); 94 } 95 96 @BeforeClass setupClass()97 public static void setupClass() { 98 sContext = InstrumentationRegistry.getInstrumentation().getContext(); 99 } 100 assertAllAre(float expected, float[] value)101 private static void assertAllAre(float expected, float[] value) { 102 assertEquals(3, value.length); 103 for (int i = 0; i < value.length; i++) { 104 assertEquals("value[" + i + "] didn't match " + expected, expected, value[i], EPSILON); 105 } 106 } 107 assertAre(float r, float g, float b, float[] value)108 private static void assertAre(float r, float g, float b, float[] value) { 109 assertEquals(3, value.length); 110 assertEquals(r, value[0], EPSILON); 111 assertEquals(g, value[1], EPSILON); 112 assertEquals(b, value[2], EPSILON); 113 } 114 checkGainmap(Bitmap bitmap)115 private void checkGainmap(Bitmap bitmap) throws Exception { 116 assertNotNull(bitmap); 117 assertTrue("Missing gainmap", bitmap.hasGainmap()); 118 if (bitmap.getConfig() == Bitmap.Config.HARDWARE) { 119 assertEquals(HardwareBuffer.RGBA_8888, bitmap.getHardwareBuffer().getFormat()); 120 } else { 121 assertEquals(Bitmap.Config.ARGB_8888, bitmap.getConfig()); 122 } 123 assertEquals(ColorSpace.Named.SRGB.ordinal(), bitmap.getColorSpace().getId()); 124 Gainmap gainmap = bitmap.getGainmap(); 125 assertNotNull(gainmap); 126 Bitmap gainmapData = gainmap.getGainmapContents(); 127 assertNotNull(gainmapData); 128 if (bitmap.getConfig() == Bitmap.Config.HARDWARE) { 129 assertEquals(HardwareBuffer.RGBA_8888, gainmapData.getHardwareBuffer().getFormat()); 130 } else { 131 assertEquals(Bitmap.Config.ARGB_8888, gainmapData.getConfig()); 132 } 133 134 assertAllAre(0.f, gainmap.getEpsilonSdr()); 135 assertAllAre(0.f, gainmap.getEpsilonHdr()); 136 assertAllAre(1.f, gainmap.getGamma()); 137 assertEquals(1.f, gainmap.getMinDisplayRatioForHdrTransition(), EPSILON); 138 139 assertAllAre(4f, gainmap.getRatioMax()); 140 assertAllAre(1.0f, gainmap.getRatioMin()); 141 assertEquals(5f, gainmap.getDisplayRatioForFullHdr(), EPSILON); 142 } 143 checkFountainGainmap(Bitmap bitmap)144 private void checkFountainGainmap(Bitmap bitmap) throws Exception { 145 assertNotNull(bitmap); 146 assertTrue("Missing gainmap", bitmap.hasGainmap()); 147 if (bitmap.getConfig() == Bitmap.Config.HARDWARE) { 148 assertEquals(HardwareBuffer.RGBA_8888, bitmap.getHardwareBuffer().getFormat()); 149 } else { 150 assertEquals(Bitmap.Config.ARGB_8888, bitmap.getConfig()); 151 } 152 assertEquals(ColorSpace.Named.SRGB.ordinal(), bitmap.getColorSpace().getId()); 153 Gainmap gainmap = bitmap.getGainmap(); 154 assertNotNull(gainmap); 155 Bitmap gainmapData = gainmap.getGainmapContents(); 156 assertNotNull(gainmapData); 157 if (bitmap.getConfig() == Bitmap.Config.HARDWARE) { 158 final int gainmapFormat = gainmapData.getHardwareBuffer().getFormat(); 159 if (gainmapFormat != HardwareBuffer.RGBA_8888 && gainmapFormat != HardwareBuffer.R_8) { 160 fail("Unexpected gainmap format " + gainmapFormat); 161 } 162 } else { 163 assertEquals(Bitmap.Config.ALPHA_8, gainmapData.getConfig()); 164 } 165 166 assertAllAre(0.f, gainmap.getEpsilonSdr()); 167 assertAllAre(0.f, gainmap.getEpsilonHdr()); 168 assertAllAre(1.f, gainmap.getGamma()); 169 assertEquals(1.f, gainmap.getMinDisplayRatioForHdrTransition(), EPSILON); 170 171 assertAllAre(10.63548f, gainmap.getRatioMax()); 172 assertAllAre(1.0f, gainmap.getRatioMin()); 173 assertEquals(10.63548f, gainmap.getDisplayRatioForFullHdr(), EPSILON); 174 } 175 176 interface DecoderVariation { decode(int id)177 Bitmap decode(int id) throws Exception; 178 } 179 getGainmapDecodeVariations()180 static DecoderVariation[] getGainmapDecodeVariations() { 181 final BitmapFactory.Options hardwareOptions = new BitmapFactory.Options(); 182 hardwareOptions.inPreferredConfig = Bitmap.Config.HARDWARE; 183 DecoderVariation[] callables = new DecoderVariation[] { 184 (id) -> ImageDecoder.decodeBitmap( 185 ImageDecoder.createSource(sContext.getResources(), id), 186 (decoder, info, source) -> decoder.setAllocator( 187 ImageDecoder.ALLOCATOR_SOFTWARE)), 188 189 (id) -> ImageDecoder.decodeBitmap( 190 ImageDecoder.createSource(sContext.getResources(), id)), 191 192 (id) -> ImageDecoder.decodeBitmap( 193 ImageDecoder.createSource(sContext.getResources(), id), 194 (decoder, info, source) -> decoder.setTargetSampleSize(2)), 195 196 (id) -> BitmapFactory.decodeResource(sContext.getResources(), id), 197 198 (id) -> BitmapFactory.decodeResource(sContext.getResources(), id, 199 hardwareOptions), 200 }; 201 return callables; 202 } 203 204 @Test 205 @Parameters(method = "getGainmapDecodeVariations") testDecodeGainmap(DecoderVariation provider)206 public void testDecodeGainmap(DecoderVariation provider) throws Exception { 207 checkGainmap(provider.decode(R.raw.gainmap)); 208 } 209 210 @Test 211 @Parameters(method = "getGainmapDecodeVariations") testDecodeFountainGainmap(DecoderVariation provider)212 public void testDecodeFountainGainmap(DecoderVariation provider) throws Exception { 213 checkFountainGainmap(provider.decode(R.raw.fountain_night)); 214 } 215 216 @Test testDecodeGainmapBitmapFactoryReuse()217 public void testDecodeGainmapBitmapFactoryReuse() throws Exception { 218 BitmapFactory.Options options = new BitmapFactory.Options(); 219 options.inMutable = true; 220 options.inDensity = 160; 221 options.inTargetDensity = 160; 222 223 Bitmap bitmap = BitmapFactory.decodeResource(sContext.getResources(), R.raw.gainmap, 224 options); 225 checkGainmap(bitmap); 226 options.inBitmap = bitmap; 227 assertSame(bitmap, BitmapFactory.decodeResource( 228 sContext.getResources(), R.drawable.baseline_jpeg, options)); 229 assertEquals(1280, bitmap.getWidth()); 230 assertEquals(960, bitmap.getHeight()); 231 assertFalse(bitmap.hasGainmap()); 232 assertNull(bitmap.getGainmap()); 233 assertSame(bitmap, BitmapFactory.decodeResource( 234 sContext.getResources(), R.raw.gainmap, options)); 235 checkGainmap(bitmap); 236 } 237 238 @Test testDecodeGainmapBitmapRegionDecoder()239 public void testDecodeGainmapBitmapRegionDecoder() throws Exception { 240 InputStream is = sContext.getResources().openRawResource(R.raw.gainmap); 241 BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is); 242 Bitmap region = decoder.decodeRegion(new Rect(0, 0, TILE_SIZE, TILE_SIZE), null); 243 checkGainmap(region); 244 } 245 246 @Test testDecodeGainmapBitmapRegionDecoderReuse()247 public void testDecodeGainmapBitmapRegionDecoderReuse() throws Exception { 248 InputStream is = sContext.getResources().openRawResource(R.raw.gainmap); 249 BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is); 250 BitmapFactory.Options options = new BitmapFactory.Options(); 251 options.inMutable = true; 252 options.inDensity = 160; 253 options.inTargetDensity = 160; 254 Bitmap region = decoder.decodeRegion(new Rect(0, 0, TILE_SIZE, TILE_SIZE), 255 options); 256 checkGainmap(region); 257 Bitmap previousGainmap = region.getGainmap().getGainmapContents(); 258 options.inBitmap = region; 259 260 is = sContext.getResources().openRawResource(R.drawable.baseline_jpeg); 261 BitmapRegionDecoder secondDecoder = BitmapRegionDecoder.newInstance(is); 262 assertSame(region, secondDecoder.decodeRegion(new Rect(0, 0, TILE_SIZE, TILE_SIZE), 263 options)); 264 assertFalse(region.hasGainmap()); 265 assertNull(region.getGainmap()); 266 267 assertSame(region, decoder.decodeRegion(new Rect(0, 0, TILE_SIZE, TILE_SIZE), 268 options)); 269 checkGainmap(region); 270 assertNotSame(previousGainmap, region.getGainmap().getGainmapContents()); 271 } 272 273 @Test testDecodeGainmapBitmapRegionDecoderReusePastBounds()274 public void testDecodeGainmapBitmapRegionDecoderReusePastBounds() throws Exception { 275 InputStream is = sContext.getResources().openRawResource(R.raw.gainmap); 276 BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is); 277 BitmapFactory.Options options = new BitmapFactory.Options(); 278 options.inMutable = true; 279 options.inDensity = 160; 280 options.inTargetDensity = 160; 281 int offsetX = decoder.getWidth() - (TILE_SIZE / 2); 282 int offsetY = decoder.getHeight() - (TILE_SIZE / 4); 283 Bitmap region = decoder.decodeRegion(new Rect(offsetX, offsetY, offsetX + TILE_SIZE, 284 offsetY + TILE_SIZE), options); 285 checkGainmap(region); 286 Bitmap gainmap = region.getGainmap().getGainmapContents(); 287 // Since there's no re-use bitmap, the resulting bitmap size will be the size of the rect 288 // that overlaps with the image. 1/2 of the X and 3/4ths of the Y are out of bounds 289 assertEquals(TILE_SIZE / 2, region.getWidth()); 290 assertEquals(TILE_SIZE / 4, region.getHeight()); 291 // The test image has a 1:1 ratio between base & gainmap 292 assertEquals(region.getWidth(), gainmap.getWidth()); 293 assertEquals(region.getHeight(), gainmap.getHeight()); 294 295 options.inBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Bitmap.Config.ARGB_8888); 296 region = decoder.decodeRegion(new Rect(offsetX, offsetY, offsetX + TILE_SIZE, 297 offsetY + TILE_SIZE), options); 298 gainmap = region.getGainmap().getGainmapContents(); 299 // Although 1/2 the X and 3/4ths the Y are out of bounds, because there's a re-use 300 // bitmap the resulting decode must exactly match the size given 301 assertEquals(TILE_SIZE, region.getWidth()); 302 assertEquals(TILE_SIZE, region.getHeight()); 303 // The test image has a 1:1 ratio between base & gainmap 304 assertEquals(region.getWidth(), gainmap.getWidth()); 305 assertEquals(region.getHeight(), gainmap.getHeight()); 306 } 307 308 @Test testDecodeGainmapBitmapRegionDecoderReuseCropped()309 public void testDecodeGainmapBitmapRegionDecoderReuseCropped() throws Exception { 310 InputStream is = sContext.getResources().openRawResource(R.raw.gainmap); 311 BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is); 312 BitmapFactory.Options options = new BitmapFactory.Options(); 313 options.inMutable = true; 314 options.inDensity = 160; 315 options.inTargetDensity = 160; 316 options.inBitmap = Bitmap.createBitmap(TILE_SIZE / 2, TILE_SIZE / 2, 317 Bitmap.Config.ARGB_8888); 318 Bitmap region = decoder.decodeRegion(new Rect(0, 0, TILE_SIZE, TILE_SIZE), 319 options); 320 checkGainmap(region); 321 Bitmap gainmap = region.getGainmap().getGainmapContents(); 322 // Although the rect was entirely in-bounds of the image, the inBitmap is 1/2th the 323 // the specified width/height so make sure the gainmap matches 324 assertEquals(TILE_SIZE / 2, region.getWidth()); 325 assertEquals(TILE_SIZE / 2, region.getHeight()); 326 // The test image has a 1:1 ratio between base & gainmap 327 assertEquals(region.getWidth(), gainmap.getWidth()); 328 assertEquals(region.getHeight(), gainmap.getHeight()); 329 } 330 331 @Test testDecodeGainmapBitmapRegionDecoderWithInSampleSize()332 public void testDecodeGainmapBitmapRegionDecoderWithInSampleSize() throws Exception { 333 // Use a quite generous threshold because we're dealing with lossy jpeg. This is still 334 // plenty sufficient to catch the difference between RED and GREEN without any risk 335 // of flaking on compression artifacts 336 final int threshold = 20; 337 338 InputStream is = sContext.getResources().openRawResource(R.raw.grid_gainmap); 339 BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is); 340 BitmapFactory.Options options = new BitmapFactory.Options(); 341 options.inMutable = true; 342 options.inDensity = 160; 343 options.inTargetDensity = 160; 344 options.inSampleSize = 4; 345 346 // The test image is a 1024x1024 grid of 4 colors each 512x512 347 // with a gainmap that's 512x512 grid of 4 colors each 256x256 348 // RED | GREEN 349 // BLUE | BLACK 350 // So by decoding the center 512x512 of the image we should still get the same set of 351 // 4 colors in the output 352 Rect subset = new Rect(256, 256, 768, 768); 353 Bitmap region = decoder.decodeRegion(subset, options); 354 assertTrue(region.hasGainmap()); 355 Bitmap gainmap = region.getGainmap().getGainmapContents(); 356 357 // sampleSize = 4 means we expect an output scaled by 1/4th 358 assertEquals(128, region.getWidth()); 359 assertEquals(128, region.getHeight()); 360 assertEquals(64, gainmap.getWidth()); 361 assertEquals(64, gainmap.getHeight()); 362 363 assertBitmapQuadColor(region, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, threshold); 364 assertBitmapQuadColor(gainmap, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, threshold); 365 } 366 367 @Test testDefaults()368 public void testDefaults() { 369 Gainmap gainmap = new Gainmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8)); 370 assertAllAre(1.0f, gainmap.getRatioMin()); 371 assertAllAre(2.f, gainmap.getRatioMax()); 372 assertAllAre(1.f, gainmap.getGamma()); 373 assertAllAre(0.f, gainmap.getEpsilonSdr()); 374 assertAllAre(0.f, gainmap.getEpsilonHdr()); 375 assertEquals(1.f, gainmap.getMinDisplayRatioForHdrTransition(), EPSILON); 376 assertEquals(2.f, gainmap.getDisplayRatioForFullHdr(), EPSILON); 377 } 378 379 @Test testSetGet()380 public void testSetGet() { 381 Gainmap gainmap = new Gainmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8)); 382 gainmap.setDisplayRatioForFullHdr(5f); 383 gainmap.setMinDisplayRatioForHdrTransition(3f); 384 gainmap.setGamma(1.1f, 1.2f, 1.3f); 385 gainmap.setRatioMin(2.1f, 2.2f, 2.3f); 386 gainmap.setRatioMax(3.1f, 3.2f, 3.3f); 387 gainmap.setEpsilonSdr(0.1f, 0.2f, 0.3f); 388 gainmap.setEpsilonHdr(0.01f, 0.02f, 0.03f); 389 390 assertEquals(5f, gainmap.getDisplayRatioForFullHdr(), EPSILON); 391 assertEquals(3f, gainmap.getMinDisplayRatioForHdrTransition(), EPSILON); 392 assertAre(1.1f, 1.2f, 1.3f, gainmap.getGamma()); 393 assertAre(2.1f, 2.2f, 2.3f, gainmap.getRatioMin()); 394 assertAre(3.1f, 3.2f, 3.3f, gainmap.getRatioMax()); 395 assertAre(0.1f, 0.2f, 0.3f, gainmap.getEpsilonSdr()); 396 assertAre(0.01f, 0.02f, 0.03f, gainmap.getEpsilonHdr()); 397 } 398 399 @Test testCopyInfo()400 public void testCopyInfo() { 401 Gainmap original = new Gainmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8)); 402 original.setDisplayRatioForFullHdr(5f); 403 original.setMinDisplayRatioForHdrTransition(3f); 404 original.setGamma(1.1f, 1.2f, 1.3f); 405 original.setRatioMin(2.1f, 2.2f, 2.3f); 406 original.setRatioMax(3.1f, 3.2f, 3.3f); 407 original.setEpsilonSdr(0.1f, 0.2f, 0.3f); 408 original.setEpsilonHdr(0.01f, 0.02f, 0.03f); 409 410 Gainmap copy = new Gainmap(original, Bitmap.createBitmap(5, 5, Bitmap.Config.ALPHA_8)); 411 assertEquals(5f, copy.getDisplayRatioForFullHdr(), EPSILON); 412 assertEquals(3f, copy.getMinDisplayRatioForHdrTransition(), EPSILON); 413 assertAre(1.1f, 1.2f, 1.3f, copy.getGamma()); 414 assertAre(2.1f, 2.2f, 2.3f, copy.getRatioMin()); 415 assertAre(3.1f, 3.2f, 3.3f, copy.getRatioMax()); 416 assertAre(0.1f, 0.2f, 0.3f, copy.getEpsilonSdr()); 417 assertAre(0.01f, 0.02f, 0.03f, copy.getEpsilonHdr()); 418 419 assertEquals(10, original.getGainmapContents().getWidth()); 420 assertEquals(5, copy.getGainmapContents().getWidth()); 421 } 422 423 @Test testWriteToParcel()424 public void testWriteToParcel() throws Exception { 425 Bitmap bitmap = ImageDecoder.decodeBitmap( 426 ImageDecoder.createSource(sContext.getResources(), R.raw.gainmap), 427 (decoder, info, source) -> decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE)); 428 assertNotNull(bitmap); 429 430 Gainmap gainmap = bitmap.getGainmap(); 431 assertNotNull(gainmap); 432 Bitmap gainmapData = gainmap.getGainmapContents(); 433 assertNotNull(gainmapData); 434 435 Parcel p = Parcel.obtain(); 436 gainmap.writeToParcel(p, 0); 437 p.setDataPosition(0); 438 439 Gainmap unparceledGainmap = Gainmap.CREATOR.createFromParcel(p); 440 assertNotNull(unparceledGainmap); 441 Bitmap unparceledGainmapData = unparceledGainmap.getGainmapContents(); 442 assertNotNull(unparceledGainmapData); 443 444 assertTrue(gainmapData.sameAs(unparceledGainmapData)); 445 assertEquals(gainmapData.getConfig(), unparceledGainmapData.getConfig()); 446 assertEquals(gainmapData.getColorSpace(), unparceledGainmapData.getColorSpace()); 447 448 assertArrayEquals(gainmap.getEpsilonSdr(), unparceledGainmap.getEpsilonSdr(), 0f); 449 assertArrayEquals(gainmap.getEpsilonHdr(), unparceledGainmap.getEpsilonHdr(), 0f); 450 assertArrayEquals(gainmap.getGamma(), unparceledGainmap.getGamma(), 0f); 451 assertEquals(gainmap.getMinDisplayRatioForHdrTransition(), 452 unparceledGainmap.getMinDisplayRatioForHdrTransition(), 0f); 453 454 assertArrayEquals(gainmap.getRatioMax(), unparceledGainmap.getRatioMax(), 0f); 455 assertArrayEquals(gainmap.getRatioMin(), unparceledGainmap.getRatioMin(), 0f); 456 assertEquals(gainmap.getDisplayRatioForFullHdr(), 457 unparceledGainmap.getDisplayRatioForFullHdr(), 0f); 458 p.recycle(); 459 } 460 461 @Test testCompress8888()462 public void testCompress8888() throws Exception { 463 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 464 assertTrue(sScalingRed8888.compress(Bitmap.CompressFormat.JPEG, 100, stream)); 465 byte[] data = stream.toByteArray(); 466 Bitmap result = ImageDecoder.decodeBitmap( 467 ImageDecoder.createSource(data), (decoder, info, src) -> { 468 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); 469 }); 470 assertTrue(result.hasGainmap()); 471 Bitmap gainmapImage = result.getGainmap().getGainmapContents(); 472 assertEquals(Bitmap.Config.ARGB_8888, gainmapImage.getConfig()); 473 Bitmap sourceImage = sScalingRed8888.getGainmap().getGainmapContents(); 474 for (int x = 0; x < 4; x++) { 475 Color expected = sourceImage.getColor(x, 0); 476 Color got = gainmapImage.getColor(x, 0); 477 assertArrayEquals("Differed at x=" + x, 478 expected.getComponents(), got.getComponents(), 0.05f); 479 } 480 } 481 482 @Test testCompressA8ByImageDecoder()483 public void testCompressA8ByImageDecoder() throws Exception { 484 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 485 assertTrue(sScalingRedA8.compress(Bitmap.CompressFormat.JPEG, 100, stream)); 486 byte[] data = stream.toByteArray(); 487 Bitmap result = ImageDecoder.decodeBitmap( 488 ImageDecoder.createSource(data), (decoder, info, src) -> { 489 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); 490 }); 491 assertTrue(result.hasGainmap()); 492 Bitmap gainmapImage = result.getGainmap().getGainmapContents(); 493 assertEquals(Bitmap.Config.ALPHA_8, gainmapImage.getConfig()); 494 Bitmap sourceImage = sScalingRedA8.getGainmap().getGainmapContents(); 495 for (int x = 0; x < 4; x++) { 496 Color expected = sourceImage.getColor(x, 0); 497 Color got = gainmapImage.getColor(x, 0); 498 assertArrayEquals("Differed at x=" + x, 499 expected.getComponents(), got.getComponents(), 0.05f); 500 } 501 } 502 503 @Test 504 @Ignore("Skip it until BitmapRegionDecoder have Alpha8 gainmap support") testCompressA8ByBitmapRegionDecoder()505 public void testCompressA8ByBitmapRegionDecoder() throws Exception { 506 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 507 assertTrue(sScalingRedA8.compress(Bitmap.CompressFormat.JPEG, 100, stream)); 508 byte[] data = stream.toByteArray(); 509 BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(data, 0, data.length); 510 Bitmap region = decoder.decodeRegion(new Rect(0, 0, 4, 1), null); 511 assertTrue(region.hasGainmap()); 512 Bitmap gainmapImage = region.getGainmap().getGainmapContents(); 513 assertEquals(Bitmap.Config.ALPHA_8, gainmapImage.getConfig()); 514 Bitmap sourceImage = sScalingRedA8.getGainmap().getGainmapContents(); 515 for (int x = 0; x < 4; x++) { 516 Color expected = sourceImage.getColor(x, 0); 517 Color got = gainmapImage.getColor(x, 0); 518 assertArrayEquals("Differed at x=" + x, 519 expected.getComponents(), got.getComponents(), 0.05f); 520 } 521 } 522 523 @Test testCompressA8ByBitmapFactory()524 public void testCompressA8ByBitmapFactory() throws Exception { 525 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 526 assertTrue(sScalingRedA8.compress(Bitmap.CompressFormat.JPEG, 100, stream)); 527 byte[] data = stream.toByteArray(); 528 Bitmap result = BitmapFactory.decodeByteArray(data, 0, data.length); 529 assertTrue(result.hasGainmap()); 530 Bitmap gainmapImage = result.getGainmap().getGainmapContents(); 531 assertEquals(Bitmap.Config.ALPHA_8, gainmapImage.getConfig()); 532 Bitmap sourceImage = sScalingRedA8.getGainmap().getGainmapContents(); 533 for (int x = 0; x < 4; x++) { 534 Color expected = sourceImage.getColor(x, 0); 535 Color got = gainmapImage.getColor(x, 0); 536 assertArrayEquals("Differed at x=" + x, 537 expected.getComponents(), got.getComponents(), 0.05f); 538 } 539 } 540 541 @Test testHardwareGainmapCopy()542 public void testHardwareGainmapCopy() throws Exception { 543 Bitmap bitmap = ImageDecoder.decodeBitmap( 544 ImageDecoder.createSource(sContext.getResources(), R.raw.gainmap), 545 (decoder, info, source) -> decoder.setAllocator(ImageDecoder.ALLOCATOR_HARDWARE)); 546 assertNotNull(bitmap); 547 assertTrue("Missing gainmap", bitmap.hasGainmap()); 548 assertEquals(Bitmap.Config.HARDWARE, bitmap.getConfig()); 549 550 Gainmap gainmap = bitmap.getGainmap(); 551 assertNotNull(gainmap); 552 Bitmap gainmapData = gainmap.getGainmapContents(); 553 assertNotNull(gainmapData); 554 assertEquals(Bitmap.Config.HARDWARE, gainmapData.getConfig()); 555 } 556 557 @Test testCopyPreservesGainmap()558 public void testCopyPreservesGainmap() throws Exception { 559 Bitmap bitmap = ImageDecoder.decodeBitmap( 560 ImageDecoder.createSource(sContext.getResources(), R.raw.gainmap), 561 (decoder, info, source) -> decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE)); 562 assertNotNull(bitmap); 563 assertTrue("Missing gainmap", bitmap.hasGainmap()); 564 565 Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true); 566 assertNotNull(copy); 567 assertTrue("Missing gainmap", copy.hasGainmap()); 568 } 569 assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, int bottomLeft, int bottomRight, int threshold)570 private static void assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, 571 int bottomLeft, int bottomRight, int threshold) { 572 Function<Float, Integer> getX = (Float x) -> (int) (bitmap.getWidth() * x); 573 Function<Float, Integer> getY = (Float y) -> (int) (bitmap.getHeight() * y); 574 575 // Just quickly sample 4 pixels in the various regions. 576 assertBitmapColor("Top left", bitmap, topLeft, 577 getX.apply(.25f), getY.apply(.25f), threshold); 578 assertBitmapColor("Top right", bitmap, topRight, 579 getX.apply(.75f), getY.apply(.25f), threshold); 580 assertBitmapColor("Bottom left", bitmap, bottomLeft, 581 getX.apply(.25f), getY.apply(.75f), threshold); 582 assertBitmapColor("Bottom right", bitmap, bottomRight, 583 getX.apply(.75f), getY.apply(.75f), threshold); 584 585 float below = .4f; 586 float above = .6f; 587 assertBitmapColor("Top left II", bitmap, topLeft, 588 getX.apply(below), getY.apply(below), threshold); 589 assertBitmapColor("Top right II", bitmap, topRight, 590 getX.apply(above), getY.apply(below), threshold); 591 assertBitmapColor("Bottom left II", bitmap, bottomLeft, 592 getX.apply(below), getY.apply(above), threshold); 593 assertBitmapColor("Bottom right II", bitmap, bottomRight, 594 getX.apply(above), getY.apply(above), threshold); 595 } 596 pixelsAreSame(int ideal, int given, int threshold)597 private static boolean pixelsAreSame(int ideal, int given, int threshold) { 598 int error = Math.abs(Color.red(ideal) - Color.red(given)); 599 error += Math.abs(Color.green(ideal) - Color.green(given)); 600 error += Math.abs(Color.blue(ideal) - Color.blue(given)); 601 return (error < threshold); 602 } 603 assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y, int threshold)604 private static void assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y, 605 int threshold) { 606 int pixel = bitmap.getPixel(x, y); 607 if (!pixelsAreSame(color, pixel, threshold)) { 608 Assert.fail(debug + "; expected=" + Integer.toHexString(color) + ", actual=" 609 + Integer.toHexString(pixel)); 610 } 611 } 612 613 } 614