encodeRgba4bpp method

Uint8List encodeRgba4bpp(
  1. Image bitmap
)

Implementation

Uint8List encodeRgba4bpp(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, y4 = 0; y < blocks; ++y, y4 += 4) {
    for (var x = 0, x4 = 0; x < blocks; ++x, x4 += 4) {
      final cbb = _calculateBoundingBoxRgba(bitmap, x4, y4);
      packet
        ..setBlock(x, y)
        ..usePunchthroughAlpha = false
        ..setColorRgbaA(cbb.min as PvrColorRgba)
        ..setColorRgbaB(cbb.max as PvrColorRgba);
    }
  }

  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.getColorRgbaA() * factors[factorIndex][0] +
              p1.getColorRgbaA() * factors[factorIndex][1] +
              p2.getColorRgbaA() * factors[factorIndex][2] +
              p3.getColorRgbaA() * factors[factorIndex][3];

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

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

          final d = cb - ca;
          final p = PvrColorRgba(r * 16, g * 16, b * 16, a * 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;
}