Fix an OOM issue in mosaic blending.
Set two limits on when we will do mosaic blending.
1) mosaic_width * mosaic_height < (5 * frame_width) * (2 * frame_height)
2) mosaic_height < 2.5 * frame_height
The latter limit rejects blending for the cases having too much movement
in the secondary direction.
Bug: 5554762
Change-Id: I05fb24bb1ff5446bea0cc1a1de8a02c37fb642d7
diff --git a/jni/feature_mos/src/mosaic/Blend.cpp b/jni/feature_mos/src/mosaic/Blend.cpp
index 18972ee..cce89ff 100644
--- a/jni/feature_mos/src/mosaic/Blend.cpp
+++ b/jni/feature_mos/src/mosaic/Blend.cpp
@@ -226,14 +226,25 @@
yTopMost = max(0, max(yTopCorners[0], yTopCorners[1]) - fullRect.top + 1);
yBottomMost = min(Mheight - 1, min(yBottomCorners[0], yBottomCorners[1]) - fullRect.top - 1);
+ if (xRightMost <= xLeftMost || yBottomMost <= yTopMost)
+ {
+ LOGE("RunBlend: aborting -consistency check failed,"
+ "(xLeftMost, xRightMost, yTopMost, yBottomMost): (%d, %d, %d, %d)",
+ xLeftMost, xRightMost, yTopMost, yBottomMost);
+ return BLEND_RET_ERROR;
+ }
+
// Make sure image width is multiple of 4
Mwidth = (unsigned short) ((Mwidth + 3) & ~3);
Mheight = (unsigned short) ((Mheight + 3) & ~3); // Round up.
- if (Mwidth < width || Mheight < height || xRightMost <= xLeftMost)
+ ret = MosaicSizeCheck(LIMIT_SIZE_MULTIPLIER, LIMIT_HEIGHT_MULTIPLIER);
+ if (ret != BLEND_RET_OK)
{
- LOGE("RunBlend: aborting - consistency check failed, w=%d, h=%d, xLeftMost=%d, xRightMost=%d", Mwidth, Mheight, xLeftMost, xRightMost);
- return BLEND_RET_ERROR;
+ LOGE("RunBlend: aborting - mosaic size check failed, "
+ "(frame_width, frame_height) vs (mosaic_width, mosaic_height): "
+ "(%d, %d) vs (%d, %d)", width, height, Mwidth, Mheight);
+ return ret;
}
LOGI("Allocate mosaic image for blending - size: %d x %d", Mwidth, Mheight);
@@ -296,6 +307,26 @@
return ret;
}
+int Blend::MosaicSizeCheck(float sizeMultiplier, float heightMultiplier) {
+ if (Mwidth < width || Mheight < height) {
+ return BLEND_RET_ERROR;
+ }
+
+ if ((Mwidth * Mheight) > (width * height * sizeMultiplier)) {
+ return BLEND_RET_ERROR;
+ }
+
+ // We won't do blending for the cases where users swing the device too much
+ // in the secondary direction. We use a short side to determine the
+ // secondary direction because users may hold the device in landsape
+ // or portrait.
+ int shortSide = min(Mwidth, Mheight);
+ if (shortSide > height * heightMultiplier) {
+ return BLEND_RET_ERROR;
+ }
+
+ return BLEND_RET_OK;
+}
int Blend::FillFramePyramid(MosaicFrame *mb)
{
diff --git a/jni/feature_mos/src/mosaic/Blend.h b/jni/feature_mos/src/mosaic/Blend.h
index 80bb577..ebb3bdc 100644
--- a/jni/feature_mos/src/mosaic/Blend.h
+++ b/jni/feature_mos/src/mosaic/Blend.h
@@ -113,6 +113,11 @@
int PerformFinalBlending(YUVinfo &imgMos, MosaicRect &cropping_rect);
void CropFinalMosaic(YUVinfo &imgMos, MosaicRect &cropping_rect);
+
+private:
+ static const float LIMIT_SIZE_MULTIPLIER = 5.0f * 2.0f;
+ static const float LIMIT_HEIGHT_MULTIPLIER = 2.5f;
+ int MosaicSizeCheck(float sizeMultiplier, float heightMultiplier);
};
#endif
diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java
index 8b9c65a..a65d263 100755
--- a/src/com/android/camera/panorama/PanoramaActivity.java
+++ b/src/com/android/camera/panorama/PanoramaActivity.java
@@ -989,9 +989,20 @@
keepScreenOnAwhile();
}
+ /**
+ * Generate the final mosaic image.
+ *
+ * @param highRes flag to indicate whether we want to get a high-res version.
+ * @return a MosaicJpeg with its isValid flag set to true if successful; null if the generation
+ * process is cancelled; and a MosaicJpeg with its isValid flag set to false if there
+ * is an error in generating the final mosaic.
+ */
public MosaicJpeg generateFinalMosaic(boolean highRes) {
- if (mMosaicFrameProcessor.createMosaic(highRes) == Mosaic.MOSAIC_RET_CANCELLED) {
+ int mosaicReturnCode = mMosaicFrameProcessor.createMosaic(highRes);
+ if (mosaicReturnCode == Mosaic.MOSAIC_RET_CANCELLED) {
return null;
+ } else if (mosaicReturnCode == Mosaic.MOSAIC_RET_ERROR) {
+ return new MosaicJpeg();
}
byte[] imageData = mMosaicFrameProcessor.getFinalMosaicNV21();