optimizedCachedBlurhashDecode function
Future<Uint8List>
optimizedCachedBlurhashDecode({
- required String blurHash,
- required int width,
- required int height,
- double punch = 1.0,
- CachedBlurhashOptimizationMode optimizationMode = CachedBlurhashOptimizationMode.standard,
Implementation
Future<Uint8List> optimizedCachedBlurhashDecode({
required String blurHash,
required int width,
required int height,
double punch = 1.0,
CachedBlurhashOptimizationMode optimizationMode = CachedBlurhashOptimizationMode.standard,
}) {
_validateBlurHash(blurHash);
final sizeFlag = _decode83(blurHash[0]);
final numY = (sizeFlag / 9).floor() + 1;
final numX = (sizeFlag % 9) + 1;
final quantisedMaximumValue = _decode83(blurHash[1]);
final maximumValue = (quantisedMaximumValue + 1) / 166;
// Preallocate colors array with fixed size
final colors = List<List<double>>.filled(numX * numY, [0, 0, 0]);
// Decode DC component (first component)
final dcValue = _decode83(blurHash.substring(2, 6));
colors[0] = _decodeDC(dcValue);
// Decode AC components (remaining components)
final adjustedPunch = maximumValue * punch;
for (var i = 1; i < colors.length; i++) {
final value = _decode83(blurHash.substring(4 + i * 2, 6 + i * 2));
colors[i] = _decodeAC(value, adjustedPunch);
}
// Precalculate cosine values for x and y
final cosinesX = List<List<double>>.generate(
numX,
(i) => List<double>.generate(
width,
(x) => cos((pi * x * i) / width),
),
);
final cosinesY = List<List<double>>.generate(
numY,
(j) => List<double>.generate(
height,
(y) => cos((pi * y * j) / height),
),
);
final bytesPerRow = width * 4;
final pixels = Uint8List(bytesPerRow * height);
// Process image in chunks to improve cache locality
const chunkSize = 32;
// Process the image in tiles for better cache performance
for (int yChunk = 0; yChunk < height; yChunk += chunkSize) {
final yEnd = min(yChunk + chunkSize, height);
for (int xChunk = 0; xChunk < width; xChunk += chunkSize) {
final xEnd = min(xChunk + chunkSize, width);
for (int y = yChunk; y < yEnd; y++) {
int p = (y * width + xChunk) * 4;
for (int x = xChunk; x < xEnd; x++) {
var r = 0.0, g = 0.0, b = 0.0;
// Use precalculated cosine values
for (int j = 0; j < numY; j++) {
final cosY = cosinesY[j][y];
for (int i = 0; i < numX; i++) {
final basis = cosinesX[i][x] * cosY;
final color = colors[i + j * numX];
r += color[0] * basis;
g += color[1] * basis;
b += color[2] * basis;
}
}
// Convert linear RGB to sRGB space based on optimization mode
switch (optimizationMode) {
case CachedBlurhashOptimizationMode.approximation:
pixels[p++] = _approximatedLinearTosRGB(r);
pixels[p++] = _approximatedLinearTosRGB(g);
pixels[p++] = _approximatedLinearTosRGB(b);
break;
case CachedBlurhashOptimizationMode.standard:
case CachedBlurhashOptimizationMode.none:
pixels[p++] = _linearTosRGB(r);
pixels[p++] = _linearTosRGB(g);
pixels[p++] = _linearTosRGB(b);
break;
}
pixels[p++] = 255; // Alpha is always 255
}
}
}
}
return Future.value(pixels);
}