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.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.os.Build
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.provider.MediaStore
import android.util.DisplayMetrics
import android.util.Log
import android.view.Display
import android.view.KeyEvent
@ -23,6 +25,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.sync.Mutex
import mobile.Mobile
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
@ -100,6 +103,7 @@ class MainActivity : FlutterActivity() {
// 获取可以迁移数据地址
"androidGetExtendDirs" -> androidGetExtendDirs()
"androidSecureFlag" -> androidSecureFlag(call.argument("flag")!!)
"convertToPNG" -> convertToPNG(call.argument("path")!!)
else -> {
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,
Future<T?> chooseListDialog<T>(
BuildContext context,
String title,
List<T> items,
) async {
BuildContext context, String title, List<T> items,
{String? tips}) async {
List<Widget> widgets = [];
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>(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: Text(title),
children: items
.map((e) => SimpleDialogOption(
onPressed: () {
Navigator.of(context).pop(e);
},
child: Text('$e'),
))
.toList(),
children: widgets,
);
},
);

View File

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:pikapi/basic/Entities.dart';
@ -607,4 +608,9 @@ class Method {
"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/ChooserRoot.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/DownloadThreadCount.dart';
import 'package:pikapi/basic/config/FullScreenAction.dart';
@ -67,6 +68,7 @@ class _InitScreenState extends State<InitScreen> {
await initDownloadAndExportPath();
await initAndroidSecureFlag();
await initDownloadThreadCount();
await initConvertToPNG();
// , token失效重新登录, 1
if (await method.preLogin()) {
// 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/ChooserRoot.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/DownloadThreadCount.dart';
import 'package:pikapi/basic/config/FullScreenAction.dart';
@ -37,6 +38,7 @@ class SettingsScreen extends StatelessWidget {
NetworkSetting(),
Divider(),
qualitySetting(),
convertToPNGSetting(),
readerTypeSetting(),
readerDirectionSetting(),
autoFullScreenSetting(),

View File

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