shadows/framework/src/main/java/org/robolectric/shadows/ShadowOpenGLMatrix.java
@Implementation
  protected static void multiplyMM(
      float[] result, int resultOffset, float[] lhs, int lhsOffset, float[] rhs, int rhsOffset) {
    if (result == null) {
      throw new IllegalArgumentException("result == null");
    }
    if (lhs == null) {
      throw new IllegalArgumentException("lhs == null");
    }
    if (rhs == null) {
      throw new IllegalArgumentException("rhs == null");
    }
    if (resultOffset + 16 > result.length) {
      throw new IllegalArgumentException("resultOffset + 16 > result.length");
    }
    if (lhsOffset + 16 > lhs.length) {
      throw new IllegalArgumentException("lhsOffset + 16 > lhs.length");
    }
    if (rhsOffset + 16 > rhs.length) {
      throw new IllegalArgumentException("rhsOffset + 16 > rhs.length");
    }
    for (int i = 0; i < 4; i++) {
      final float rhs_i0 = rhs[I(i, 0, rhsOffset)];
      float ri0 = lhs[I(0, 0, lhsOffset)] * rhs_i0;
      float ri1 = lhs[I(0, 1, lhsOffset)] * rhs_i0;
      float ri2 = lhs[I(0, 2, lhsOffset)] * rhs_i0;
      float ri3 = lhs[I(0, 3, lhsOffset)] * rhs_i0;
      for (int j = 1; j < 4; j++) {
        final float rhs_ij = rhs[I(i, j, rhsOffset)];
        ri0 += lhs[I(j, 0, lhsOffset)] * rhs_ij;
        ri1 += lhs[I(j, 1, lhsOffset)] * rhs_ij;
        ri2 += lhs[I(j, 2, lhsOffset)] * rhs_ij;
        ri3 += lhs[I(j, 3, lhsOffset)] * rhs_ij;
      }
      result[I(i, 0, resultOffset)] = ri0;
      result[I(i, 1, resultOffset)] = ri1;
      result[I(i, 2, resultOffset)] = ri2;
      result[I(i, 3, resultOffset)] = ri3;
    }
  }
@Implementation
  protected static void multiplyMM(
      float[] result, int resultOffset, float[] lhs, int lhsOffset, float[] rhs, int rhsOffset) {
    if (result == null) {
      throw new IllegalArgumentException("result == null");
    }
    if (lhs == null) {
      throw new IllegalArgumentException("lhs == null");
    }
    if (rhs == null) {
      throw new IllegalArgumentException("rhs == null");
    }
    if (resultOffset + 16 > result.length) {
      throw new IllegalArgumentException("resultOffset + 16 > result.length");
    }
    if (lhsOffset + 16 > lhs.length) {
      throw new IllegalArgumentException("lhsOffset + 16 > lhs.length");
    }
    if (rhsOffset + 16 > rhs.length) {
      throw new IllegalArgumentException("rhsOffset + 16 > rhs.length");
    }
    for (int i = 0; i < 4; i++) {
      final float rhs_i0 = rhs[I(i, 0, rhsOffset)];
      float ri0 = lhs[I(0, 0, lhsOffset)] * rhs_i0;
      float ri1 = lhs[I(0, 1, lhsOffset)] * rhs_i0;
      float ri2 = lhs[I(0, 2, lhsOffset)] * rhs_i0;
      float ri3 = lhs[I(0, 3, lhsOffset)] * rhs_i0;
      for (int j = 1; j < 4; j++) {
        final float rhs_ij = rhs[I(i, j, rhsOffset)];
        ri0 += lhs[I(j, 0, lhsOffset)] * rhs_ij;
        ri1 += lhs[I(j, 1, lhsOffset)] * rhs_ij;
        ri2 += lhs[I(j, 2, lhsOffset)] * rhs_ij;
        ri3 += lhs[I(j, 3, lhsOffset)] * rhs_ij;
      }
      result[I(i, 0, resultOffset)] = ri0;
      result[I(i, 1, resultOffset)] = ri1;
      result[I(i, 2, resultOffset)] = ri2;
      result[I(i, 3, resultOffset)] = ri3;
    }
  }
Multiplies two 4x4 matrices together and stores the result in a third 4x4 matrix. In matrix
notation: result = lhs x rhs. Due to the way matrix multiplication works, the result matrix
will have the same effect as first multiplying by the rhs matrix, then multiplying by the lhs
matrix. This is the opposite of what you might expect.

<p>The same float array may be passed for result, lhs, and/or rhs. However, the result element
values are undefined if the result elements overlap either the lhs or rhs elements.

@param result The float array that holds the result.
@param resultOffset The offset into the result array where the result is stored.
@param lhs The float array that holds the left-hand-side matrix.
@param lhsOffset The offset into the lhs array where the lhs is stored
@param rhs The float array that holds the right-hand-side matrix.
@param rhsOffset The offset into the rhs array where the rhs is stored.
@throws IllegalArgumentException if result, lhs, or rhs are null, or if resultOffset + 16 >
result.length or lhsOffset + 16 > lhs.length or rhsOffset + 16 > rhs.length.