final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
final Canvas canvas = Canvas(pictureRecorder);
final Paint paint = Paint()..color;
final double radius = size / 2;
final Path clipPath = Path();
clipPath.addRRect(RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.toDouble(), size.toDouble()),
Radius.circular(100)));
canvas.clipPath(clipPath);
final Uint8List imageUint8List =
await (await NetworkAssetBundle(Uri.parse(src)).load(src))
.buffer
.asUint8List();
final ui.Codec codec = await ui.instantiateImageCodec(imageUint8List);
final ui.FrameInfo imageFI = await codec.getNextFrame();
paintImage(
canvas: canvas,
rect: Rect.fromLTWH(0, 0, size.toDouble(), size.toDouble()),
image: imageFI.image);
if (addBorder) {
//draw Border
paint..color = borderColor;
paint..style = PaintingStyle.stroke;
paint..strokeWidth = borderSize;
canvas.drawCircle(Offset(radius, radius), radius, paint);
}
final _image = await pictureRecorder
.endRecording()
.toImage(size, (size * 1.1).toInt());
final rdata = await _image.toByteData(format: ui.ImageByteFormat.png);
The code is as above. Granted I just learned about canvas today I have been trying to piece together code that will fit an image inside a circle to use as a marker for a map. The resultant image however looks like this:
I need it to fit entirely.
Adding
Fit:Boxfit.cover
to my paint image worked. Credits to #spazhead's comment.
Related
I am creating custom images with flutters canvas and need them to be saved as 24 bit (rgb) pngs without the alpha channel.
I am using the flutter_file_dialog package for the image export.
But the final images are saved in the 32 bit format (rgba).
I have searched the flutter documentation but have not found an easy way to remove the alpha channel.
Would I have to do the removal myself by changing the uint8list?
import 'dart:ui' as ui;
Future<void> createImage() async {
final recorder = ui.PictureRecorder();
const Size size = Size(2000, 2000);
final Rect rect = Rect.fromLTRB(0, 0, size.width, size.height);
final canvas = Canvas(recorder, rect);
// canvas design ...
// capture canvas and save it as image
final picture = recorder.endRecording();
ui.Image newImage = await picture.toImage(size.width.toInt(), size.height.toInt());
ByteData pngBytes = await newImage.toByteData(format: ui.ImageByteFormat.png);
Uint8List uint8list = Uint8List.view(pngBytes.buffer);
// export the image
final params = SaveFileDialogParams(fileName: "image.png", data: uint8list);
final filePath = await FlutterFileDialog.saveFile(params: params);
}
I use this method for custom markers on a map. I added a custom round background on the marker by using Canvas and Paint and just put those 2 together before returning it back to BitmapDescriptor using code I found which I don't understand very much in depth. I believe that is the reason my background and marker is getting clipped into this square box no matter how I try to adjust it.
My goal is to slightly increase the round background behind the marker. But, it just takes the limits of the marker/icon. If I increase its size, it gets clipped. Also wondering how canvas operations still works even if this class does not have a CustomPainter mixin
class IconLoader {
var vd = VendorDatabase();
late var iconPath;
var convertedIcon;
int iconSize = 200;
Future <BitmapDescriptor?> getIconWithBackground(vendorId) async {
...fetch icon path....
return processIconWithBackground(iconPath, iconSize).then((onValue) async {
return convertedIcon = BitmapDescriptor.fromBytes(onValue!);
});
Future<Uint8List?> processIconWithBackground(String path, int width) async {
final imageData = await rootBundle.load(path); //original code: loads the image into ByteData
// Performs Witchcraft
ui.Codec codec = await ui.instantiateImageCodec(imageData.buffer.asUint8List(), targetWidth: width);
// Something about animation if image is gif? <-- I'm not dealing with animations, how can i get rid of this?
ui.FrameInfo fi = await codec.getNextFrame();
// Converts ByteData to Png format?
var process = (await fi.image.toByteData(format: ui.ImageByteFormat.png))?.buffer.asUint8List();
// finally makes an Image Type Object
final iconImage = await decodeImageFromList(process!);
final pictureRecorder = ui.PictureRecorder(); // starts to record
final canvas = ui.Canvas(pictureRecorder);
canvas.scale(1.5);
_paintCircleStroke(canvas);
final imagePaint = ui.Paint();
canvas.drawImage(iconImage, Offset.zero, Paint());
final drawing = pictureRecorder.endRecording();// ends recording the "drawing"
final image = await drawing.toImage(iconSize.round(), iconSize.round());//converts this drawing to an image
final bytes = await image.toByteData(format: ui.ImageByteFormat.png);
return bytes?.buffer.asUint8List();}
void _paintCircleStroke (ui.Canvas canvas) {
double _circleOffset = iconSize /2;
double _circleStrokeWidth = iconSize / 10;
double _outlineCircleWidth = iconSize /2 ;// (_circleStrokeWidth / 2) ;
// canvas.scale(1.0);
final paint = ui.Paint()
..style = ui.PaintingStyle.fill
..color = ui.Color .fromRGBO(132, 123, 123, 1);
// ..strokeWidth = _circleStrokeWidth;
canvas.drawCircle(ui.Offset(_circleOffset, _circleOffset), _outlineCircleWidth, paint);
There is the way to save the widget into transparent png and save it into gallery?
Thanks in advance.
Flutter draws every single pixel, so you can easily convert your widget to an image.
You need to follow these steps:
Edit -- first import path_provider link to pupspec.yaml and then follow this steps
Create a GlobalKey
final _globalKey = GlobalKey();
Create Uint8List
Uint8List pngBytes;
Wrap your widget with RepaintBoundary widget & pass in the key
RepaintBoundary(
key: _globalKey,
child: YourWidget(),
),
Create a method to convert your widget to image:
Future<void> _capturePng() async {
try {
final RenderRepaintBoundary boundary =
_globalKey.currentContext.findRenderObject();
final image = await boundary.toImage(pixelRatio: 2.0); // image quality
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
pngBytes = byteData.buffer.asUint8List();
} catch (e) {
print(e);
}
}
Convert your image to a file so that you can save it in your app
Future<File> convertImageToFile(Uint8List image) async {
final file = File(
'${(await
getTemporaryDirectory()).path}/${DateTime.now().millisecondsSinceEpoch}.png');
await file.writeAsBytes(image);
return file;
}
Working on a flutter project which needs to take photos and resize / crop images to 640 / 420 (15:10) before uploading to the server but they seem to be too lose too much quality and pixelated.
img.Image image = img.decodeImage(a2);
double width = (image.height / 10) * 15;
image = img.copyCrop(image, ((image.width - width) / 2).round(), 0, width.round(), image.height);
resized = img.encodeJpg(img.copyResize(image, width:640, interpolation: img.Interpolation.cubic));
currently using the camera to shoot at 720p (1280x720) and the image plugin for the crop and resize using average interpolation.
I'm mostly wondering if this is the best way to handle the image processing to keep the most quality or if there is a better method for this use case.
you should try this method, I've been using the following method for myself and it works like charm, the image isn't losing any quality either and the size is way too compressed
final _tempDir = await getTemporaryDirectory();
final _path = _tempDir.path;
im.Image imageFile = im.decodeImage(_file.readAsBytesSync());
mealID = Uuid().v4();
final compressedImageFile = File('$_path/img_$mealID.jpg')
..writeAsBytesSync(im.encodeJpg(imageFile, quality: 80));
setState(() {
_file = compressedImageFile;
});
the above method is for compressing image, and for capturing image from gallery/camera I'm using the method below.
Future getImageFromCamera() async {
Navigator.pop(context);
var image = await ImagePicker.pickImage(
source: ImageSource.camera,
imageQuality: 50,
/*you can set max height and/or width here as well just by setting value for maxHeight and/or maxWidth argument*/
);
setState(() {
_file = image;
});
await compressImage();
}
Future getImageFromGallery() async {
Navigator.pop(context);
var image = await ImagePicker.pickImage(
source: ImageSource.gallery,
imageQuality: 50,
);
setState(() {
_file = image;
});
await compressImage();
}
I have this demostrative code:
import 'dart:convert';
import 'dart:ui';
void main() async {
final pictureRecorder = PictureRecorder();
Canvas(pictureRecorder).drawCircle(
const Offset(100, 100),
100,
Paint()..color = const Color.fromARGB(255, 255, 0, 0),
);
final picture = pictureRecorder.endRecording();
final image = await picture.toImage(200, 200);
print('//1 before toByteData await');
final rawByteData = await image.toByteData(format: ImageByteFormat.rawRgba);
print('//2 after toByteData await');
final encoded = base64.encode(rawByteData.buffer.asUint8List());
print('//3 base64: $encoded');
}
The result on a mobile is, as expected:
I/flutter (18489): //1 before toByteData await
I/flutter (18489): //2 after toByteData await
I/flutter (18489): //3 base64: AAAAAAAAAAAAAAAAAAAAAA.. (all the base64 string)
The result on a web:
//1 before toByteData await
And never return the result, can somebody explainme how can I get the result in web?
Thank you very much for your time sharing your knowledge!
toByteData doesn't work for Flutter web on beta channel yet.
So workaround is to use html canvas to generate image.
import 'package:universal_html/html.dart' as html;
final htmlCanvas = html.CanvasElement(width: size.width.floor(), height: size.height.floor());
final html.CanvasRenderingContext2D context = htmlCanvas.getContext("2d");
context.fillStyle = 'white';
context.fillRect(0, 0, size.width, size.height);
for (int i = 0 ; i < _paths.length ; i++) {
context.strokeStyle = 'red';
context.lineWidth = _paths[i].key.strokeWidth;
context.beginPath();
context.moveTo(caculatedOffset.dx, caculatedOffset.dy);
for (int j = 1 ; j < _paths[i].value.length ; j ++) {
context.lineTo(caculatedOffset.dx, caculatedOffset.dy);
}
context.stroke();
}
final blob = await htmlCanvas.toBlob('image/jpeg', 0.8);