I guess that for the full image you coded something like that:
Uint8List getBytes() {
final WriteBuffer allBytes = WriteBuffer();
for (final Plane plane in cameraImage.planes) {
allBytes.putUint8List(plane.bytes);
}
return allBytes.done().buffer.asUint8List();
}
In fact, you are concatenating one after the other the data of the 3 planes of YUV: all Y, then all U, then all V.
As you can see in the wikipedia page plane Y has the same width and height as the image, while planes U and V use width/2 and height/2.
If we go byte after byte that means that the code above is similar to the following code:
int divider = 1; // for first plane: Y
for (final Plane plane in cameraImage.planes) {
for (int i = 0; i < cameraImage.height ~/ divider; i++) {
for (int j = 0; j < cameraImage.width ~/ divider; j++) {
allBytes.putUint8(plane.bytes[j + i * cameraImage.width ~/ divider]);
}
}
divider = 2; // for planes U and V
}
Now that you're here, I think you understand how to crop:
int divider = 1; // for first plane: Y
for (final Plane plane in cameraImage.planes) {
for (int i = cropTop ~/ divider; i < cropBottom ~/ divider; i++) {
for (int j = cropLeft ~/ divider; j < cropRight ~/ divider; j++) {
allBytes.putUint8(plane.bytes[j + i * cameraImage.width ~/ divider]);
}
}
divider = 2; // for planes U and V
}
where the crop* variables are computed from the full image.
That's the theory: this piece of code does not take into consideration the camera orientation, the possible side-effects with odd sizes and the performances. But that's the general idea.