1 /* 2 * Copyright (C) 2010 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 package android.graphics.cts; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertNotNull; 20 import static org.junit.Assert.assertTrue; 21 import static org.junit.Assert.fail; 22 23 import android.content.res.Resources; 24 import android.graphics.Bitmap; 25 import android.graphics.BitmapFactory; 26 import android.graphics.Canvas; 27 import android.graphics.Color; 28 import android.graphics.ColorSpace; 29 import android.graphics.ImageFormat; 30 import android.graphics.Rect; 31 import android.graphics.YuvImage; 32 import android.platform.test.annotations.RequiresFlagsEnabled; 33 import android.media.ExifInterface; 34 import android.util.Log; 35 36 import androidx.test.InstrumentationRegistry; 37 import androidx.test.filters.SmallTest; 38 import androidx.test.runner.AndroidJUnit4; 39 40 import com.android.compatibility.common.util.ApiTest; 41 import com.android.compatibility.common.util.BitmapUtils; 42 import com.android.graphics.flags.Flags; 43 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 47 import java.io.ByteArrayInputStream; 48 import java.io.ByteArrayOutputStream; 49 import java.io.IOException; 50 import java.nio.file.Files; 51 import java.nio.file.Paths; 52 import java.util.Arrays; 53 54 @SmallTest 55 @RunWith(AndroidJUnit4.class) 56 public class YuvImageTest { 57 // Coefficients are taken from jcolor.c in libjpeg. 58 private static final int CSHIFT = 16; 59 private static final int CYR = 19595; 60 private static final int CYG = 38470; 61 private static final int CYB = 7471; 62 private static final int CUR = -11059; 63 private static final int CUG = -21709; 64 private static final int CUB = 32768; 65 private static final int CVR = 32768; 66 private static final int CVG = -27439; 67 private static final int CVB = -5329; 68 69 private static final String TAG = "YuvImageTest"; 70 71 private static final int[] FORMATS = { ImageFormat.NV21, ImageFormat.YUY2, 72 ImageFormat.YCBCR_P010, ImageFormat.YUV_420_888 }; 73 private static final int[] JPEG_FORMATS = { ImageFormat.NV21, ImageFormat.YUY2 }; 74 75 private static final int WIDTH = 256; 76 private static final int HEIGHT = 128; 77 78 private static final int[] RECT_WIDTHS = { 128, 124, 123 }; 79 private static final int[] RECT_HEIGHTS = { 64, 60, 59 }; 80 81 // Various rectangles: 82 // mRects[0] : a normal one. 83 // mRects[1] : same size to that of mRects[1], but its left-top point is shifted 84 // mRects[2] : sides are not multiples of 16 85 // mRects[3] : the left-top point is at an odd position 86 private static final Rect[] RECTS = { new Rect(0, 0, 0 + RECT_WIDTHS[0], 0 + RECT_HEIGHTS[0]), 87 new Rect(10, 10, 10 + RECT_WIDTHS[0], 10 + RECT_HEIGHTS[0]), 88 new Rect(0, 0, 0 + RECT_WIDTHS[1], 0 + RECT_HEIGHTS[1]), 89 new Rect(11, 11, 11 + RECT_WIDTHS[1], 11 + RECT_HEIGHTS[1]) }; 90 91 // Two rectangles of same size but at different positions 92 private static final Rect[] RECTS_SHIFTED = { RECTS[0], RECTS[1] }; 93 94 // A rect whose side lengths are odd. 95 private static final Rect RECT_ODD_SIDES = new Rect(10, 10, 10 + RECT_WIDTHS[2], 96 10 + RECT_HEIGHTS[2]); 97 98 private static final int[] PADDINGS = { 0, 32 }; 99 100 // There are three color components and 101 // each should be within a square difference of 15 * 15. 102 private static final int MSE_MARGIN = 3 * (15 * 15); 103 104 private Bitmap[] mTestBitmaps = new Bitmap[1]; 105 106 @Test testYuvImage()107 public void testYuvImage() { 108 int width = 100; 109 int height = 100; 110 byte[] yuv = new byte[width * height * 2]; 111 YuvImage image; 112 113 // normal case: test that the required formats are all supported 114 for (int i = 0; i < FORMATS.length; ++i) { 115 try { 116 new YuvImage(yuv, FORMATS[i], width, height, null); 117 } catch (Exception e) { 118 Log.e(TAG, "unexpected exception", e); 119 fail("unexpected exception"); 120 } 121 } 122 123 // normal case: test that default strides are returned correctly 124 for (int i = 0; i < FORMATS.length; ++i) { 125 int[] expected = null; 126 int[] actual = null; 127 switch (FORMATS[i]) { 128 case ImageFormat.NV21: 129 expected = new int[]{width, width}; 130 break; 131 case ImageFormat.YCBCR_P010: 132 expected = new int[]{width * 2, width * 2}; 133 break; 134 case ImageFormat.YUV_420_888: 135 expected = new int[]{width, (width + 1) / 2, (width + 1) / 2}; 136 break; 137 case ImageFormat.YUY2: 138 expected = new int[]{width * 2}; 139 break; 140 default: 141 // We shouldn't hit here. 142 } 143 144 try { 145 image = new YuvImage(yuv, FORMATS[i], width, height, null); 146 actual = image.getStrides(); 147 assertTrue("default strides not calculated correctly", 148 Arrays.equals(expected, actual)); 149 } catch (Exception e) { 150 Log.e(TAG, "unexpected exception", e); 151 fail("unexpected exception"); 152 } 153 } 154 155 // abnormal case: pass a null ColorSpace, should throw IllegalArgumentException 156 try{ 157 image = new YuvImage(yuv, FORMATS[0], width, height, null, null); 158 fail("not catching hdr empty"); 159 } catch (IllegalArgumentException e){ 160 // expected 161 } 162 } 163 164 @Test(expected=IllegalArgumentException.class) testYuvImageNegativeWidth()165 public void testYuvImageNegativeWidth(){ 166 new YuvImage(new byte[100 * 100 * 2], FORMATS[0], -1, 100, null); 167 } 168 169 @Test(expected=IllegalArgumentException.class) testYuvImageNegativeHeight()170 public void testYuvImageNegativeHeight(){ 171 new YuvImage(new byte[100 * 100 * 2], FORMATS[0], 100, -1, null); 172 } 173 174 @Test(expected=IllegalArgumentException.class) testYuvImageNullArray()175 public void testYuvImageNullArray(){ 176 new YuvImage(null, FORMATS[0], 100, 100, null); 177 } 178 179 @Test testCompressYuvToJpeg()180 public void testCompressYuvToJpeg() { 181 generateTestBitmaps(WIDTH, HEIGHT); 182 183 // test if handling compression parameters correctly 184 verifyParameters(); 185 186 // test various cases by varying 187 // <ImageFormat, Bitmap, HasPaddings, Rect> 188 for (int i = 0; i < JPEG_FORMATS.length; ++i) { 189 for (int j = 0; j < mTestBitmaps.length; ++j) { 190 for (int k = 0; k < PADDINGS.length; ++k) { 191 YuvImage image = generateYuvImage(JPEG_FORMATS[i], 192 mTestBitmaps[j], PADDINGS[k], null); 193 for (int l = 0; l < RECTS.length; ++l) { 194 195 // test compressing the same rect in 196 // mTestBitmaps[j] and image. 197 compressRects(mTestBitmaps[j], image, 198 RECTS[l], RECTS[l]); 199 } 200 201 // test compressing different rects in 202 // mTestBitmap[j] and image. 203 compressRects(mTestBitmaps[j], image, RECTS_SHIFTED[0], 204 RECTS_SHIFTED[1]); 205 206 // test compressing a rect whose side lengths are odd. 207 compressOddRect(mTestBitmaps[j], image, RECT_ODD_SIDES); 208 } 209 } 210 } 211 212 } 213 214 @Test testGetHeight()215 public void testGetHeight() { 216 generateTestBitmaps(WIDTH, HEIGHT); 217 YuvImage image = generateYuvImage(ImageFormat.YUY2, mTestBitmaps[0], 0, null); 218 assertEquals(mTestBitmaps[0].getHeight(), image.getHeight()); 219 assertEquals(mTestBitmaps[0].getWidth(), image.getWidth()); 220 } 221 222 @Test testGetColorSpace()223 public void testGetColorSpace() { 224 generateTestBitmaps(WIDTH, HEIGHT); 225 ColorSpace defaultColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); 226 ColorSpace expectedColorSpace = ColorSpace.get(ColorSpace.Named.BT709); 227 YuvImage imageSRGB = generateYuvImage(ImageFormat.YUY2, mTestBitmaps[0], 0, null); 228 YuvImage imageBT709 = generateYuvImage( 229 ImageFormat.YUY2, mTestBitmaps[0], 0, expectedColorSpace); 230 assertEquals(defaultColorSpace, imageSRGB.getColorSpace()); 231 assertEquals(expectedColorSpace, imageBT709.getColorSpace()); 232 } 233 234 @Test testGetYuvData()235 public void testGetYuvData() { 236 generateTestBitmaps(WIDTH, HEIGHT); 237 int width = mTestBitmaps[0].getWidth(); 238 int height = mTestBitmaps[0].getHeight(); 239 int stride = width; 240 int[] argb = new int[stride * height]; 241 mTestBitmaps[0].getPixels(argb, 0, stride, 0, 0, width, height); 242 byte[] yuv = convertArgbsToYuvs(argb, stride, height, ImageFormat.NV21); 243 int[] strides = new int[] { 244 stride, stride 245 }; 246 YuvImage image = new YuvImage(yuv, ImageFormat.NV21, width, height, strides); 247 assertEquals(yuv, image.getYuvData()); 248 } 249 250 @Test testGetYuvFormat()251 public void testGetYuvFormat() { 252 generateTestBitmaps(WIDTH, HEIGHT); 253 YuvImage image = generateYuvImage(ImageFormat.YUY2, mTestBitmaps[0], 0, null); 254 assertEquals(ImageFormat.YUY2, image.getYuvFormat()); 255 } 256 257 @Test testCompressYuvToJpegRWithBadInputs()258 public void testCompressYuvToJpegRWithBadInputs() { 259 String hdrInput = Utils.obtainPath(R.raw.raw_p010_image, 0); 260 String sdrInput = Utils.obtainPath(R.raw.raw_yuv420_image, 0); 261 262 final int width = 1280; 263 final int height = 720; 264 265 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 266 267 try { 268 byte[] emptyData = new byte[0]; 269 byte[] hdrData = Files.readAllBytes(Paths.get(hdrInput)); 270 byte[] sdrData = Files.readAllBytes(Paths.get(sdrInput)); 271 272 YuvImage emptyHdr = new YuvImage( 273 emptyData, ImageFormat.YCBCR_P010, width, height, null); 274 YuvImage emptySdr = new YuvImage( 275 emptyData, ImageFormat.YUV_420_888, width, height, null); 276 YuvImage supportedHdr = new YuvImage( 277 hdrData, ImageFormat.YCBCR_P010, width, height, null); 278 YuvImage supportedSdr = new YuvImage( 279 sdrData, ImageFormat.YUV_420_888, width, height, null); 280 YuvImage unsupportedHdr = new YuvImage( 281 hdrData, ImageFormat.YUV_420_888, width, height, null); 282 YuvImage unsupportedSdr = new YuvImage( 283 sdrData, ImageFormat.NV21, width, height, null); 284 YuvImage sdrWithDifferentRes = new YuvImage( 285 sdrData, ImageFormat.YUV_420_888, 720, 480, null); 286 YuvImage hdrWithNotSupportedColorSpace = new YuvImage( 287 hdrData, ImageFormat.YCBCR_P010, 720, 480, null, 288 ColorSpace.get(ColorSpace.Named.CIE_LAB)); 289 YuvImage sdrWithNotSupportedColorSpace = new YuvImage( 290 sdrData, ImageFormat.YUV_420_888, 720, 480, null, 291 ColorSpace.get(ColorSpace.Named.BT709)); 292 293 // abnormal case: hdr is empty 294 try{ 295 emptyHdr.compressToJpegR(supportedSdr, 90, stream); 296 fail("not catching hdr empty"); 297 } catch (IllegalArgumentException e){ 298 // expected 299 } 300 301 // abnormal case: sdr is empty 302 try{ 303 supportedHdr.compressToJpegR(emptySdr, 90, stream); 304 fail("not catching sdr empty"); 305 } catch (IllegalArgumentException e){ 306 // expected 307 } 308 309 // abnormal case: sdr is unsupported color format 310 try{ 311 supportedHdr.compressToJpegR(unsupportedSdr, 90, stream); 312 fail("not catching sdr is unsupported color format"); 313 } catch (IllegalArgumentException e){ 314 // expected 315 } 316 317 // abnormal case: sdr is null 318 try{ 319 supportedHdr.compressToJpegR(null, 90, stream); 320 fail("not catching sdr is null"); 321 } catch (IllegalArgumentException e){ 322 // expected 323 } 324 325 // abnormal case: quality < 0 326 try{ 327 supportedHdr.compressToJpegR(supportedSdr, -1, stream); 328 fail("not catching illegal compression quality"); 329 } catch (IllegalArgumentException e){ 330 // expected 331 } 332 333 // abnormal case: quality > 100 334 try{ 335 supportedHdr.compressToJpegR(supportedSdr, 101, stream); 336 fail("not catching illegal compression quality"); 337 } catch (IllegalArgumentException e){ 338 // expected 339 } 340 341 // abnormal case: stream is null 342 try{ 343 supportedHdr.compressToJpegR(supportedSdr, 90, null); 344 fail("not catching null stream"); 345 } catch (IllegalArgumentException e){ 346 // expected 347 } 348 349 // abnormal case: resolution mismatch 350 try{ 351 supportedHdr.compressToJpegR(sdrWithDifferentRes, 90, stream); 352 fail("not catching resolution mismatch"); 353 } catch (IllegalArgumentException e){ 354 // expected 355 } 356 357 // abnormal case: not supported color space 358 try{ 359 supportedHdr.compressToJpegR(sdrWithNotSupportedColorSpace, 90, stream); 360 fail("not catching resolution mismatch"); 361 } catch (IllegalArgumentException e){ 362 // expected 363 } 364 try{ 365 hdrWithNotSupportedColorSpace.compressToJpegR(supportedSdr, 90, stream); 366 fail("not catching resolution mismatch"); 367 } catch (IllegalArgumentException e){ 368 // expected 369 } 370 } catch (Exception e) { 371 Log.e(TAG, "unexpected exception", e); 372 fail("unexpected exception"); 373 } 374 } 375 376 @Test testCompressYuvToJpegR()377 public void testCompressYuvToJpegR() { 378 String hdrInput = Utils.obtainPath(R.raw.raw_p010_image, 0); 379 String sdrInput = Utils.obtainPath(R.raw.raw_yuv420_image, 0); 380 381 final int width = 1280; 382 final int height = 720; 383 384 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 385 386 try { 387 byte[] hdrData = Files.readAllBytes(Paths.get(hdrInput)); 388 byte[] sdrData = Files.readAllBytes(Paths.get(sdrInput)); 389 390 YuvImage hdrImage = new YuvImage(hdrData, ImageFormat.YCBCR_P010, 391 width, height, null, 392 ColorSpace.get(ColorSpace.Named.BT2020_HLG)); 393 YuvImage sdrImage = new YuvImage(sdrData, ImageFormat.YUV_420_888, 394 width, height, null); 395 396 assertTrue("Fail in JPEG/R compression.", 397 hdrImage.compressToJpegR(sdrImage, 90, stream)); 398 byte[] jpegRData = stream.toByteArray(); 399 Bitmap decoded = BitmapFactory.decodeByteArray(jpegRData, 0, jpegRData.length); 400 assertNotNull(decoded); 401 } catch (Exception e) { 402 Log.e(TAG, "unexpected exception", e); 403 fail("unexpected exception"); 404 } 405 } 406 407 @ApiTest(apis = {"android.graphics.YuvImage#compressToJpegR"}) 408 @RequiresFlagsEnabled(Flags.FLAG_YUV_IMAGE_COMPRESS_TO_ULTRA_HDR) 409 @Test testCompressYuvToJpegRWithExif()410 public void testCompressYuvToJpegRWithExif() { 411 String hdrInput = Utils.obtainPath(R.raw.raw_p010_image, 0); 412 String sdrInput = Utils.obtainPath(R.raw.raw_yuv420_image, 0); 413 String exifInput = Utils.obtainPath(R.raw.sample_exif, 0); 414 415 final int width = 1280; 416 final int height = 720; 417 418 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 419 420 try { 421 byte[] hdrData = Files.readAllBytes(Paths.get(hdrInput)); 422 byte[] sdrData = Files.readAllBytes(Paths.get(sdrInput)); 423 byte[] exifData = Files.readAllBytes(Paths.get(exifInput)); 424 425 YuvImage hdrImage = new YuvImage(hdrData, ImageFormat.YCBCR_P010, 426 width, height, null, 427 ColorSpace.get(ColorSpace.Named.BT2020_HLG)); 428 YuvImage sdrImage = new YuvImage(sdrData, ImageFormat.YUV_420_888, 429 width, height, null); 430 431 assertTrue("Fail in JPEG/R compression.", 432 hdrImage.compressToJpegR(sdrImage, 90, stream, exifData)); 433 byte[] jpegRData = stream.toByteArray(); 434 Bitmap decoded = BitmapFactory.decodeByteArray(jpegRData, 0, jpegRData.length); 435 assertNotNull(decoded); 436 437 ExifInterface exifInterface = new ExifInterface( 438 new ByteArrayInputStream(stream.toByteArray())); 439 assertEquals(exifInterface.getAttribute(ExifInterface.TAG_IMAGE_WIDTH), 440 String.valueOf(width)); 441 assertEquals(exifInterface.getAttribute(ExifInterface.TAG_IMAGE_LENGTH), 442 String.valueOf(height)); 443 assertEquals(exifInterface.getAttribute(ExifInterface.TAG_DATETIME), 444 "2022:11:22 23:05:32"); 445 assertEquals(exifInterface.getAttribute(ExifInterface.TAG_MAKE), 446 "Google"); 447 assertEquals(exifInterface.getAttribute(ExifInterface.TAG_MODEL), 448 "Cuttlefish GMS x86_64"); 449 } catch (Exception e) { 450 Log.e(TAG, "unexpected exception", e); 451 fail("unexpected exception"); 452 } 453 } 454 455 @Test testCompressYuvToJpegRWithStrides()456 public void testCompressYuvToJpegRWithStrides() throws Exception { 457 String hdrInput = Utils.obtainPath(R.raw.raw_p010_image, 0); 458 String sdrInput = Utils.obtainPath(R.raw.raw_yuv420_image, 0); 459 460 final int stride = 1280; 461 final int padding = 20; 462 final int width = stride - padding; 463 final int height = 720; 464 465 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 466 467 byte[] hdrData = Files.readAllBytes(Paths.get(hdrInput)); 468 byte[] sdrData = Files.readAllBytes(Paths.get(sdrInput)); 469 470 YuvImage hdrImage = new YuvImage(hdrData, ImageFormat.YCBCR_P010, 471 width, height, new int[] {stride * 2, stride * 2}, 472 ColorSpace.get(ColorSpace.Named.BT2020_HLG)); 473 YuvImage sdrImage = new YuvImage(sdrData, ImageFormat.YUV_420_888, 474 width, height, new int[] {stride, stride / 2, stride / 2}); 475 476 assertTrue("Fail in JPEG/R compression.", 477 hdrImage.compressToJpegR(sdrImage, 90, stream)); 478 byte[] jpegRData = stream.toByteArray(); 479 Bitmap decoded = BitmapFactory.decodeByteArray(jpegRData, 0, jpegRData.length); 480 assertNotNull(decoded); 481 assertEquals(decoded.getWidth(), width); 482 } 483 generateTestBitmaps(int width, int height)484 private void generateTestBitmaps(int width, int height) { 485 Bitmap dst = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 486 Canvas c = new Canvas(dst); 487 488 // mTestBitmap[0] = scaled testimage.jpg 489 Resources res = InstrumentationRegistry.getTargetContext().getResources(); 490 Bitmap src = BitmapFactory.decodeResource(res, R.drawable.testimage); 491 c.drawBitmap(src, null, new Rect(0, 0, WIDTH, HEIGHT), null); 492 mTestBitmaps[0] = dst; 493 } 494 495 // Generate YuvImage based on the content in bitmap. If paddings > 0, the 496 // strides of YuvImage are calculated by adding paddings to bitmap.getWidth(). 497 // If colorSpace is null, this method will return a YuvImage with the default 498 // ColorSpace (SRGB). Otherwise, it will return a YuvImage with configured ColorSpace. generateYuvImage(int format, Bitmap bitmap, int paddings, ColorSpace colorSpace)499 private YuvImage generateYuvImage(int format, Bitmap bitmap, int paddings, 500 ColorSpace colorSpace) { 501 int width = bitmap.getWidth(); 502 int height = bitmap.getHeight(); 503 504 int stride = width + paddings; 505 506 YuvImage image = null; 507 int[] argb = new int [stride * height]; 508 bitmap.getPixels(argb, 0, stride, 0, 0, width, height); 509 byte[] yuv = convertArgbsToYuvs(argb, stride, height, format); 510 511 int[] strides = null; 512 if (format == ImageFormat.NV21) { 513 strides = new int[] {stride, stride}; 514 } else if (format == ImageFormat.YUY2) { 515 strides = new int[] {stride * 2}; 516 } 517 518 if (colorSpace != null) { 519 image = new YuvImage(yuv, format, width, height, strides, colorSpace); 520 } else { 521 image = new YuvImage(yuv, format, width, height, strides); 522 } 523 return image; 524 } 525 526 // Compress rect1 in testBitmap and rect2 in image. 527 // Then, compare the two results to check their MSE. compressRects(Bitmap testBitmap, YuvImage image, Rect rect1, Rect rect2)528 private void compressRects(Bitmap testBitmap, YuvImage image, 529 Rect rect1, Rect rect2) { 530 Bitmap expected = null; 531 Bitmap actual = null; 532 boolean sameRect = rect1.equals(rect2) ? true : false; 533 534 Rect actualRect = new Rect(rect2); 535 actual = compressDecompress(image, actualRect); 536 537 Rect expectedRect = sameRect ? actualRect : rect1; 538 expected = Bitmap.createBitmap(testBitmap, expectedRect.left, expectedRect.top, 539 expectedRect.width(), expectedRect.height()); 540 BitmapUtils.assertBitmapsMse(expected, actual, MSE_MARGIN, sameRect, false); 541 } 542 543 // Compress rect in image. 544 // If side lengths of rect are odd, the rect might be modified by image, 545 // We use the modified one to get pixels from testBitmap. compressOddRect(Bitmap testBitmap, YuvImage image, Rect rect)546 private void compressOddRect(Bitmap testBitmap, YuvImage image, 547 Rect rect) { 548 Bitmap expected = null; 549 Bitmap actual = null; 550 actual = compressDecompress(image, rect); 551 552 Rect newRect = rect; 553 expected = Bitmap.createBitmap(testBitmap, newRect.left, newRect.top, 554 newRect.width(), newRect.height()); 555 556 BitmapUtils.assertBitmapsMse(expected, actual, MSE_MARGIN, true, false); 557 } 558 559 // Compress rect in image to a jpeg and then decode the jpeg to a bitmap. compressDecompress(YuvImage image, Rect rect)560 private Bitmap compressDecompress(YuvImage image, Rect rect) { 561 Bitmap result = null; 562 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 563 try { 564 boolean rt = image.compressToJpeg(rect, 90, stream); 565 assertTrue("fail in compression", rt); 566 byte[] jpegData = stream.toByteArray(); 567 result = BitmapFactory.decodeByteArray(jpegData, 0, 568 jpegData.length); 569 } catch (Exception e){ 570 Log.e(TAG, "unexpected exception", e); 571 fail("unexpected exception"); 572 } 573 return result; 574 } 575 convertArgbsToYuvs(int[] argb, int width, int height, int format)576 private byte[] convertArgbsToYuvs(int[] argb, int width, int height, 577 int format) { 578 byte[] yuv = new byte[width * height * 579 ImageFormat.getBitsPerPixel(format)]; 580 if (format == ImageFormat.NV21) { 581 int vuStart = width * height; 582 byte[] yuvColor = new byte[3]; 583 for (int row = 0; row < height; ++row) { 584 for (int col = 0; col < width; ++col) { 585 int idx = row * width + col; 586 argb2yuv(argb[idx], yuvColor); 587 yuv[idx] = yuvColor[0]; 588 if ((row & 1) == 0 && (col & 1) == 0) { 589 int offset = row / 2 * width + col / 2 * 2; 590 yuv[vuStart + offset] = yuvColor[2]; 591 yuv[vuStart + offset + 1] = yuvColor[1]; 592 } 593 } 594 } 595 } else if (format == ImageFormat.YUY2) { 596 byte[] yuvColor0 = new byte[3]; 597 byte[] yuvColor1 = new byte[3]; 598 for (int row = 0; row < height; ++row) { 599 for (int col = 0; col < width; col += 2) { 600 int idx = row * width + col; 601 argb2yuv(argb[idx], yuvColor0); 602 argb2yuv(argb[idx + 1], yuvColor1); 603 int offset = idx / 2 * 4; 604 yuv[offset] = yuvColor0[0]; 605 yuv[offset + 1] = yuvColor0[1]; 606 yuv[offset + 2] = yuvColor1[0]; 607 yuv[offset + 3] = yuvColor0[2]; 608 } 609 } 610 } 611 612 return yuv; 613 } 614 argb2yuv(int argb, byte[] yuv)615 private void argb2yuv(int argb, byte[] yuv) { 616 int r = Color.red(argb); 617 int g = Color.green(argb); 618 int b = Color.blue(argb); 619 yuv[0] = (byte) ((CYR * r + CYG * g + CYB * b) >> CSHIFT); 620 yuv[1] = (byte) (((CUR * r + CUG * g + CUB * b) >> CSHIFT) + 128); 621 yuv[2] = (byte) (((CVR * r + CVG * g + CVB * b) >> CSHIFT) + 128); 622 } 623 verifyParameters()624 private void verifyParameters() { 625 int format = ImageFormat.NV21; 626 int[] argb = new int[WIDTH * HEIGHT]; 627 mTestBitmaps[0].getPixels(argb, 0, WIDTH, 0, 0, WIDTH, HEIGHT); 628 byte[] yuv = convertArgbsToYuvs(argb, WIDTH, HEIGHT, format); 629 630 YuvImage image = new YuvImage(yuv, format, WIDTH, HEIGHT, null); 631 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 632 633 // abnormal case: quality > 100 634 try{ 635 Rect rect = new Rect(0, 0, WIDTH, HEIGHT); 636 image.compressToJpeg(rect, 101, stream); 637 fail("not catching illegal compression quality"); 638 } catch (IllegalArgumentException e){ 639 // expected 640 } 641 642 // abnormal case: quality < 0 643 try{ 644 Rect rect = new Rect(0, 0, WIDTH, HEIGHT); 645 image.compressToJpeg(rect, -1, stream); 646 fail("not catching illegal compression quality"); 647 } catch (IllegalArgumentException e){ 648 // expected 649 } 650 651 // abnormal case: stream is null 652 try { 653 Rect rect = new Rect(0, 0, WIDTH, HEIGHT); 654 image.compressToJpeg(rect, 80, null); 655 fail("not catching null stream"); 656 } catch (IllegalArgumentException e){ 657 // expected 658 } 659 660 // abnormal case: rectangle not within the whole image 661 try { 662 Rect rect = new Rect(10, 0, WIDTH, HEIGHT + 5); 663 image.compressToJpeg(rect, 80, stream); 664 fail("not catching illegal rectangular region"); 665 } catch (IllegalArgumentException e){ 666 // expected 667 } 668 669 // abnormal case: unsupported color space 670 try { 671 Rect rect = new Rect(0, 0, WIDTH, HEIGHT); 672 YuvImage image2 = new YuvImage(yuv, format, WIDTH, HEIGHT, null, 673 ColorSpace.get(ColorSpace.Named.BT709)); 674 image2.compressToJpeg(rect, 80, stream); 675 fail("not catching illegal color Space"); 676 } catch (IllegalArgumentException e){ 677 // expected 678 } 679 } 680 } 681