mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
improve image api & update version code
This commit is contained in:
@@ -224,7 +224,25 @@ let Convert = {
|
|||||||
key: key,
|
key: key,
|
||||||
isEncode: false
|
isEncode: false
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
/** Encode bytes to hex string
|
||||||
|
* @param bytes {ArrayBuffer}
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
hexEncode: (bytes) => {
|
||||||
|
const hexDigits = '0123456789abcdef';
|
||||||
|
const view = new Uint8Array(bytes);
|
||||||
|
let charCodes = new Uint8Array(view.length * 2);
|
||||||
|
let j = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < view.length; i++) {
|
||||||
|
let byte = view[i];
|
||||||
|
charCodes[j++] = hexDigits.charCodeAt((byte >> 4) & 0xF);
|
||||||
|
charCodes[j++] = hexDigits.charCodeAt(byte & 0xF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.fromCharCode(...charCodes);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1064,26 +1082,28 @@ class Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new image based on the current image without copying the data.
|
* fill [image] with range(srcX, srcY, width, height) to this image at (x, y)
|
||||||
* Modifying the new image will affect the current image.
|
|
||||||
* @param x
|
* @param x
|
||||||
* @param y
|
* @param y
|
||||||
|
* @param image
|
||||||
|
* @param srcX
|
||||||
|
* @param srcY
|
||||||
* @param width
|
* @param width
|
||||||
* @param height
|
* @param height
|
||||||
* @returns {Image|null}
|
|
||||||
*/
|
*/
|
||||||
subImage(x, y, width, height) {
|
fillImageRangeAt(x, y, image, srcX, srcY, width, height) {
|
||||||
let key = sendMessage({
|
sendMessage({
|
||||||
method: "image",
|
method: "image",
|
||||||
function: "subImage",
|
function: "fillImageRangeAt",
|
||||||
key: this.key,
|
key: this.key,
|
||||||
x: x,
|
x: x,
|
||||||
y: y,
|
y: y,
|
||||||
|
image: image.key,
|
||||||
|
srcX: srcX,
|
||||||
|
srcY: srcY,
|
||||||
width: width,
|
width: width,
|
||||||
height: height
|
height: height
|
||||||
})
|
})
|
||||||
if(key == null) return null;
|
|
||||||
return new Image(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
|
@@ -10,7 +10,7 @@ export "widget_utils.dart";
|
|||||||
export "context.dart";
|
export "context.dart";
|
||||||
|
|
||||||
class _App {
|
class _App {
|
||||||
final version = "1.0.1";
|
final version = "1.0.2";
|
||||||
|
|
||||||
bool get isAndroid => Platform.isAndroid;
|
bool get isAndroid => Platform.isAndroid;
|
||||||
|
|
||||||
|
@@ -87,17 +87,16 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
|||||||
return await decode(buffer);
|
return await decode(buffer);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await CacheManager().delete(this.key);
|
await CacheManager().delete(this.key);
|
||||||
Object error = e;
|
|
||||||
if (data.length < 2 * 1024) {
|
if (data.length < 2 * 1024) {
|
||||||
// data is too short, it's likely that the data is text, not image
|
// data is too short, it's likely that the data is text, not image
|
||||||
try {
|
try {
|
||||||
var text = const Utf8Codec(allowMalformed: false).decoder.convert(data);
|
var text = const Utf8Codec(allowMalformed: false).decoder.convert(data);
|
||||||
error = Exception("Expected image data, but got text: $text");
|
throw Exception("Expected image data, but got text: $text");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw error;
|
rethrow;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
scheduleMicrotask(() {
|
scheduleMicrotask(() {
|
||||||
|
@@ -3,6 +3,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:venera/foundation/cache_manager.dart';
|
import 'package:venera/foundation/cache_manager.dart';
|
||||||
import 'package:venera/foundation/comic_source/comic_source.dart';
|
import 'package:venera/foundation/comic_source/comic_source.dart';
|
||||||
import 'package:venera/foundation/consts.dart';
|
import 'package:venera/foundation/consts.dart';
|
||||||
|
import 'package:venera/utils/image.dart';
|
||||||
|
|
||||||
import 'app_dio.dart';
|
import 'app_dio.dart';
|
||||||
|
|
||||||
@@ -27,8 +28,8 @@ class ImageDownloader {
|
|||||||
configs = comicSource?.getThumbnailLoadingConfig?.call(url) ?? {};
|
configs = comicSource?.getThumbnailLoadingConfig?.call(url) ?? {};
|
||||||
}
|
}
|
||||||
configs['headers'] ??= {};
|
configs['headers'] ??= {};
|
||||||
if(configs['headers']['user-agent'] == null
|
if (configs['headers']['user-agent'] == null &&
|
||||||
&& configs['headers']['User-Agent'] == null) {
|
configs['headers']['User-Agent'] == null) {
|
||||||
configs['headers']['user-agent'] = webUA;
|
configs['headers']['user-agent'] = webUA;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,11 +121,22 @@ class ImageDownloader {
|
|||||||
buffer = configs['onResponse'](buffer);
|
buffer = configs['onResponse'](buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
await CacheManager().writeCache(cacheKey, buffer);
|
var data = Uint8List.fromList(buffer);
|
||||||
|
buffer.clear();
|
||||||
|
|
||||||
|
if (configs['modifyImage'] != null) {
|
||||||
|
var newData = await modifyImageWithScript(
|
||||||
|
data,
|
||||||
|
configs['modifyImage'],
|
||||||
|
);
|
||||||
|
data = newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
await CacheManager().writeCache(cacheKey, data);
|
||||||
yield ImageDownloadProgress(
|
yield ImageDownloadProgress(
|
||||||
currentBytes: buffer.length,
|
currentBytes: data.length,
|
||||||
totalBytes: buffer.length,
|
totalBytes: data.length,
|
||||||
imageBytes: Uint8List.fromList(buffer),
|
imageBytes: data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -161,71 +161,76 @@ class _BodyState extends State<_Body> {
|
|||||||
for (var item in source.settings!.entries) {
|
for (var item in source.settings!.entries) {
|
||||||
var key = item.key;
|
var key = item.key;
|
||||||
String type = item.value['type'];
|
String type = item.value['type'];
|
||||||
if (type == "select") {
|
try {
|
||||||
var current = source.data['settings'][key];
|
if (type == "select") {
|
||||||
if (current == null) {
|
var current = source.data['settings'][key];
|
||||||
var d = item.value['default'];
|
if (current == null) {
|
||||||
for (var option in item.value['options']) {
|
var d = item.value['default'];
|
||||||
if (option['value'] == d) {
|
for (var option in item.value['options']) {
|
||||||
current = option['text'] ?? option['value'];
|
if (option['value'] == d) {
|
||||||
break;
|
current = option['text'] ?? option['value'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
yield ListTile(
|
||||||
|
title: Text((item.value['title'] as String).ts(source.key)),
|
||||||
|
trailing: Select(
|
||||||
|
current: (current as String).ts(source.key),
|
||||||
|
values: (item.value['options'] as List)
|
||||||
|
.map<String>(
|
||||||
|
(e) => ((e['text'] ?? e['value']) as String).ts(source.key))
|
||||||
|
.toList(),
|
||||||
|
onTap: (i) {
|
||||||
|
source.data['settings'][key] = item.value['options'][i]['value'];
|
||||||
|
source.saveData();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (type == "switch") {
|
||||||
|
var current = source.data['settings'][key] ?? item.value['default'];
|
||||||
|
yield ListTile(
|
||||||
|
title: Text((item.value['title'] as String).ts(source.key)),
|
||||||
|
trailing: Switch(
|
||||||
|
value: current,
|
||||||
|
onChanged: (v) {
|
||||||
|
source.data['settings'][key] = v;
|
||||||
|
source.saveData();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (type == "input") {
|
||||||
|
var current =
|
||||||
|
source.data['settings'][key] ?? item.value['default'] ?? '';
|
||||||
|
yield ListTile(
|
||||||
|
title: Text((item.value['title'] as String).ts(source.key)),
|
||||||
|
subtitle: Text(current, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Icons.edit),
|
||||||
|
onPressed: () {
|
||||||
|
showInputDialog(
|
||||||
|
context: context,
|
||||||
|
title: (item.value['title'] as String).ts(source.key),
|
||||||
|
initialValue: current,
|
||||||
|
inputValidator: item.value['validator'] == null
|
||||||
|
? null
|
||||||
|
: RegExp(item.value['validator']),
|
||||||
|
onConfirm: (value) {
|
||||||
|
source.data['settings'][key] = value;
|
||||||
|
source.saveData();
|
||||||
|
setState(() {});
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
yield ListTile(
|
}
|
||||||
title: Text((item.value['title'] as String).ts(source.key)),
|
catch(e, s) {
|
||||||
trailing: Select(
|
Log.error("ComicSourcePage", "Failed to build a setting\n$e\n$s");
|
||||||
current: (current as String).ts(source.key),
|
|
||||||
values: (item.value['options'] as List)
|
|
||||||
.map<String>(
|
|
||||||
(e) => ((e['text'] ?? e['value']) as String).ts(source.key))
|
|
||||||
.toList(),
|
|
||||||
onTap: (i) {
|
|
||||||
source.data['settings'][key] = item.value['options'][i]['value'];
|
|
||||||
source.saveData();
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (type == "switch") {
|
|
||||||
var current = source.data['settings'][key] ?? item.value['default'];
|
|
||||||
yield ListTile(
|
|
||||||
title: Text((item.value['title'] as String).ts(source.key)),
|
|
||||||
trailing: Switch(
|
|
||||||
value: current,
|
|
||||||
onChanged: (v) {
|
|
||||||
source.data['settings'][key] = v;
|
|
||||||
source.saveData();
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (type == "input") {
|
|
||||||
var current =
|
|
||||||
source.data['settings'][key] ?? item.value['default'] ?? '';
|
|
||||||
yield ListTile(
|
|
||||||
title: Text((item.value['title'] as String).ts(source.key)),
|
|
||||||
subtitle: Text(current, maxLines: 1, overflow: TextOverflow.ellipsis),
|
|
||||||
trailing: IconButton(
|
|
||||||
icon: const Icon(Icons.edit),
|
|
||||||
onPressed: () {
|
|
||||||
showInputDialog(
|
|
||||||
context: context,
|
|
||||||
title: (item.value['title'] as String).ts(source.key),
|
|
||||||
initialValue: current,
|
|
||||||
inputValidator: item.value['validator'] == null
|
|
||||||
? null
|
|
||||||
: RegExp(item.value['validator']),
|
|
||||||
onConfirm: (value) {
|
|
||||||
source.data['settings'][key] = value;
|
|
||||||
source.saveData();
|
|
||||||
setState(() {});
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -389,7 +389,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
|
|
||||||
Widget buildPageInfoText() {
|
Widget buildPageInfoText() {
|
||||||
var epName = context.reader.widget.chapters?.values
|
var epName = context.reader.widget.chapters?.values
|
||||||
.elementAt(context.reader.chapter - 1) ??
|
.elementAtOrNull(context.reader.chapter - 1) ??
|
||||||
"E${context.reader.chapter}";
|
"E${context.reader.chapter}";
|
||||||
if (epName.length > 8) {
|
if (epName.length > 8) {
|
||||||
epName = "${epName.substring(0, 8)}...";
|
epName = "${epName.substring(0, 8)}...";
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:ffi';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
@@ -12,7 +13,12 @@ class Image {
|
|||||||
|
|
||||||
final int height;
|
final int height;
|
||||||
|
|
||||||
Image(this._data, this.width, this.height);
|
Image(this._data, this.width, this.height) {
|
||||||
|
if (_data.length != width * height) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid argument: data length must be equal to width * height.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Image.empty(this.width, this.height) : _data = Uint32List(width * height);
|
Image.empty(this.width, this.height) : _data = Uint32List(width * height);
|
||||||
|
|
||||||
@@ -25,7 +31,7 @@ class Image {
|
|||||||
throw Exception('Failed to decode image');
|
throw Exception('Failed to decode image');
|
||||||
}
|
}
|
||||||
var image = Image(
|
var image = Image(
|
||||||
Uint32List.fromList(info.buffer.asUint32List()),
|
info.buffer.asUint32List(),
|
||||||
frame.image.width,
|
frame.image.width,
|
||||||
frame.image.height,
|
frame.image.height,
|
||||||
);
|
);
|
||||||
@@ -34,6 +40,20 @@ class Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Image copyRange(int x, int y, int width, int height) {
|
Image copyRange(int x, int y, int width, int height) {
|
||||||
|
if (width + x > this.width) {
|
||||||
|
throw ArgumentError('''
|
||||||
|
Invalid argument: x + width must be less than or equal to the image width.
|
||||||
|
x: $x, width: $width, image width: ${this.width}
|
||||||
|
'''
|
||||||
|
.trim());
|
||||||
|
}
|
||||||
|
if (height + y > this.height) {
|
||||||
|
throw ArgumentError('''
|
||||||
|
Invalid argument: y + height must be less than or equal to the image height.
|
||||||
|
y: $y, height: $height, image height: ${this.height}
|
||||||
|
'''
|
||||||
|
.trim());
|
||||||
|
}
|
||||||
var data = Uint32List(width * height);
|
var data = Uint32List(width * height);
|
||||||
for (var j = 0; j < height; j++) {
|
for (var j = 0; j < height; j++) {
|
||||||
for (var i = 0; i < width; i++) {
|
for (var i = 0; i < width; i++) {
|
||||||
@@ -44,6 +64,20 @@ class Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fillImageAt(int x, int y, Image image) {
|
void fillImageAt(int x, int y, Image image) {
|
||||||
|
if (x + image.width > width) {
|
||||||
|
throw ArgumentError('''
|
||||||
|
Invalid argument: x + image width must be less than or equal to the image width.
|
||||||
|
x: $x, image width: ${image.width}, image width: $width
|
||||||
|
'''
|
||||||
|
.trim());
|
||||||
|
}
|
||||||
|
if (y + image.height > height) {
|
||||||
|
throw ArgumentError('''
|
||||||
|
Invalid argument: y + image height must be less than or equal to the image height.
|
||||||
|
y: $y, image height: ${image.height}, image height: $height
|
||||||
|
'''
|
||||||
|
.trim());
|
||||||
|
}
|
||||||
for (var j = 0; j < image.height && (j + y) < height; j++) {
|
for (var j = 0; j < image.height && (j + y) < height; j++) {
|
||||||
for (var i = 0; i < image.width && (i + x) < width; i++) {
|
for (var i = 0; i < image.width && (i + x) < width; i++) {
|
||||||
_data[(j + y) * width + i + x] = image._data[j * image.width + i];
|
_data[(j + y) * width + i + x] = image._data[j * image.width + i];
|
||||||
@@ -51,6 +85,44 @@ class Image {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fillImageRangeAt(
|
||||||
|
int x, int y, Image image, int srcX, int srcY, int width, int height) {
|
||||||
|
if (x + width > this.width) {
|
||||||
|
throw ArgumentError('''
|
||||||
|
Invalid argument: x + width must be less than or equal to the image width.
|
||||||
|
x: $x, width: $width, image width: ${this.width}
|
||||||
|
'''
|
||||||
|
.trim());
|
||||||
|
}
|
||||||
|
if (y + height > this.height) {
|
||||||
|
throw ArgumentError('''
|
||||||
|
Invalid argument: y + height must be less than or equal to the image height.
|
||||||
|
y: $y, height: $height, image height: ${this.height}
|
||||||
|
'''
|
||||||
|
.trim());
|
||||||
|
}
|
||||||
|
if (srcX + width > image.width) {
|
||||||
|
throw ArgumentError('''
|
||||||
|
Invalid argument: srcX + width must be less than or equal to the image width.
|
||||||
|
srcX: $srcX, width: $width, image width: ${image.width}
|
||||||
|
'''
|
||||||
|
.trim());
|
||||||
|
}
|
||||||
|
if (srcY + height > image.height) {
|
||||||
|
throw ArgumentError('''
|
||||||
|
Invalid argument: srcY + height must be less than or equal to the image height.
|
||||||
|
srcY: $srcY, height: $height, image height: ${image.height}
|
||||||
|
'''
|
||||||
|
.trim());
|
||||||
|
}
|
||||||
|
for (var j = 0; j < height; j++) {
|
||||||
|
for (var i = 0; i < width; i++) {
|
||||||
|
_data[(j + y) * this.width + i + x] =
|
||||||
|
image._data[(j + srcY) * image.width + i + srcX];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Image copyAndRotate90() {
|
Image copyAndRotate90() {
|
||||||
var data = Uint32List(width * height);
|
var data = Uint32List(width * height);
|
||||||
for (var j = 0; j < height; j++) {
|
for (var j = 0; j < height; j++) {
|
||||||
@@ -62,28 +134,37 @@ class Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Color getPixel(int x, int y) {
|
Color getPixel(int x, int y) {
|
||||||
|
if (x < 0 || x >= width) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid argument: x must be in the range of [0, $width).');
|
||||||
|
}
|
||||||
|
if (y < 0 || y >= height) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid argument: y must be in the range of [0, $height).');
|
||||||
|
}
|
||||||
return Color.fromValue(_data[y * width + x]);
|
return Color.fromValue(_data[y * width + x]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPixel(int x, int y, Color color) {
|
void setPixel(int x, int y, Color color) {
|
||||||
|
if (x < 0 || x >= width) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid argument: x must be in the range of [0, $width).');
|
||||||
|
}
|
||||||
|
if (y < 0 || y >= height) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid argument: y must be in the range of [0, $height).');
|
||||||
|
}
|
||||||
_data[y * width + x] = color.value;
|
_data[y * width + x] = color.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image subImage(int x, int y, int width, int height) {
|
|
||||||
var data = Uint32List.sublistView(
|
|
||||||
_data,
|
|
||||||
y * this.width + x,
|
|
||||||
width * height,
|
|
||||||
);
|
|
||||||
return Image(data, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8List encodePng() {
|
Uint8List encodePng() {
|
||||||
return lodepng.encodePng(lodepng.Image(
|
var data = lodepng.encodePngToPointer(lodepng.Image(
|
||||||
_data.buffer.asUint8List(),
|
_data.buffer.asUint8List(),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
));
|
));
|
||||||
|
return Pointer<Uint8>.fromAddress(data.address).asTypedList(data.length,
|
||||||
|
finalizer: lodepng.ByteBuffer.finalizer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,16 +247,21 @@ class JsEngine {
|
|||||||
if (image2 == null) return null;
|
if (image2 == null) return null;
|
||||||
image.fillImageAt(x, y, image2);
|
image.fillImageAt(x, y, image2);
|
||||||
return null;
|
return null;
|
||||||
case 'subImage':
|
case 'fillImageRangeAt':
|
||||||
var key = message['key'];
|
var key = message['key'];
|
||||||
var image = images[key];
|
var image = images[key];
|
||||||
if (image == null) return null;
|
if (image == null) return null;
|
||||||
var x = message['x'];
|
var x = message['x'];
|
||||||
var y = message['y'];
|
var y = message['y'];
|
||||||
|
var key2 = message['image'];
|
||||||
|
var image2 = images[key2];
|
||||||
|
if (image2 == null) return null;
|
||||||
|
var srcX = message['srcX'];
|
||||||
|
var srcY = message['srcY'];
|
||||||
var width = message['width'];
|
var width = message['width'];
|
||||||
var height = message['height'];
|
var height = message['height'];
|
||||||
var newImage = image.subImage(x, y, width, height);
|
image.fillImageRangeAt(x, y, image2, srcX, srcY, width, height);
|
||||||
return setImage(newImage);
|
return null;
|
||||||
case 'getWidth':
|
case 'getWidth':
|
||||||
var key = message['key'];
|
var key = message['key'];
|
||||||
var image = images[key];
|
var image = images[key];
|
||||||
@@ -213,16 +299,18 @@ Future<Uint8List> modifyImageWithScript(Uint8List data, String script) async {
|
|||||||
jsEngine.runCode(script);
|
jsEngine.runCode(script);
|
||||||
var key = jsEngine.setImage(image);
|
var key = jsEngine.setImage(image);
|
||||||
var res = jsEngine.runCode('''
|
var res = jsEngine.runCode('''
|
||||||
let image = new Image($key);
|
let func = () => {
|
||||||
let result = modifyImage(image);
|
let image = new Image($key);
|
||||||
return result.key;
|
let result = modifyImage(image);
|
||||||
''');
|
return result.key;
|
||||||
|
}
|
||||||
|
func();
|
||||||
|
''');
|
||||||
var newImage = jsEngine.images[res];
|
var newImage = jsEngine.images[res];
|
||||||
var data = newImage!.encodePng();
|
var data = newImage!.encodePng();
|
||||||
return data;
|
return Uint8List.fromList(data);
|
||||||
});
|
});
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
_tasksCount--;
|
_tasksCount--;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -461,7 +461,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: "115b896a04c270a8d6d5d7bea09dcd04047bfad3"
|
resolved-ref: "5223cf4ce8aad1c2315db0093db3cc5c6c7191a8"
|
||||||
url: "https://github.com/venera-app/lodepng_flutter"
|
url: "https://github.com/venera-app/lodepng_flutter"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
@@ -2,7 +2,7 @@ name: venera
|
|||||||
description: "A comic app."
|
description: "A comic app."
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 1.0.1+101
|
version: 1.0.2+102
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.5.0 <4.0.0'
|
sdk: '>=3.5.0 <4.0.0'
|
||||||
|
Reference in New Issue
Block a user