resize large image

This commit is contained in:
niuhuan 2021-11-06 19:25:44 +08:00
parent 173c19d557
commit fdbce75afd
7 changed files with 133 additions and 18 deletions

View File

@ -3,11 +3,13 @@ package niuhuan.pikapi
import android.content.ContentValues import android.content.ContentValues
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.os.Build import android.os.Build
import android.os.Environment import android.os.Environment
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.provider.MediaStore import android.provider.MediaStore
import android.util.DisplayMetrics
import android.util.Log import android.util.Log
import android.view.Display import android.view.Display
import android.view.KeyEvent import android.view.KeyEvent
@ -23,6 +25,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.newSingleThreadContext import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import mobile.Mobile import mobile.Mobile
import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
@ -100,6 +103,7 @@ class MainActivity : FlutterActivity() {
// 获取可以迁移数据地址 // 获取可以迁移数据地址
"androidGetExtendDirs" -> androidGetExtendDirs() "androidGetExtendDirs" -> androidGetExtendDirs()
"androidSecureFlag" -> androidSecureFlag(call.argument("flag")!!) "androidSecureFlag" -> androidSecureFlag(call.argument("flag")!!)
"convertToPNG" -> convertToPNG(call.argument("path")!!)
else -> { else -> {
notImplementedToken notImplementedToken
} }
@ -350,4 +354,36 @@ class MainActivity : FlutterActivity() {
} }
} }
private fun convertToPNG(path: String): ByteArray {
BitmapFactory.decodeFile(path)?.let { bitmap ->
val maxWidth =
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> windowManager.currentWindowMetrics.bounds.width()
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 -> {
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getRealMetrics(displayMetrics)
displayMetrics.widthPixels
}
else -> throw Exception("not support")
}
if (bitmap.width > maxWidth) {
val newHeight = maxWidth * bitmap.height / bitmap.width
val newImage = Bitmap.createScaledBitmap(bitmap, maxWidth, newHeight, true)
return compressBitMap(newImage)
}
return compressBitMap(bitmap)
}
throw Exception("error pic")
}
private fun compressBitMap(bitmap: Bitmap): ByteArray {
val bos = ByteArrayOutputStream()
bos.use { bos ->
Log.d("BITMAP", bitmap.width.toString())
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
}
return bos.toByteArray()
}
} }

View File

@ -95,23 +95,30 @@ List<T> filteredList<T>(List<T> list, bool Function(T) filter) {
/// , null, /// , null,
Future<T?> chooseListDialog<T>( Future<T?> chooseListDialog<T>(
BuildContext context, BuildContext context, String title, List<T> items,
String title, {String? tips}) async {
List<T> items, List<Widget> widgets = [];
) async { if (tips != null) {
widgets.add(
Container(
padding: EdgeInsets.fromLTRB(15, 5, 15, 15),
child: Text(tips),
)
);
}
widgets.addAll(items.map((e) => SimpleDialogOption(
onPressed: () {
Navigator.of(context).pop(e);
},
child: Text('$e'),
)));
return showDialog<T>( return showDialog<T>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return SimpleDialog( return SimpleDialog(
title: Text(title), title: Text(title),
children: items children: widgets,
.map((e) => SimpleDialogOption(
onPressed: () {
Navigator.of(context).pop(e);
},
child: Text('$e'),
))
.toList(),
); );
}, },
); );

View File

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:pikapi/basic/Entities.dart'; import 'package:pikapi/basic/Entities.dart';
@ -607,4 +608,9 @@ class Method {
"comicId": comicId, "comicId": comicId,
}); });
} }
/// PNG
Future<Uint8List> convertToPNG(String path) async {
return await _channel.invokeMethod("convertToPNG", {"path": path});
}
} }

View File

@ -0,0 +1,48 @@
import 'dart:io';
import 'package:flutter/material.dart';
import '../Common.dart';
import '../Method.dart';
const _propertyName = "convertToPNG";
var _convertToPNG = false;
Future initConvertToPNG() async {
if (Platform.isAndroid) {
_convertToPNG =
(await method.loadProperty(_propertyName, "false")) == "true";
}
}
bool convertToPNG() {
return _convertToPNG;
}
Future<void> _chooseConvertToPNGSetting(BuildContext context) async {
String? result = await chooseListDialog<String>(context, "超大图片缩放", ["", ""],
tips: "会增加耗电\n可以解决部分漫画崩溃的问题");
if (result != null) {
var target = result == "";
await method.saveProperty(_propertyName, "$target");
_convertToPNG = target;
}
}
Widget convertToPNGSetting() {
if (Platform.isAndroid) {
return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) {
return ListTile(
title: Text("读取到超大图片时进行缩放(防止崩溃)"),
subtitle: Text(_convertToPNG ? "" : ""),
onTap: () async {
await _chooseConvertToPNGSetting(context);
setState(() {});
},
);
},
);
}
return Container();
}

View File

