Request for clarification using Canvas() and Paint() in flutter - flutter

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);

Related

Save uint8list as 24 bit rgb png

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);
}

How to convert Image Data type from imageUrl

I need to convert this watermark(from assets) image and imageurl(getting from API) to Image data type, because ui.drawImage takes only Image data types
ui.Image? watermarkImage = ui.decodeImage('assets/images/watermark.png');// getting error
ui.Image? lookbookImage = ui.decodeImage(imageurl); // getting error
final originalImage = imgData;
ui.Image image = ui.Image(160, 50);
ui.drawImage(image, watermarkImage);
First you need to get the image bytes
// network
final response = await http.Client().get(Uri.parse(imageurl));
final bytes = response.bodyBytes;
//assets
final byteData = await rootBundle.load('assets/images/watermark.png');
final bytes = byteData.buffer.asUint8List();
Then you can decode ui.Image from the image bytes
final image = await decodeImageFromList(bytes);

Fitting Image inside a circle using canvas flutter

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.

How to resize image using multi_image_picker in Flutter?

I'm using multi_image_picker package to pick images and upload to server, but before uploading I want to resize images. I'm trying to accomplish it using dart.ui but having a problem:
//assets is List<Asset> from MultiImagePicker.pickImages
assets.forEach((asset) {
Future<ByteData> byteData = asset.getByteData();
byteData.then((d) async {
List<int> imageData = d.buffer.asUint8List();
String b64 =base64Encode(imageData);
print(b64); // prints [/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE...
//if i send b64 to server then decode it and save as img it's working well
//Resize
ui.instantiateImageCodec(imageData,targetHeight: 800, targetWidth: 600)
.then((codec) {
codec.getNextFrame().then((frameInfo) async {
ui.Image i = frameInfo.image;
ByteData bytes = await i.toByteData();
List<int> resizedImageData = bytes.buffer.asUint8List();
String rb64 = base64Encode(resizedImageData);
print(rb64); // prints too many backslashes:[k5KO/5qWk/+ZlZL/mpaT/5uXlP+alpP/mJSR/5iUkf+YlJH/mZSR/5uWk/+blpP/n5qX/6GcmP+gm5f/oZyY/6GcmP+fmpb/nZi..
//If i send rb64 to server then server cannot decode and save it.
});
});
});
});
This is the function I normally use to resize:
import 'dart:ui' as skia;
Future<skia.Image> resizeImage(String path, {int width, int height}) async {
Uint8List data = await File(path).readAsBytes();
final codec = await skia.instantiateImageCodec(data, targetWidth: width, targetHeight: height);
final frame = await codec.getNextFrame();
return frame.image;
}
As I mentioned in the comment, this is currently not working in Flutter Web but it's due to a bug that will be fixed soon, hopefully.

Flutter image processing causing pixelation

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();
}