Commit 977bb671 authored by danakj's avatar danakj Committed by Chromium LUCI CQ

Reland "Use skia.mojom.BitmapN32 in Detection mojoms"

This is a reland of c7326b7f

Canvases may have different color formats than N32, and when we pull
the content of them into a cpu bitmap through SkImage::toLegacyBitmap()
we need to convert that to N32 format if we're going to send the bitmap
over IPC due to security restrictions of the BitmapN32 mojom type.

This does that conversion in the blink ShapeDetector and adds a test with
a float16 color format canvas which would cause the IPC sending to be
dropped (and the renderer to crash).

TBR=dcheng

Original change's description:
> Use skia.mojom.BitmapN32 in Detection mojoms
>
> These bitmaps are safer for transport from untrustworthy sources since
> all bitmaps should be in N32 format and the browser can make bad
> assumptions as a result.
>
> R=dcheng@chromium.org, reillyg@chromium.org
>
> Bug: 1144462
> Change-Id: I7d02cb187c62be37259f445a182f8c971aa03661
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2580483
> Commit-Queue: danakj <danakj@chromium.org>
> Reviewed-by: Reilly Grant <reillyg@chromium.org>
> Reviewed-by: Daniel Cheng <dcheng@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#835685}

Bug: 1144462
Change-Id: I23ef3df85049a7111830c5d272c02b69f9d7cd38
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2593827
Commit-Queue: danakj <danakj@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#837241}
parent 9873909f
...@@ -75,8 +75,7 @@ public class BarcodeDetectionImpl implements BarcodeDetection { ...@@ -75,8 +75,7 @@ public class BarcodeDetectionImpl implements BarcodeDetection {
} }
@Override @Override
public void detect( public void detect(org.chromium.skia.mojom.BitmapN32 bitmapData, DetectResponse callback) {
org.chromium.skia.mojom.BitmapWithArbitraryBpp bitmapData, DetectResponse callback) {
// The vision library will be downloaded the first time the API is used // The vision library will be downloaded the first time the API is used
// on the device; this happens "fast", but it might have not completed, // on the device; this happens "fast", but it might have not completed,
// bail in this case. Also, the API was disabled between and v.9.0 and // bail in this case. Also, the API was disabled between and v.9.0 and
......
...@@ -9,7 +9,6 @@ import android.graphics.Bitmap; ...@@ -9,7 +9,6 @@ import android.graphics.Bitmap;
import com.google.android.gms.vision.Frame; import com.google.android.gms.vision.Frame;
import org.chromium.mojo_base.BigBufferUtil; import org.chromium.mojo_base.BigBufferUtil;
import org.chromium.skia.mojom.ColorType;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
...@@ -17,20 +16,19 @@ import java.nio.ByteBuffer; ...@@ -17,20 +16,19 @@ import java.nio.ByteBuffer;
* Utility class to convert a Bitmap to a GMS core YUV Frame. * Utility class to convert a Bitmap to a GMS core YUV Frame.
*/ */
public class BitmapUtils { public class BitmapUtils {
public static Bitmap convertToBitmap( public static Bitmap convertToBitmap(org.chromium.skia.mojom.BitmapN32 bitmapData) {
org.chromium.skia.mojom.BitmapWithArbitraryBpp bitmapData) { // A null BitmapN32 has null pixelData. Otherwise, BitmapN32 always has
if (bitmapData.imageInfo == null) return null; // a valid N32 (aka RGBA_8888 or BGRA_8888 depending on the build
// config) colorType.
if (bitmapData.pixelData == null) {
return null;
}
int width = bitmapData.imageInfo.width; int width = bitmapData.imageInfo.width;
int height = bitmapData.imageInfo.height; int height = bitmapData.imageInfo.height;
final long numPixels = (long) width * height; final long numPixels = (long) width * height;
// TODO(mcasas): https://crbug.com/670028 homogeneize overflow checking. // TODO(mcasas): https://crbug.com/670028 homogeneize overflow checking.
if (bitmapData.pixelData == null || width <= 0 || height <= 0 if (width <= 0 || height <= 0 || numPixels > (Long.MAX_VALUE / 4)) {
|| numPixels > (Long.MAX_VALUE / 4)) {
return null;
}
if (bitmapData.imageInfo.colorType != ColorType.RGBA_8888
&& bitmapData.imageInfo.colorType != ColorType.BGRA_8888) {
return null; return null;
} }
...@@ -46,7 +44,7 @@ public class BitmapUtils { ...@@ -46,7 +44,7 @@ public class BitmapUtils {
return bitmap; return bitmap;
} }
public static Frame convertToFrame(org.chromium.skia.mojom.BitmapWithArbitraryBpp bitmapData) { public static Frame convertToFrame(org.chromium.skia.mojom.BitmapN32 bitmapData) {
Bitmap bitmap = convertToBitmap(bitmapData); Bitmap bitmap = convertToBitmap(bitmapData);
if (bitmap == null) { if (bitmap == null) {
return null; return null;
......
...@@ -36,8 +36,8 @@ public class FaceDetectionImpl implements FaceDetection { ...@@ -36,8 +36,8 @@ public class FaceDetectionImpl implements FaceDetection {
} }
@Override @Override
public void detect(org.chromium.skia.mojom.BitmapWithArbitraryBpp bitmapData, public void detect(
final DetectResponse callback) { org.chromium.skia.mojom.BitmapN32 bitmapData, final DetectResponse callback) {
Bitmap bitmap = BitmapUtils.convertToBitmap(bitmapData); Bitmap bitmap = BitmapUtils.convertToBitmap(bitmapData);
if (bitmap == null) { if (bitmap == null) {
Log.e(TAG, "Error converting Mojom Bitmap to Android Bitmap"); Log.e(TAG, "Error converting Mojom Bitmap to Android Bitmap");
......
...@@ -59,8 +59,7 @@ public class FaceDetectionImplGmsCore implements FaceDetection { ...@@ -59,8 +59,7 @@ public class FaceDetectionImplGmsCore implements FaceDetection {
} }
@Override @Override
public void detect( public void detect(org.chromium.skia.mojom.BitmapN32 bitmapData, DetectResponse callback) {
org.chromium.skia.mojom.BitmapWithArbitraryBpp bitmapData, DetectResponse callback) {
// The vision library will be downloaded the first time the API is used // The vision library will be downloaded the first time the API is used
// on the device; this happens "fast", but it might have not completed, // on the device; this happens "fast", but it might have not completed,
// bail in this case. // bail in this case.
......
...@@ -34,8 +34,7 @@ public class TextDetectionImpl implements TextDetection { ...@@ -34,8 +34,7 @@ public class TextDetectionImpl implements TextDetection {
} }
@Override @Override
public void detect( public void detect(org.chromium.skia.mojom.BitmapN32 bitmapData, DetectResponse callback) {
org.chromium.skia.mojom.BitmapWithArbitraryBpp bitmapData, DetectResponse callback) {
// The vision library will be downloaded the first time the API is used // The vision library will be downloaded the first time the API is used
// on the device; this happens "fast", but it might have not completed, // on the device; this happens "fast", but it might have not completed,
// bail in this case. Also, the API was disabled between and v.9.0 and // bail in this case. Also, the API was disabled between and v.9.0 and
......
...@@ -40,7 +40,7 @@ import java.util.concurrent.TimeUnit; ...@@ -40,7 +40,7 @@ import java.util.concurrent.TimeUnit;
@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class) @UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
@DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.N_MR1, message = "crbug.com/1153716") @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.N_MR1, message = "crbug.com/1153716")
public class BarcodeDetectionImplTest { public class BarcodeDetectionImplTest {
private static final org.chromium.skia.mojom.BitmapWithArbitraryBpp QR_CODE_BITMAP = private static final org.chromium.skia.mojom.BitmapN32 QR_CODE_BITMAP =
TestUtils.mojoBitmapFromFile("qr_code.png"); TestUtils.mojoBitmapFromFile("qr_code.png");
private static final int[] SUPPORTED_FORMATS = {BarcodeFormat.AZTEC, BarcodeFormat.CODE_128, private static final int[] SUPPORTED_FORMATS = {BarcodeFormat.AZTEC, BarcodeFormat.CODE_128,
...@@ -69,14 +69,13 @@ public class BarcodeDetectionImplTest { ...@@ -69,14 +69,13 @@ public class BarcodeDetectionImplTest {
return toReturn; return toReturn;
} }
private static BarcodeDetectionResult[] detect( private static BarcodeDetectionResult[] detect(org.chromium.skia.mojom.BitmapN32 mojoBitmap) {
org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmap) {
BarcodeDetectorOptions options = new BarcodeDetectorOptions(); BarcodeDetectorOptions options = new BarcodeDetectorOptions();
return detectWithOptions(mojoBitmap, options); return detectWithOptions(mojoBitmap, options);
} }
private static BarcodeDetectionResult[] detectWithHint( private static BarcodeDetectionResult[] detectWithHint(
org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmap, int format) { org.chromium.skia.mojom.BitmapN32 mojoBitmap, int format) {
Assert.assertTrue(BarcodeFormat.isKnownValue(format)); Assert.assertTrue(BarcodeFormat.isKnownValue(format));
BarcodeDetectorOptions options = new BarcodeDetectorOptions(); BarcodeDetectorOptions options = new BarcodeDetectorOptions();
options.formats = new int[] {format}; options.formats = new int[] {format};
...@@ -84,8 +83,7 @@ public class BarcodeDetectionImplTest { ...@@ -84,8 +83,7 @@ public class BarcodeDetectionImplTest {
} }
private static BarcodeDetectionResult[] detectWithOptions( private static BarcodeDetectionResult[] detectWithOptions(
org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmap, org.chromium.skia.mojom.BitmapN32 mojoBitmap, BarcodeDetectorOptions options) {
BarcodeDetectorOptions options) {
BarcodeDetection detector = new BarcodeDetectionImpl(options); BarcodeDetection detector = new BarcodeDetectionImpl(options);
final ArrayBlockingQueue<BarcodeDetectionResult[]> queue = new ArrayBlockingQueue<>(1); final ArrayBlockingQueue<BarcodeDetectionResult[]> queue = new ArrayBlockingQueue<>(1);
...@@ -175,8 +173,7 @@ public class BarcodeDetectionImplTest { ...@@ -175,8 +173,7 @@ public class BarcodeDetectionImplTest {
if (!TestUtils.IS_GMS_CORE_SUPPORTED) { if (!TestUtils.IS_GMS_CORE_SUPPORTED) {
return; return;
} }
org.chromium.skia.mojom.BitmapWithArbitraryBpp bitmap = org.chromium.skia.mojom.BitmapN32 bitmap = TestUtils.mojoBitmapFromFile(inputFile);
TestUtils.mojoBitmapFromFile(inputFile);
BarcodeDetectionResult[] results = detectWithHint(bitmap, format); BarcodeDetectionResult[] results = detectWithHint(bitmap, format);
Assert.assertEquals(1, results.length); Assert.assertEquals(1, results.length);
Assert.assertEquals(value, results[0].rawValue); Assert.assertEquals(value, results[0].rawValue);
...@@ -196,8 +193,7 @@ public class BarcodeDetectionImplTest { ...@@ -196,8 +193,7 @@ public class BarcodeDetectionImplTest {
if (!TestUtils.IS_GMS_CORE_SUPPORTED) { if (!TestUtils.IS_GMS_CORE_SUPPORTED) {
return; return;
} }
org.chromium.skia.mojom.BitmapWithArbitraryBpp bitmap = org.chromium.skia.mojom.BitmapN32 bitmap = TestUtils.mojoBitmapFromFile(inputFile);
TestUtils.mojoBitmapFromFile(inputFile);
BarcodeDetectionResult[] results = detect(bitmap); BarcodeDetectionResult[] results = detect(bitmap);
Assert.assertEquals(1, results.length); Assert.assertEquals(1, results.length);
Assert.assertEquals(value, results[0].rawValue); Assert.assertEquals(value, results[0].rawValue);
......
...@@ -34,9 +34,9 @@ import java.util.concurrent.TimeUnit; ...@@ -34,9 +34,9 @@ import java.util.concurrent.TimeUnit;
@Batch(Batch.UNIT_TESTS) @Batch(Batch.UNIT_TESTS)
@DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.N_MR1, message = "crbug.com/1153716") @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.N_MR1, message = "crbug.com/1153716")
public class FaceDetectionImplTest { public class FaceDetectionImplTest {
private static final org.chromium.skia.mojom.BitmapWithArbitraryBpp MONA_LISA_BITMAP = private static final org.chromium.skia.mojom.BitmapN32 MONA_LISA_BITMAP =
TestUtils.mojoBitmapFromFile("mona_lisa.jpg"); TestUtils.mojoBitmapFromFile("mona_lisa.jpg");
private static final org.chromium.skia.mojom.BitmapWithArbitraryBpp FACE_POSE_BITMAP = private static final org.chromium.skia.mojom.BitmapN32 FACE_POSE_BITMAP =
TestUtils.mojoBitmapFromFile("face_pose.png"); TestUtils.mojoBitmapFromFile("face_pose.png");
// Different versions of Android have different implementations of FaceDetector.findFaces(), so // Different versions of Android have different implementations of FaceDetector.findFaces(), so
// we have to use a large error threshold. // we have to use a large error threshold.
...@@ -47,9 +47,8 @@ public class FaceDetectionImplTest { ...@@ -47,9 +47,8 @@ public class FaceDetectionImplTest {
public FaceDetectionImplTest() {} public FaceDetectionImplTest() {}
private static FaceDetectionResult[] detect( private static FaceDetectionResult[] detect(org.chromium.skia.mojom.BitmapN32 mojoBitmap,
org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmap, boolean fastMode, boolean fastMode, DetectionProviderType api) {
DetectionProviderType api) {
FaceDetectorOptions options = new FaceDetectorOptions(); FaceDetectorOptions options = new FaceDetectorOptions();
options.fastMode = fastMode; options.fastMode = fastMode;
options.maxDetectedFaces = 32; options.maxDetectedFaces = 32;
...@@ -116,8 +115,7 @@ public class FaceDetectionImplTest { ...@@ -116,8 +115,7 @@ public class FaceDetectionImplTest {
MONA_LISA_BITMAP.imageInfo.height, Bitmap.Config.ARGB_8888); MONA_LISA_BITMAP.imageInfo.height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(paddedBitmap); Canvas canvas = new Canvas(paddedBitmap);
canvas.drawBitmap(BitmapUtils.convertToBitmap(MONA_LISA_BITMAP), 0, 0, null); canvas.drawBitmap(BitmapUtils.convertToBitmap(MONA_LISA_BITMAP), 0, 0, null);
org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmap = org.chromium.skia.mojom.BitmapN32 mojoBitmap = TestUtils.mojoBitmapFromBitmap(paddedBitmap);
TestUtils.mojoBitmapFromBitmap(paddedBitmap);
Assert.assertEquals(1, mojoBitmap.imageInfo.width % 2); Assert.assertEquals(1, mojoBitmap.imageInfo.width % 2);
FaceDetectionResult[] results = detect(mojoBitmap, true, DetectionProviderType.ANDROID); FaceDetectionResult[] results = detect(mojoBitmap, true, DetectionProviderType.ANDROID);
......
...@@ -13,8 +13,7 @@ import android.graphics.Paint; ...@@ -13,8 +13,7 @@ import android.graphics.Paint;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.test.util.UrlUtils; import org.chromium.base.test.util.UrlUtils;
import org.chromium.gms.ChromiumPlayServicesAvailability; import org.chromium.gms.ChromiumPlayServicesAvailability;
import org.chromium.skia.mojom.ColorType; import org.chromium.skia.mojom.BitmapN32ImageInfo;
import org.chromium.skia.mojom.ImageInfo;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
...@@ -31,31 +30,26 @@ public class TestUtils { ...@@ -31,31 +30,26 @@ public class TestUtils {
ContextUtils.getApplicationContext()); ContextUtils.getApplicationContext());
} }
public static org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmapFromBitmap( public static org.chromium.skia.mojom.BitmapN32 mojoBitmapFromBitmap(Bitmap bitmap) {
Bitmap bitmap) {
ByteBuffer buffer = ByteBuffer.allocate(bitmap.getByteCount()); ByteBuffer buffer = ByteBuffer.allocate(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(buffer); bitmap.copyPixelsToBuffer(buffer);
org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmap = org.chromium.skia.mojom.BitmapN32 mojoBitmap = new org.chromium.skia.mojom.BitmapN32();
new org.chromium.skia.mojom.BitmapWithArbitraryBpp(); mojoBitmap.imageInfo = new BitmapN32ImageInfo();
mojoBitmap.imageInfo = new ImageInfo();
mojoBitmap.imageInfo.width = bitmap.getWidth(); mojoBitmap.imageInfo.width = bitmap.getWidth();
mojoBitmap.imageInfo.height = bitmap.getHeight(); mojoBitmap.imageInfo.height = bitmap.getHeight();
mojoBitmap.imageInfo.colorType = ColorType.RGBA_8888;
mojoBitmap.pixelData = new org.chromium.mojo_base.mojom.BigBuffer(); mojoBitmap.pixelData = new org.chromium.mojo_base.mojom.BigBuffer();
mojoBitmap.pixelData.setBytes(buffer.array()); mojoBitmap.pixelData.setBytes(buffer.array());
return mojoBitmap; return mojoBitmap;
} }
public static org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmapFromFile( public static org.chromium.skia.mojom.BitmapN32 mojoBitmapFromFile(String relPath) {
String relPath) {
String path = UrlUtils.getIsolatedTestFilePath("services/test/data/" + relPath); String path = UrlUtils.getIsolatedTestFilePath("services/test/data/" + relPath);
Bitmap bitmap = BitmapFactory.decodeFile(path); Bitmap bitmap = BitmapFactory.decodeFile(path);
return mojoBitmapFromBitmap(bitmap); return mojoBitmapFromBitmap(bitmap);
} }
public static org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmapFromText( public static org.chromium.skia.mojom.BitmapN32 mojoBitmapFromText(String[] texts) {
String[] texts) {
final int x = 10; final int x = 10;
final int baseline = 100; final int baseline = 100;
......
...@@ -34,11 +34,10 @@ public class TextDetectionImplTest { ...@@ -34,11 +34,10 @@ public class TextDetectionImplTest {
"The quick brown fox jumped over the lazy dog.", "Helvetica Neue 36."}; "The quick brown fox jumped over the lazy dog.", "Helvetica Neue 36."};
private static final float[][] TEXT_BOUNDING_BOX = { private static final float[][] TEXT_BOUNDING_BOX = {
{0.0f, 71.0f, 753.0f, 36.0f}, {4.0f, 173.0f, 307.0f, 28.0f}}; {0.0f, 71.0f, 753.0f, 36.0f}, {4.0f, 173.0f, 307.0f, 28.0f}};
private static final org.chromium.skia.mojom.BitmapWithArbitraryBpp TEXT_DETECTION_BITMAP = private static final org.chromium.skia.mojom.BitmapN32 TEXT_DETECTION_BITMAP =
TestUtils.mojoBitmapFromText(DETECTION_EXPECTED_TEXT); TestUtils.mojoBitmapFromText(DETECTION_EXPECTED_TEXT);
private static TextDetectionResult[] detect( private static TextDetectionResult[] detect(org.chromium.skia.mojom.BitmapN32 mojoBitmap) {
org.chromium.skia.mojom.BitmapWithArbitraryBpp mojoBitmap) {
TextDetection detector = new TextDetectionImpl(); TextDetection detector = new TextDetectionImpl();
final ArrayBlockingQueue<TextDetectionResult[]> queue = new ArrayBlockingQueue<>(1); final ArrayBlockingQueue<TextDetectionResult[]> queue = new ArrayBlockingQueue<>(1);
......
...@@ -16,9 +16,8 @@ import org.robolectric.shadows.ShadowLog; ...@@ -16,9 +16,8 @@ import org.robolectric.shadows.ShadowLog;
import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Feature;
import org.chromium.mojo_base.BigBufferUtil; import org.chromium.mojo_base.BigBufferUtil;
import org.chromium.skia.mojom.BitmapWithArbitraryBpp; import org.chromium.skia.mojom.BitmapN32;
import org.chromium.skia.mojom.ColorType; import org.chromium.skia.mojom.BitmapN32ImageInfo;
import org.chromium.skia.mojom.ImageInfo;
/** /**
* Test suite for conversion-to-Frame utils. * Test suite for conversion-to-Frame utils.
...@@ -46,9 +45,9 @@ public class BitmapUtilsTest { ...@@ -46,9 +45,9 @@ public class BitmapUtilsTest {
@Test @Test
@Feature({"ShapeDetection"}) @Feature({"ShapeDetection"})
public void testConversionFailsWithInvalidBitmap() { public void testConversionFailsWithInvalidBitmap() {
BitmapWithArbitraryBpp bitmap = new BitmapWithArbitraryBpp(); BitmapN32 bitmap = new BitmapN32();
bitmap.pixelData = null; bitmap.pixelData = null;
bitmap.imageInfo = new ImageInfo(); bitmap.imageInfo = new BitmapN32ImageInfo();
assertNull(BitmapUtils.convertToFrame(bitmap)); assertNull(BitmapUtils.convertToFrame(bitmap));
} }
...@@ -59,8 +58,8 @@ public class BitmapUtilsTest { ...@@ -59,8 +58,8 @@ public class BitmapUtilsTest {
@Test @Test
@Feature({"ShapeDetection"}) @Feature({"ShapeDetection"})
public void testConversionFailsWithInvalidDimensions() { public void testConversionFailsWithInvalidDimensions() {
BitmapWithArbitraryBpp bitmap = new BitmapWithArbitraryBpp(); BitmapN32 bitmap = new BitmapN32();
bitmap.imageInfo = new ImageInfo(); bitmap.imageInfo = new BitmapN32ImageInfo();
bitmap.pixelData = BigBufferUtil.createBigBufferFromBytes(EMPTY_DATA); bitmap.pixelData = BigBufferUtil.createBigBufferFromBytes(EMPTY_DATA);
bitmap.imageInfo.width = INVALID_WIDTH; bitmap.imageInfo.width = INVALID_WIDTH;
bitmap.imageInfo.height = VALID_HEIGHT; bitmap.imageInfo.height = VALID_HEIGHT;
...@@ -74,12 +73,11 @@ public class BitmapUtilsTest { ...@@ -74,12 +73,11 @@ public class BitmapUtilsTest {
@Test @Test
@Feature({"ShapeDetection"}) @Feature({"ShapeDetection"})
public void testConversionFailsWithWronglyWrappedData() { public void testConversionFailsWithWronglyWrappedData() {
BitmapWithArbitraryBpp bitmap = new BitmapWithArbitraryBpp(); BitmapN32 bitmap = new BitmapN32();
bitmap.imageInfo = new ImageInfo(); bitmap.imageInfo = new BitmapN32ImageInfo();
bitmap.pixelData = BigBufferUtil.createBigBufferFromBytes(EMPTY_DATA); bitmap.pixelData = BigBufferUtil.createBigBufferFromBytes(EMPTY_DATA);
bitmap.imageInfo.width = VALID_WIDTH; bitmap.imageInfo.width = VALID_WIDTH;
bitmap.imageInfo.height = VALID_HEIGHT; bitmap.imageInfo.height = VALID_HEIGHT;
bitmap.imageInfo.colorType = ColorType.RGBA_8888;
assertNull(BitmapUtils.convertToFrame(bitmap)); assertNull(BitmapUtils.convertToFrame(bitmap));
} }
......
...@@ -20,17 +20,11 @@ ComPtr<ISoftwareBitmap> CreateWinBitmapFromSkBitmap( ...@@ -20,17 +20,11 @@ ComPtr<ISoftwareBitmap> CreateWinBitmapFromSkBitmap(
const SkBitmap& bitmap, const SkBitmap& bitmap,
ISoftwareBitmapStatics* bitmap_factory) { ISoftwareBitmapStatics* bitmap_factory) {
DCHECK(bitmap_factory); DCHECK(bitmap_factory);
DCHECK_EQ(bitmap.colorType(), kN32_SkColorType);
if (!base::CheckedNumeric<uint32_t>(bitmap.computeByteSize()).IsValid()) { if (!base::CheckedNumeric<uint32_t>(bitmap.computeByteSize()).IsValid()) {
DLOG(ERROR) << "Data overflow."; DLOG(ERROR) << "Data overflow.";
return nullptr; return nullptr;
} }
// CreateCopyFromBuffer() assumes the pixels we pass in are 32bits each and
// are tightly packed. Receiving a bitmap of a different bits-per-pixel would
// create a buffer overflow. The `pixel_format` we use below assumes the
// format of the bitmap is N32.
CHECK_EQ(bitmap.colorType(), kN32_SkColorType);
CHECK_EQ(4, bitmap.info().bytesPerPixel());
CHECK_EQ(bitmap.rowBytes(), bitmap.width() * static_cast<size_t>(4));
// Create IBuffer from bitmap data. // Create IBuffer from bitmap data.
ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
......
...@@ -37,7 +37,6 @@ struct BarcodeDetectionResult { ...@@ -37,7 +37,6 @@ struct BarcodeDetectionResult {
}; };
interface BarcodeDetection { interface BarcodeDetection {
// |bitmap_data| contains tightly packed image pixels in row-major order. Detect(skia.mojom.BitmapN32 bitmap_data)
Detect(skia.mojom.BitmapWithArbitraryBpp bitmap_data)
=> (array<BarcodeDetectionResult> results); => (array<BarcodeDetectionResult> results);
}; };
...@@ -32,7 +32,6 @@ struct FaceDetectorOptions { ...@@ -32,7 +32,6 @@ struct FaceDetectorOptions {
}; };
interface FaceDetection { interface FaceDetection {
// |bitmap_data| contains tightly packed image pixels in row-major order. Detect(skia.mojom.BitmapN32 bitmap_data)
Detect(skia.mojom.BitmapWithArbitraryBpp bitmap_data)
=> (array<FaceDetectionResult> results); => (array<FaceDetectionResult> results);
}; };
...@@ -16,7 +16,6 @@ struct TextDetectionResult { ...@@ -16,7 +16,6 @@ struct TextDetectionResult {
}; };
interface TextDetection { interface TextDetection {
// |bitmap_data| contains tightly packed image pixels in row-major order. Detect(skia.mojom.BitmapN32 bitmap_data)
Detect(skia.mojom.BitmapWithArbitraryBpp bitmap_data)
=> (array<TextDetectionResult> results); => (array<TextDetectionResult> results);
}; };
...@@ -8,6 +8,7 @@ include_rules = [ ...@@ -8,6 +8,7 @@ include_rules = [
"+services/device/public/mojom", "+services/device/public/mojom",
"+services/network/public/cpp/shared_url_loader_factory.h", "+services/network/public/cpp/shared_url_loader_factory.h",
"+services/viz/public/mojom/hit_test/hit_test_region_list.mojom-blink.h", "+services/viz/public/mojom/hit_test/hit_test_region_list.mojom-blink.h",
"+skia/ext/skia_utils_base.h",
"+third_party/blink/public/common", "+third_party/blink/public/common",
"+third_party/blink/public/web", "+third_party/blink/public/web",
"+third_party/blink/renderer/bindings", "+third_party/blink/renderer/bindings",
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <utility> #include <utility>
#include "base/numerics/checked_math.h" #include "base/numerics/checked_math.h"
#include "skia/ext/skia_utils_base.h"
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h" #include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h"
...@@ -90,7 +91,9 @@ ScriptPromise ShapeDetector::detect( ...@@ -90,7 +91,9 @@ ScriptPromise ShapeDetector::detect(
image->PaintImageForCurrentFrame().GetSwSkImage(); image->PaintImageForCurrentFrame().GetSwSkImage();
SkBitmap sk_bitmap; SkBitmap sk_bitmap;
if (!sk_image->asLegacyBitmap(&sk_bitmap)) { SkBitmap n32_bitmap;
if (!sk_image->asLegacyBitmap(&sk_bitmap) ||
!skia::SkBitmapToN32OpaqueOrPremul(sk_bitmap, &n32_bitmap)) {
// TODO(mcasas): retrieve the pixels from elsewhere. // TODO(mcasas): retrieve the pixels from elsewhere.
NOTREACHED(); NOTREACHED();
resolver->Reject(MakeGarbageCollected<DOMException>( resolver->Reject(MakeGarbageCollected<DOMException>(
...@@ -99,7 +102,7 @@ ScriptPromise ShapeDetector::detect( ...@@ -99,7 +102,7 @@ ScriptPromise ShapeDetector::detect(
return promise; return promise;
} }
return DoDetect(resolver, std::move(sk_bitmap)); return DoDetect(resolver, std::move(n32_bitmap));
} }
ScriptPromise ShapeDetector::DetectShapesOnImageData( ScriptPromise ShapeDetector::DetectShapesOnImageData(
...@@ -121,7 +124,8 @@ ScriptPromise ShapeDetector::DetectShapesOnImageData( ...@@ -121,7 +124,8 @@ ScriptPromise ShapeDetector::DetectShapesOnImageData(
SkPixmap image_data_pixmap = image_data->GetSkPixmap(); SkPixmap image_data_pixmap = image_data->GetSkPixmap();
SkBitmap sk_bitmap; SkBitmap sk_bitmap;
if (!sk_bitmap.tryAllocPixels(image_data_pixmap.info(), if (!sk_bitmap.tryAllocPixels(
image_data_pixmap.info().makeColorType(kN32_SkColorType),
image_data_pixmap.rowBytes())) { image_data_pixmap.rowBytes())) {
resolver->Reject(MakeGarbageCollected<DOMException>( resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError, DOMExceptionCode::kInvalidStateError,
...@@ -173,7 +177,6 @@ ScriptPromise ShapeDetector::DetectShapesOnImageElement( ...@@ -173,7 +177,6 @@ ScriptPromise ShapeDetector::DetectShapesOnImageElement(
DCHECK_EQ(img->naturalHeight(), static_cast<unsigned>(sk_image->height())); DCHECK_EQ(img->naturalHeight(), static_cast<unsigned>(sk_image->height()));
SkBitmap sk_bitmap; SkBitmap sk_bitmap;
if (!sk_image || !sk_image->asLegacyBitmap(&sk_bitmap)) { if (!sk_image || !sk_image->asLegacyBitmap(&sk_bitmap)) {
resolver->Reject(MakeGarbageCollected<DOMException>( resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError, DOMExceptionCode::kInvalidStateError,
......
...@@ -11,13 +11,23 @@ const canvasElementTests = ...@@ -11,13 +11,23 @@ const canvasElementTests =
{ {
createDetector: () => { return new FaceDetector(); }, createDetector: () => { return new FaceDetector(); },
createCanvas: () => { return document.createElement("canvas"); }, createCanvas: () => { return document.createElement("canvas"); },
pixelFormat: "uint8",
mockTestName: "FaceDetectionTest", mockTestName: "FaceDetectionTest",
detectionResultTest: FaceDetectorDetectionResultTest, detectionResultTest: FaceDetectorDetectionResultTest,
name: "Face - detect(HTMLCanvasElement)" name: "Face - detect(HTMLCanvasElement)"
}, },
{
createDetector: () => { return new FaceDetector(); },
createCanvas: () => { return document.createElement("canvas"); },
pixelFormat: "float16",
mockTestName: "FaceDetectionTest",
detectionResultTest: FaceDetectorDetectionResultTest,
name: "Face - detect(HTMLCanvasElementF16Format)"
},
{ {
createDetector: () => { return new FaceDetector(); }, createDetector: () => { return new FaceDetector(); },
createCanvas: () => { return new OffscreenCanvas(300, 150); }, createCanvas: () => { return new OffscreenCanvas(300, 150); },
pixelFormat: "uint8",
mockTestName: "FaceDetectionTest", mockTestName: "FaceDetectionTest",
detectionResultTest: FaceDetectorDetectionResultTest, detectionResultTest: FaceDetectorDetectionResultTest,
name: "Face - detect(OffscreenCanvas)" name: "Face - detect(OffscreenCanvas)"
...@@ -25,13 +35,23 @@ const canvasElementTests = ...@@ -25,13 +35,23 @@ const canvasElementTests =
{ {
createDetector: () => { return new BarcodeDetector(); }, createDetector: () => { return new BarcodeDetector(); },
createCanvas: () => { return document.createElement("canvas"); }, createCanvas: () => { return document.createElement("canvas"); },
pixelFormat: "uint8",
mockTestName: "BarcodeDetectionTest", mockTestName: "BarcodeDetectionTest",
detectionResultTest: BarcodeDetectorDetectionResultTest, detectionResultTest: BarcodeDetectorDetectionResultTest,
name: "Barcode - detect(HTMLCanvasElement)" name: "Barcode - detect(HTMLCanvasElement)"
}, },
{
createDetector: () => { return new BarcodeDetector(); },
createCanvas: () => { return document.createElement("canvas"); },
pixelFormat: "float16",
mockTestName: "BarcodeDetectionTest",
detectionResultTest: BarcodeDetectorDetectionResultTest,
name: "Barcode - detect(HTMLCanvasElementF16Format)"
},
{ {
createDetector: () => { return new BarcodeDetector(); }, createDetector: () => { return new BarcodeDetector(); },
createCanvas: () => { return new OffscreenCanvas(300, 150); }, createCanvas: () => { return new OffscreenCanvas(300, 150); },
pixelFormat: "uint8",
mockTestName: "BarcodeDetectionTest", mockTestName: "BarcodeDetectionTest",
detectionResultTest: BarcodeDetectorDetectionResultTest, detectionResultTest: BarcodeDetectorDetectionResultTest,
name: "Barcode - detect(OffscreenCanvas)" name: "Barcode - detect(OffscreenCanvas)"
...@@ -39,13 +59,23 @@ const canvasElementTests = ...@@ -39,13 +59,23 @@ const canvasElementTests =
{ {
createDetector: () => { return new TextDetector(); }, createDetector: () => { return new TextDetector(); },
createCanvas: () => { return document.createElement("canvas"); }, createCanvas: () => { return document.createElement("canvas"); },
pixelFormat: "uint8",
mockTestName: "TextDetectionTest", mockTestName: "TextDetectionTest",
detectionResultTest: TextDetectorDetectionResultTest, detectionResultTest: TextDetectorDetectionResultTest,
name: "Text - detect(HTMLCanvasElement)" name: "Text - detect(HTMLCanvasElement)"
}, },
{
createDetector: () => { return new TextDetector(); },
createCanvas: () => { return document.createElement("canvas"); },
pixelFormat: "float16",
mockTestName: "TextDetectionTest",
detectionResultTest: TextDetectorDetectionResultTest,
name: "Text - detect(HTMLCanvasElementF16Format)"
},
{ {
createDetector: () => { return new TextDetector(); }, createDetector: () => { return new TextDetector(); },
createCanvas: () => { return new OffscreenCanvas(300, 150); }, createCanvas: () => { return new OffscreenCanvas(300, 150); },
pixelFormat: "uint8",
mockTestName: "TextDetectionTest", mockTestName: "TextDetectionTest",
detectionResultTest: TextDetectorDetectionResultTest, detectionResultTest: TextDetectorDetectionResultTest,
name: "Text - detect(OffscreenCanvas)" name: "Text - detect(OffscreenCanvas)"
...@@ -59,7 +89,9 @@ for (let canvasElementTest of canvasElementTests) { ...@@ -59,7 +89,9 @@ for (let canvasElementTest of canvasElementTests) {
img.src = "/images/green-16x16.png"; img.src = "/images/green-16x16.png";
await imgWatcher.wait_for("load"); await imgWatcher.wait_for("load");
const canvas = canvasElementTest.createCanvas(); const canvas = canvasElementTest.createCanvas();
canvas.getContext("2d").drawImage(img, 0, 0); canvas.getContext(
"2d", { pixelFormat: canvasElementTest.pixelFormat }).drawImage(
img, 0, 0);
const detector = canvasElementTest.createDetector(); const detector = canvasElementTest.createDetector();
const detectionResult = await detector.detect(canvas); const detectionResult = await detector.detect(canvas);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment