-
Notifications
You must be signed in to change notification settings - Fork 307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Request: cutout for embedded image #190
Comments
This can be achieved by creating your own copy of QrPainter and replacing the @override
void paint(Canvas canvas, Size size) {
// if the widget has a zero size side then we cannot continue painting.
if (size.shortestSide == 0) {
print("[QR] WARN: width or height is zero. You should set a 'size' value"
"or nest this painter in a Widget that defines a non-zero size");
return;
}
final backgroundPaint = Paint()..color = Color(0xFFFFFFFF)..style = PaintingStyle.fill;
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), backgroundPaint);
final paintMetrics = _PaintMetrics(
containerSize: size.shortestSide,
moduleCount: _qr!.moduleCount,
gapSize: (gapless ? 0 : _gapSize),
);
// draw the finder pattern elements
_drawFinderPatternItem(FinderPatternPosition.topLeft, canvas, paintMetrics);
_drawFinderPatternItem(
FinderPatternPosition.bottomLeft, canvas, paintMetrics);
_drawFinderPatternItem(
FinderPatternPosition.topRight, canvas, paintMetrics);
// DEBUG: draw the inner content boundary
/*final paint = Paint()..style = ui.PaintingStyle.stroke;
paint.strokeWidth = 1;
paint.color = const Color(0x55222222);
canvas.drawRect(
Rect.fromLTWH(paintMetrics.inset, paintMetrics.inset,
paintMetrics.innerContentSize, paintMetrics.innerContentSize),
paint);*/
double left;
double top;
final gap = !gapless ? _gapSize : 0;
// get the painters for the pixel information
final pixelPaint = _paintCache.firstPaint(QrCodeElement.codePixel);
pixelPaint!.color = dataModuleStyle.color!;
Paint? emptyPixelPaint;
emptyPixelPaint = _paintCache.firstPaint(QrCodeElement.codePixelEmpty);
emptyPixelPaint!.color = Colors.transparent;
//Determine the coordinates of the embedded image so we'll know where to place
//it and where not to paint qrcode's dataModules
Offset position = Offset(0, 0);
Size imageSize = Size(0, 0);
Rect imageRect = Rect.zero;
if (embeddedImage != null) {
final originalSize = Size(
embeddedImage!.width.toDouble(),
embeddedImage!.height.toDouble(),
);
final requestedSize =
embeddedImageStyle != null ? embeddedImageStyle!.size : null;
imageSize = _scaledAspectSize(size, originalSize, requestedSize);
position = Offset(
(size.width - imageSize.width) / 2.0,
(size.height - imageSize.height) / 2.0,
);
imageRect = Rect.fromLTWH(position.dx, position.dy,
imageSize.width, imageSize.height);
}
// DEBUG: draw the embedded image's boundary
/*final paint = Paint()..style = ui.PaintingStyle.stroke;
paint.strokeWidth = 1;
paint.color = const Color(0x55222222);
canvas.drawRect(
Rect.fromLTWH(position.dx, position.dy,
imageSize.width, imageSize.height),
paint);*/
for (var x = 0; x < _qr!.moduleCount; x++) {
for (var y = 0; y < _qr!.moduleCount; y++) {
// draw the finder patterns independently
if (_isFinderPatternPosition(x, y)) continue;
final paint = _qr!.isDark(y, x) ? pixelPaint : emptyPixelPaint;
// paint a pixel
left = paintMetrics.inset + (x * (paintMetrics.pixelSize + gap));
top = paintMetrics.inset + (y * (paintMetrics.pixelSize + gap));
var pixelHTweak = 0.0;
var pixelVTweak = 0.0;
if (gapless && _hasAdjacentHorizontalPixel(x, y, _qr!.moduleCount)) {
pixelHTweak = 0.5;
}
if (gapless && _hasAdjacentVerticalPixel(x, y, _qr!.moduleCount)) {
pixelVTweak = 0.5;
}
final squareRect = Rect.fromLTWH(
left,
top,
paintMetrics.pixelSize + pixelHTweak,
paintMetrics.pixelSize + pixelVTweak,
);
//If dataModule overlaps innerContent (image) area -> don't paint it
if(imageRect.overlaps(squareRect)) continue;
if (dataModuleStyle.dataModuleShape == QrDataModuleShape.square) {
canvas.drawRect(squareRect, paint);
} else {
final roundedRect = RRect.fromRectAndRadius(squareRect,
Radius.circular(paintMetrics.pixelSize + pixelHTweak));
canvas.drawRRect(roundedRect, paint);
}
}
}
if (embeddedImage != null) {
// draw the image overlay.
_drawImageOverlay(canvas, position, imageSize, embeddedImageStyle);
}
} I added a couple of comments to underline which lines achieve the result you require. |
@SeriousMonk |
I believe so, but only if you created a copy of qr_painter.dart from a commit after package version was bumped up to 4.0.1 (which hasn't been released on pub.dev yet). In that commit the dependency on the qr package was updated to version 3.0.0 and that removes the isDark method from QrCode class. I should also note that flutter 3.3.x has a bug where the embedded image is not rendered on web when trying to download the qrcode using |
Thanks for your response. I've use your solution using 3.0.0 qr_painter code, and I change the _qr!.isDark to _qrImage.isDark and it works there is a gap around the image but when I scan the qr it's not working, the response is no qr code found. Maybe I should use your next solution to use 4.0.0 code. And currently I just use it on mobile. |
There is no need to go as back as to version 3.0.0. I am using files from version 4.0.0 of the package. Try using those with my solution. (NB version 3.0.0 I mentioned in my previous message was referring to the qr package, not qr_flutter package) Another possible reason is that your qr code is too small or the correction level is too low; try setting |
Woah, you right! In my painter I just change this
Thanks!! @SeriousMonk |
This should be in the main repo! Doesn't make sense to be able to use an image without paddings to the QRCode content. |
It would be great to have a cutout area for the embedded image, rather than for it to be laid on top of the qr code. Like:
Of course, you could add a white background to the embedded image, but some of the individual data modules may get cut in half.
The text was updated successfully, but these errors were encountered: