mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
fix #91
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'dart:async' show Future, StreamController, scheduleMicrotask;
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:ui' as ui show Codec;
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -10,6 +11,39 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
||||
extends ImageProvider<T> {
|
||||
const BaseImageProvider();
|
||||
|
||||
static double? _effectiveScreenWidth;
|
||||
|
||||
static const double _normalComicImageRatio = 0.72;
|
||||
|
||||
static const double _minComicImageWidth = 1920 * _normalComicImageRatio;
|
||||
|
||||
static TargetImageSize _getTargetSize(width, height) {
|
||||
if (_effectiveScreenWidth == null) {
|
||||
final screens = PlatformDispatcher.instance.displays;
|
||||
for (var screen in screens) {
|
||||
if (screen.size.width > screen.size.height) {
|
||||
_effectiveScreenWidth = max(
|
||||
_effectiveScreenWidth ?? 0,
|
||||
screen.size.height * _normalComicImageRatio,
|
||||
);
|
||||
} else {
|
||||
_effectiveScreenWidth = max(
|
||||
_effectiveScreenWidth ?? 0,
|
||||
screen.size.width
|
||||
);
|
||||
}
|
||||
}
|
||||
if (_effectiveScreenWidth! < _minComicImageWidth) {
|
||||
_effectiveScreenWidth = _minComicImageWidth;
|
||||
}
|
||||
}
|
||||
if (width > _effectiveScreenWidth!) {
|
||||
height = (height * _effectiveScreenWidth! / width).round();
|
||||
width = _effectiveScreenWidth!.round();
|
||||
}
|
||||
return TargetImageSize(width: width, height: height);
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter loadImage(T key, ImageDecoderCallback decode) {
|
||||
final chunkEvents = StreamController<ImageChunkEvent>();
|
||||
@@ -45,19 +79,12 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
||||
|
||||
while (data == null && !stop) {
|
||||
try {
|
||||
if(_cache.containsKey(key.key)){
|
||||
data = _cache[key.key];
|
||||
} else {
|
||||
data = await load(chunkEvents);
|
||||
_checkCacheSize();
|
||||
_cache[key.key] = data;
|
||||
_cacheSize += data.length;
|
||||
}
|
||||
data = await load(chunkEvents);
|
||||
} catch (e) {
|
||||
if(e.toString().contains("Invalid Status Code: 404")) {
|
||||
if (e.toString().contains("Invalid Status Code: 404")) {
|
||||
rethrow;
|
||||
}
|
||||
if(e.toString().contains("Invalid Status Code: 403")) {
|
||||
if (e.toString().contains("Invalid Status Code: 403")) {
|
||||
rethrow;
|
||||
}
|
||||
if (e.toString().contains("handshake")) {
|
||||
@@ -73,23 +100,24 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
||||
}
|
||||
}
|
||||
|
||||
if(stop) {
|
||||
if (stop) {
|
||||
throw Exception("Image loading is stopped");
|
||||
}
|
||||
|
||||
if(data!.isEmpty) {
|
||||
if (data!.isEmpty) {
|
||||
throw Exception("Empty image data");
|
||||
}
|
||||
|
||||
try {
|
||||
final buffer = await ImmutableBuffer.fromUint8List(data);
|
||||
return await decode(buffer);
|
||||
return await decode(buffer, getTargetSize: _getTargetSize);
|
||||
} catch (e) {
|
||||
await CacheManager().delete(this.key);
|
||||
if (data.length < 2 * 1024) {
|
||||
// data is too short, it's likely that the data is text, not image
|
||||
try {
|
||||
var text = const Utf8Codec(allowMalformed: false).decoder.convert(data);
|
||||
var text =
|
||||
const Utf8Codec(allowMalformed: false).decoder.convert(data);
|
||||
throw Exception("Expected image data, but got text: $text");
|
||||
} catch (e) {
|
||||
// ignore
|
||||
@@ -107,30 +135,6 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
||||
}
|
||||
}
|
||||
|
||||
static final _cache = <String, Uint8List>{};
|
||||
|
||||
static var _cacheSize = 0;
|
||||
|
||||
static var _cacheSizeLimit = 50 * 1024 * 1024;
|
||||
|
||||
static void _checkCacheSize(){
|
||||
while (_cacheSize > _cacheSizeLimit){
|
||||
var firstKey = _cache.keys.first;
|
||||
_cacheSize -= _cache[firstKey]!.length;
|
||||
_cache.remove(firstKey);
|
||||
}
|
||||
}
|
||||
|
||||
static void clearCache(){
|
||||
_cache.clear();
|
||||
_cacheSize = 0;
|
||||
}
|
||||
|
||||
static void setCacheSizeLimit(int size){
|
||||
_cacheSizeLimit = size;
|
||||
_checkCacheSize();
|
||||
}
|
||||
|
||||
Future<Uint8List> load(StreamController<ImageChunkEvent> chunkEvents);
|
||||
|
||||
String get key;
|
||||
|
@@ -2,6 +2,7 @@ import 'dart:async' show Future, StreamController;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:venera/network/images.dart';
|
||||
import 'package:venera/utils/io.dart';
|
||||
import 'base_image_provider.dart';
|
||||
import 'reader_image.dart' as image_provider;
|
||||
|
||||
@@ -20,6 +21,14 @@ class ReaderImageProvider
|
||||
|
||||
@override
|
||||
Future<Uint8List> load(StreamController<ImageChunkEvent> chunkEvents) async {
|
||||
if (imageKey.startsWith('file://')) {
|
||||
var file = File(imageKey);
|
||||
if (await file.exists()) {
|
||||
return file.readAsBytes();
|
||||
}
|
||||
throw "Error: File not found.";
|
||||
}
|
||||
|
||||
await for (var event
|
||||
in ImageDownloader.loadComicImage(imageKey, sourceKey, cid, eid)) {
|
||||
chunkEvents.add(ImageChunkEvent(
|
||||
|
@@ -162,19 +162,22 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
} else {
|
||||
int pageIndex = index - 1;
|
||||
int startIndex = pageIndex * reader.imagesPerPage;
|
||||
int endIndex = math.min(startIndex + reader.imagesPerPage, reader.images!.length);
|
||||
List<String> pageImages = reader.images!.sublist(startIndex, endIndex);
|
||||
int endIndex = math.min(
|
||||
startIndex + reader.imagesPerPage, reader.images!.length);
|
||||
List<String> pageImages =
|
||||
reader.images!.sublist(startIndex, endIndex);
|
||||
|
||||
cached[index] = true;
|
||||
cache(index);
|
||||
|
||||
photoViewControllers[index] = PhotoViewController();
|
||||
|
||||
if(reader.imagesPerPage == 1) {
|
||||
if (reader.imagesPerPage == 1) {
|
||||
return PhotoViewGalleryPageOptions(
|
||||
filterQuality: FilterQuality.medium,
|
||||
controller: photoViewControllers[index],
|
||||
imageProvider: _createImageProviderFromKey(pageImages[0], context),
|
||||
imageProvider:
|
||||
_createImageProviderFromKey(pageImages[0], context),
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (_, error, s, retry) {
|
||||
return NetworkError(message: error.toString(), retry: retry);
|
||||
@@ -645,32 +648,19 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
||||
|
||||
ImageProvider _createImageProviderFromKey(
|
||||
String imageKey, BuildContext context) {
|
||||
if (imageKey.startsWith('file://')) {
|
||||
return FileImage(File(imageKey.replaceFirst("file://", '')));
|
||||
} else {
|
||||
var reader = context.reader;
|
||||
return ReaderImageProvider(
|
||||
imageKey,
|
||||
reader.type.comicSource!.key,
|
||||
reader.cid,
|
||||
reader.eid,
|
||||
);
|
||||
}
|
||||
var reader = context.reader;
|
||||
return ReaderImageProvider(
|
||||
imageKey,
|
||||
reader.type.comicSource!.key,
|
||||
reader.cid,
|
||||
reader.eid,
|
||||
);
|
||||
}
|
||||
|
||||
ImageProvider _createImageProvider(int page, BuildContext context) {
|
||||
var reader = context.reader;
|
||||
var imageKey = reader.images![page - 1];
|
||||
if (imageKey.startsWith('file://')) {
|
||||
return FileImage(File(imageKey.replaceFirst("file://", '')));
|
||||
} else {
|
||||
return ReaderImageProvider(
|
||||
imageKey,
|
||||
reader.type.comicSource!.key,
|
||||
reader.cid,
|
||||
reader.eid,
|
||||
);
|
||||
}
|
||||
return _createImageProviderFromKey(imageKey, context);
|
||||
}
|
||||
|
||||
void _precacheImage(int page, BuildContext context) {
|
||||
|
@@ -9,6 +9,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_memory_info/flutter_memory_info.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photo_view/photo_view_gallery.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
@@ -21,6 +22,7 @@ import 'package:venera/foundation/comic_type.dart';
|
||||
import 'package:venera/foundation/history.dart';
|
||||
import 'package:venera/foundation/image_provider/reader_image.dart';
|
||||
import 'package:venera/foundation/local.dart';
|
||||
import 'package:venera/foundation/log.dart';
|
||||
import 'package:venera/pages/settings/settings_page.dart';
|
||||
import 'package:venera/utils/data_sync.dart';
|
||||
import 'package:venera/utils/file_type.dart';
|
||||
@@ -142,9 +144,27 @@ class _ReaderState extends State<Reader> with _ReaderLocation, _ReaderWindow {
|
||||
if(appdata.settings['enableTurnPageByVolumeKey']) {
|
||||
handleVolumeEvent();
|
||||
}
|
||||
setImageCacheSize();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void setImageCacheSize() async {
|
||||
var availableRAM = await MemoryInfo.getFreePhysicalMemorySize();
|
||||
if (availableRAM == null) return;
|
||||
int maxImageCacheSize;
|
||||
if (availableRAM < 1 << 30) {
|
||||
maxImageCacheSize = 100 << 20;
|
||||
} else if (availableRAM < 2 << 30) {
|
||||
maxImageCacheSize = 200 << 20;
|
||||
} else if (availableRAM < 4 << 30) {
|
||||
maxImageCacheSize = 300 << 20;
|
||||
} else {
|
||||
maxImageCacheSize = 500 << 20;
|
||||
}
|
||||
Log.info("Reader", "Detect available RAM: $availableRAM, set image cache size to $maxImageCacheSize");
|
||||
PaintingBinding.instance.imageCache.maximumSizeBytes = maxImageCacheSize;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
autoPageTurningTimer?.cancel();
|
||||
@@ -154,6 +174,7 @@ class _ReaderState extends State<Reader> with _ReaderLocation, _ReaderWindow {
|
||||
Future.microtask(() {
|
||||
DataSync().onDataChanged();
|
||||
});
|
||||
PaintingBinding.instance.imageCache.maximumSizeBytes = 100 << 20;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
13
pubspec.lock
13
pubspec.lock
@@ -388,6 +388,15 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_memory_info:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: a401e3f96dca6ab0ab07b6b2ec0b649833f04c14
|
||||
url: "https://github.com/wgh136/flutter_memory_info"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1070,10 +1079,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
|
||||
sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.4"
|
||||
version: "5.9.0"
|
||||
window_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@@ -72,6 +72,9 @@ dependencies:
|
||||
pdf: ^3.11.1
|
||||
dynamic_color: ^1.7.0
|
||||
shimmer: ^3.0.0
|
||||
flutter_memory_info:
|
||||
git:
|
||||
url: https://github.com/wgh136/flutter_memory_info
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user