Add Supa account and search changes
This commit is contained in:
@@ -17,15 +17,19 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fast_equatable/hash.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart' as svg;
|
||||
import 'package:weblibre/core/logger.dart';
|
||||
import 'package:weblibre/domain/entities/equatable_image.dart';
|
||||
import 'package:weblibre/utils/lru_cache.dart';
|
||||
|
||||
final _cache = LRUCache<int, EquatableImage>(100);
|
||||
const _defaultSvgIconSize = 32;
|
||||
|
||||
/// Clears the global image decode cache.
|
||||
void clearImageCache() {
|
||||
@@ -63,8 +67,141 @@ Future<EquatableImage?> tryDecodeImage(
|
||||
return image;
|
||||
}
|
||||
} catch (e, s) {
|
||||
if (_isLikelySvg(bytes)) {
|
||||
try {
|
||||
final svgImage = await _tryDecodeSvg(
|
||||
bytes,
|
||||
hash: digest,
|
||||
targetWidth: targetWidth,
|
||||
targetHeight: targetHeight,
|
||||
);
|
||||
if (svgImage != null) {
|
||||
_cache.set(digest, svgImage);
|
||||
return svgImage;
|
||||
}
|
||||
} catch (svgError, svgStackTrace) {
|
||||
logger.w(
|
||||
'Failed to rasterize SVG image',
|
||||
error: svgError,
|
||||
stackTrace: svgStackTrace,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
logger.w('Failed to decode image', error: e, stackTrace: s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _isLikelySvg(Uint8List bytes) {
|
||||
final prefix = utf8
|
||||
.decode(bytes.sublist(0, min(bytes.length, 512)), allowMalformed: true)
|
||||
.trimLeft();
|
||||
final normalized = prefix.isNotEmpty && prefix.codeUnitAt(0) == 0xFEFF
|
||||
? prefix.substring(1)
|
||||
: prefix;
|
||||
final lower = normalized.toLowerCase();
|
||||
|
||||
return lower.contains('<svg') ||
|
||||
lower.contains('<!doctype svg') ||
|
||||
(lower.contains('<?xml') && lower.contains('<svg'));
|
||||
}
|
||||
|
||||
Future<EquatableImage?> _tryDecodeSvg(
|
||||
Uint8List bytes, {
|
||||
required int hash,
|
||||
int? targetWidth,
|
||||
int? targetHeight,
|
||||
}) async {
|
||||
final pictureInfo = await svg.vg.loadPicture(svg.SvgBytesLoader(bytes), null);
|
||||
|
||||
try {
|
||||
final dimensions = _resolveSvgRasterDimensions(
|
||||
pictureInfo.size,
|
||||
targetWidth: targetWidth,
|
||||
targetHeight: targetHeight,
|
||||
);
|
||||
|
||||
final sourceWidth = pictureInfo.size.width > 0
|
||||
? pictureInfo.size.width
|
||||
: dimensions.width.toDouble();
|
||||
final sourceHeight = pictureInfo.size.height > 0
|
||||
? pictureInfo.size.height
|
||||
: dimensions.height.toDouble();
|
||||
final recorder = PictureRecorder();
|
||||
final canvas = Canvas(recorder);
|
||||
|
||||
canvas.clipRect(
|
||||
Rect.fromLTWH(
|
||||
0,
|
||||
0,
|
||||
dimensions.width.toDouble(),
|
||||
dimensions.height.toDouble(),
|
||||
),
|
||||
);
|
||||
canvas.scale(
|
||||
dimensions.width / sourceWidth,
|
||||
dimensions.height / sourceHeight,
|
||||
);
|
||||
canvas.drawPicture(pictureInfo.picture);
|
||||
|
||||
final scaledPicture = recorder.endRecording();
|
||||
|
||||
try {
|
||||
final rasterized = await scaledPicture.toImage(
|
||||
dimensions.width,
|
||||
dimensions.height,
|
||||
);
|
||||
return EquatableImage(rasterized, hash: hash);
|
||||
} finally {
|
||||
scaledPicture.dispose();
|
||||
}
|
||||
} finally {
|
||||
pictureInfo.picture.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
({int width, int height}) _resolveSvgRasterDimensions(
|
||||
Size sourceSize, {
|
||||
int? targetWidth,
|
||||
int? targetHeight,
|
||||
}) {
|
||||
if (targetWidth != null && targetHeight != null) {
|
||||
return (width: max(1, targetWidth), height: max(1, targetHeight));
|
||||
}
|
||||
|
||||
final fallbackSize = targetWidth ?? targetHeight ?? _defaultSvgIconSize;
|
||||
final sourceWidth = sourceSize.width > 0
|
||||
? sourceSize.width
|
||||
: fallbackSize.toDouble();
|
||||
final sourceHeight = sourceSize.height > 0
|
||||
? sourceSize.height
|
||||
: fallbackSize.toDouble();
|
||||
|
||||
if (targetWidth != null) {
|
||||
return (
|
||||
width: max(1, targetWidth),
|
||||
height: max(1, (targetWidth * sourceHeight / sourceWidth).round()),
|
||||
);
|
||||
}
|
||||
|
||||
if (targetHeight != null) {
|
||||
return (
|
||||
width: max(1, (targetHeight * sourceWidth / sourceHeight).round()),
|
||||
height: max(1, targetHeight),
|
||||
);
|
||||
}
|
||||
|
||||
if (sourceWidth >= sourceHeight) {
|
||||
return (
|
||||
width: fallbackSize,
|
||||
height: max(1, (fallbackSize * sourceHeight / sourceWidth).round()),
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
width: max(1, (fallbackSize * sourceWidth / sourceHeight).round()),
|
||||
height: fallbackSize,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user