@ -6,6 +6,7 @@ import 'package:pikapi/basic/config/AutoClean.dart';
import 'package:pikapi/basic/config/AutoFullScreen.dart'; import 'package:pikapi/basic/config/AutoFullScreen.dart';
import 'package:pikapi/basic/config/ChooserRoot.dart'; import 'package:pikapi/basic/config/ChooserRoot.dart';
import 'package:pikapi/basic/config/ContentFailedReloadAction.dart'; import 'package:pikapi/basic/config/ContentFailedReloadAction.dart';
import 'package:pikapi/basic/config/ConvertToPNG.dart';
import 'package:pikapi/basic/config/DownloadAndExportPath.dart'; import 'package:pikapi/basic/config/DownloadAndExportPath.dart';
import 'package:pikapi/basic/config/DownloadThreadCount.dart'; import 'package:pikapi/basic/config/DownloadThreadCount.dart';
import 'package:pikapi/basic/config/FullScreenAction.dart'; import 'package:pikapi/basic/config/FullScreenAction.dart';
@ -67,6 +68,7 @@ class _InitScreenState extends State<InitScreen> {
await initDownloadAndExportPath(); await initDownloadAndExportPath();
await initAndroidSecureFlag(); await initAndroidSecureFlag();
await initDownloadThreadCount(); await initDownloadThreadCount();
await initConvertToPNG();
// , token失效重新登录, 1 // , token失效重新登录, 1
if (await method.preLogin()) { if (await method.preLogin()) {
// token或username+password有效则直接进入登录好的界面 // token或username+password有效则直接进入登录好的界面

View File

@ -9,6 +9,7 @@ import 'package:pikapi/basic/config/AutoClean.dart';
import 'package:pikapi/basic/config/AutoFullScreen.dart'; import 'package:pikapi/basic/config/AutoFullScreen.dart';
import 'package:pikapi/basic/config/ChooserRoot.dart'; import 'package:pikapi/basic/config/ChooserRoot.dart';
import 'package:pikapi/basic/config/ContentFailedReloadAction.dart'; import 'package:pikapi/basic/config/ContentFailedReloadAction.dart';
import 'package:pikapi/basic/config/ConvertToPNG.dart';
import 'package:pikapi/basic/config/DownloadAndExportPath.dart'; import 'package:pikapi/basic/config/DownloadAndExportPath.dart';
import 'package:pikapi/basic/config/DownloadThreadCount.dart'; import 'package:pikapi/basic/config/DownloadThreadCount.dart';
import 'package:pikapi/basic/config/FullScreenAction.dart'; import 'package:pikapi/basic/config/FullScreenAction.dart';
@ -37,6 +38,7 @@ class SettingsScreen extends StatelessWidget {
NetworkSetting(), NetworkSetting(),
Divider(), Divider(),
qualitySetting(), qualitySetting(),
convertToPNGSetting(),
readerTypeSetting(), readerTypeSetting(),
readerDirectionSetting(), readerDirectionSetting(),
autoFullScreenSetting(), autoFullScreenSetting(),

View File

@ -1,20 +1,31 @@
import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:pikapi/basic/Method.dart'; import 'package:pikapi/basic/Method.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:pikapi/basic/config/ConvertToPNG.dart';
import 'dart:io'; import 'dart:io';
import 'dart:ui' as ui show Codec; import 'dart:ui' as ui show Codec;
Future<Uint8List> _loadImageFile(String path) {
if (convertToPNG()) {
return method.convertToPNG(path);
}
return File(path).readAsBytes();
}
// //
class ResourceFileImageProvider extends ImageProvider<ResourceFileImageProvider> { class ResourceFileImageProvider
extends ImageProvider<ResourceFileImageProvider> {
final String path; final String path;
final double scale; final double scale;
ResourceFileImageProvider(this.path, {this.scale = 1.0}); ResourceFileImageProvider(this.path, {this.scale = 1.0});
@override @override
ImageStreamCompleter load(ResourceFileImageProvider key, DecoderCallback decode) { ImageStreamCompleter load(
ResourceFileImageProvider key, DecoderCallback decode) {
return MultiFrameImageStreamCompleter( return MultiFrameImageStreamCompleter(
codec: _loadAsync(key), codec: _loadAsync(key),
scale: key.scale, scale: key.scale,
@ -22,14 +33,15 @@ class ResourceFileImageProvider extends ImageProvider<ResourceFileImageProvider>
} }
@override @override
Future<ResourceFileImageProvider> obtainKey(ImageConfiguration configuration) { Future<ResourceFileImageProvider> obtainKey(
ImageConfiguration configuration) {
return SynchronousFuture<ResourceFileImageProvider>(this); return SynchronousFuture<ResourceFileImageProvider>(this);
} }
Future<ui.Codec> _loadAsync(ResourceFileImageProvider key) async { Future<ui.Codec> _loadAsync(ResourceFileImageProvider key) async {
assert(key == this); assert(key == this);
return PaintingBinding.instance! return PaintingBinding.instance!
.instantiateImageCodec(await File(path).readAsBytes()); .instantiateImageCodec(await _loadImageFile(path));
} }
@override @override
@ -96,7 +108,8 @@ class ResourceDownloadFileImageProvider
} }
// 使 (file显示) // 使 (file显示)
class ResourceRemoteImageProvider extends ImageProvider<ResourceRemoteImageProvider> { class ResourceRemoteImageProvider
extends ImageProvider<ResourceRemoteImageProvider> {
final String fileServer; final String fileServer;
final String path; final String path;
final double scale; final double scale;
@ -113,7 +126,8 @@ class ResourceRemoteImageProvider extends ImageProvider<ResourceRemoteImageProvi
} }
@override @override
Future<ResourceRemoteImageProvider> obtainKey(ImageConfiguration configuration) { Future<ResourceRemoteImageProvider> obtainKey(
ImageConfiguration configuration) {
return SynchronousFuture<ResourceRemoteImageProvider>(this); return SynchronousFuture<ResourceRemoteImageProvider>(this);
} }