parent
06d805cb72
commit
e96c9f0846
@ -0,0 +1,96 @@ |
||||
package com.otaliastudios.cameraview.internal.utils; |
||||
|
||||
import android.graphics.ImageFormat; |
||||
import android.graphics.Rect; |
||||
import android.media.Image; |
||||
|
||||
import com.otaliastudios.cameraview.size.AspectRatio; |
||||
import com.otaliastudios.cameraview.size.Size; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
import androidx.annotation.RequiresApi; |
||||
|
||||
/** |
||||
* Conversions for {@link android.media.Image}s into byte arrays. |
||||
*/ |
||||
@RequiresApi(19) |
||||
public class ImageHelper { |
||||
|
||||
// https://stackoverflow.com/a/52740776/4288782
|
||||
public static void convertToNV21(@NonNull Image image, @NonNull byte[] result) { |
||||
if (image.getFormat() != ImageFormat.YUV_420_888) { |
||||
throw new IllegalStateException("CAn only convert from YUV_420_888."); |
||||
} |
||||
int width = image.getWidth(); |
||||
int height = image.getHeight(); |
||||
int ySize = width * height; |
||||
int uvSize = width * height / 4; |
||||
|
||||
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); // Y
|
||||
ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); // U
|
||||
ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); // V
|
||||
|
||||
int rowStride = image.getPlanes()[0].getRowStride(); |
||||
|
||||
if (image.getPlanes()[0].getPixelStride() != 1) { |
||||
throw new AssertionError("Something wrong in convertToNV21"); |
||||
} |
||||
|
||||
int pos = 0; |
||||
|
||||
if (rowStride == width) { // likely
|
||||
yBuffer.get(result, 0, ySize); |
||||
pos += ySize; |
||||
} |
||||
else { |
||||
int yBufferPos = width - rowStride; // not an actual position
|
||||
for (; pos<ySize; pos+=width) { |
||||
yBufferPos += rowStride - width; |
||||
yBuffer.position(yBufferPos); |
||||
yBuffer.get(result, pos, width); |
||||
} |
||||
} |
||||
|
||||
rowStride = image.getPlanes()[2].getRowStride(); |
||||
int pixelStride = image.getPlanes()[2].getPixelStride(); |
||||
|
||||
if (rowStride != image.getPlanes()[1].getRowStride()) { |
||||
throw new AssertionError("Something wrong in convertToNV21"); |
||||
} |
||||
if (pixelStride != image.getPlanes()[1].getPixelStride()) { |
||||
throw new AssertionError("Something wrong in convertToNV21"); |
||||
} |
||||
|
||||
if (pixelStride == 2 && rowStride == width && uBuffer.get(0) == vBuffer.get(1)) { |
||||
// maybe V an U planes overlap as per NV21, which means vBuffer[1] is alias of uBuffer[0]
|
||||
byte savePixel = vBuffer.get(1); |
||||
vBuffer.put(1, (byte)0); |
||||
if (uBuffer.get(0) == 0) { |
||||
vBuffer.put(1, (byte)255); |
||||
//noinspection ConstantConditions
|
||||
if (uBuffer.get(0) == 255) { |
||||
vBuffer.put(1, savePixel); |
||||
vBuffer.get(result, ySize, uvSize); |
||||
return; // shortcut
|
||||
} |
||||
} |
||||
|
||||
// unfortunately, the check failed. We must save U and V pixel by pixel
|
||||
vBuffer.put(1, savePixel); |
||||
} |
||||
|
||||
// other optimizations could check if (pixelStride == 1) or (pixelStride == 2),
|
||||
// but performance gain would be less significant
|
||||
|
||||
for (int row=0; row<height/2; row++) { |
||||
for (int col=0; col<width/2; col++) { |
||||
int vuPos = col*pixelStride + row*rowStride; |
||||
result[pos++] = vBuffer.get(vuPos); |
||||
result[pos++] = uBuffer.get(vuPos); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue