encodeRgb4bpp method

Uint8List encodeRgb4bpp(
  1. Image bitmap
)

Implementation

Uint8List encodeRgb4bpp(Image bitmap) {
  if (bitmap.width != bitmap.height) {
    throw ImageException('PVRTC requires a square image.');
  }

  if (!PvrBitUtility.isPowerOf2(bitmap.width)) {
    throw ImageException('PVRTC requires a power-of-two sized image.');
  }

  final size = bitmap.width;
  final blocks = size ~/ 4;
  final blockMask = blocks - 1;

  // Allocate enough data for encoding the image.
  final outputData = Uint8List((bitmap.width * bitmap.height) ~/ 2);
  final packet = PvrPacket(outputData);
  final p0 = PvrPacket(outputData);
  final p1 = PvrPacket(outputData);
  final p2 = PvrPacket(outputData);
  final p3 = PvrPacket(outputData);

  for (var y = 0; y < blocks; ++y) {
    for (var x = 0; x < blocks; ++x) {
      final cbb = _calculateBoundingBoxRgb(bitmap, x, y);
      packet
        ..setBlock(x, y)
        ..usePunchthroughAlpha = false
        ..setColorRgbA(cbb.min as PvrColorRgb)
        ..setColorRgbB(cbb.max as PvrColorRgb);
    }
  }

  const factors = PvrPacket.bilinearFactors;

  for (var y = 0, y4 = 0; y < blocks; ++y, y4 += 4) {
    for (var x = 0, x4 = 0; x < blocks; ++x, x4 += 4) {
      var factorIndex = 0;
      var modulationData = 0;

      for (var py = 0; py < 4; ++py) {
        final yOffset = (py < 2) ? -1 : 0;
        final y0 = (y + yOffset) & blockMask;
        final y1 = (y0 + 1) & blockMask;

        for (var px = 0; px < 4; ++px) {
          final xOffset = (px < 2) ? -1 : 0;
          final x0 = (x + xOffset) & blockMask;
          final x1 = (x0 + 1) & blockMask;

          p0.setBlock(x0, y0);
          p1.setBlock(x1, y0);
          p2.setBlock(x0, y1);
          p3.setBlock(x1, y1);

          final ca = p0.getColorRgbA() * factors[factorIndex][0] +
              p1.getColorRgbA() * factors[factorIndex][1] +
              p2.getColorRgbA() * factors[factorIndex][2] +
              p3.getColorRgbA() * factors[factorIndex][3];

          final cb = p0.getColorRgbB() * factors[factorIndex][0] +
              p1.getColorRgbB() * factors[factorIndex][1] +
              p2.getColorRgbB() * factors[factorIndex][2] +
              p3.getColorRgbB() * factors[factorIndex][3];

          //final pi = pixelIndex + ((py * size + px) * 4);
          final pi = bitmap.getPixel(x4 + px, y4 + py);
          final r = pi.r.toInt();
          final g = pi.g.toInt();
          final b = pi.b.toInt();

          final d = cb - ca;
          final p = PvrColorRgb(r * 16, g * 16, b * 16);
          final v = p - ca;

          // PVRTC uses weightings of 0, 3/8, 5/8 and 1
          // The boundaries for these are 3/16, 1/2 (=8/16), 13/16
          final projection = v.dotProd(d) * 16;
          final lengthSquared = d.dotProd(d);
          if (projection > 3 * lengthSquared) {
            modulationData++;
          }
          if (projection > 8 * lengthSquared) {
            modulationData++;
          }
          if (projection > 13 * lengthSquared) {
            modulationData++;
          }

          modulationData = PvrBitUtility.rotateRight(modulationData, 2);

          factorIndex++;
        }
      }

      packet
        ..setBlock(x, y)
        ..modulationData = modulationData;
    }
  }

  return outputData;
}