feat: Tipos de qrcodes (bolinhas, arredondado ou quadrado)
This commit is contained in:
parent
8541e68711
commit
eb0751cb16
@ -31,7 +31,9 @@
|
||||
"Read(//mnt/c/vscode/**)",
|
||||
"Read(//mnt/c/**)",
|
||||
"Bash(chmod +x /mnt/c/vscode/qrrapido/Scripts/update-plans.sh)",
|
||||
"Bash(netstat -tln)"
|
||||
"Bash(netstat -tln)",
|
||||
"Bash(npm install)",
|
||||
"Bash(npm install:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
||||
@ -679,21 +679,230 @@ namespace QRRapidoApp.Services
|
||||
|
||||
private byte[] ApplyCornerStyle(byte[] qrBytes, string cornerStyle, int targetSize)
|
||||
{
|
||||
if (cornerStyle == "square" || string.IsNullOrEmpty(cornerStyle))
|
||||
{
|
||||
return qrBytes; // No processing needed for square style
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Simplified implementation for cross-platform compatibility
|
||||
// The complex corner styling can be re-implemented later using ImageSharp drawing primitives
|
||||
_logger.LogInformation("Corner style '{CornerStyle}' temporarily disabled for cross-platform compatibility. Returning original QR code.", cornerStyle);
|
||||
_logger.LogInformation("Applying corner style '{CornerStyle}' to QR code", cornerStyle);
|
||||
|
||||
using var qrImage = Image.Load<Rgba32>(qrBytes);
|
||||
int width = qrImage.Width;
|
||||
int height = qrImage.Height;
|
||||
|
||||
// Detect module size by scanning the image
|
||||
int moduleSize = DetectModuleSize(qrImage);
|
||||
if (moduleSize <= 0)
|
||||
{
|
||||
_logger.LogWarning("Could not detect module size, returning original QR code");
|
||||
return qrBytes;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Detected module size: {ModuleSize}px for {Width}x{Height} QR code", moduleSize, width, height);
|
||||
|
||||
// Create a new image with styled modules
|
||||
using var styledImage = new Image<Rgba32>(width, height);
|
||||
|
||||
// Process each module
|
||||
for (int y = 0; y < height; y += moduleSize)
|
||||
{
|
||||
for (int x = 0; x < width; x += moduleSize)
|
||||
{
|
||||
// Check if this module is dark (part of the QR pattern)
|
||||
var centerPixel = qrImage[x + moduleSize / 2, y + moduleSize / 2];
|
||||
bool isDark = centerPixel.R < 128; // Dark if RGB < 128
|
||||
|
||||
if (isDark)
|
||||
{
|
||||
// Draw styled module based on cornerStyle
|
||||
DrawStyledModule(styledImage, x, y, moduleSize, cornerStyle, centerPixel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw background (light modules)
|
||||
DrawRectangle(styledImage, x, y, moduleSize, moduleSize, centerPixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to bytes
|
||||
using var outputStream = new MemoryStream();
|
||||
styledImage.SaveAsPng(outputStream);
|
||||
_logger.LogInformation("Successfully applied corner style '{CornerStyle}'", cornerStyle);
|
||||
return outputStream.ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error applying corner style {CornerStyle}, returning original QR code", cornerStyle);
|
||||
// Return original QR code if styling fails
|
||||
return qrBytes;
|
||||
}
|
||||
}
|
||||
|
||||
private int DetectModuleSize(Image<Rgba32> qrImage)
|
||||
{
|
||||
// Scan horizontally from left to find the first transition from light to dark
|
||||
int width = qrImage.Width;
|
||||
int height = qrImage.Height;
|
||||
int midY = height / 2;
|
||||
|
||||
bool wasLight = true;
|
||||
int darkStart = -1;
|
||||
int darkEnd = -1;
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
var pixel = qrImage[x, midY];
|
||||
bool isLight = pixel.R >= 128;
|
||||
|
||||
if (wasLight && !isLight)
|
||||
{
|
||||
// Transition from light to dark
|
||||
darkStart = x;
|
||||
}
|
||||
else if (!wasLight && isLight && darkStart >= 0)
|
||||
{
|
||||
// Transition from dark to light
|
||||
darkEnd = x;
|
||||
break;
|
||||
}
|
||||
|
||||
wasLight = isLight;
|
||||
}
|
||||
|
||||
if (darkStart >= 0 && darkEnd > darkStart)
|
||||
{
|
||||
return darkEnd - darkStart;
|
||||
}
|
||||
|
||||
// Fallback: estimate based on typical QR code structure (21-177 modules)
|
||||
// Most common is 25-29 modules for QR version 1-5
|
||||
return width / 25; // Rough estimate
|
||||
}
|
||||
|
||||
private void DrawStyledModule(Image<Rgba32> image, int x, int y, int size, string style, Rgba32 color)
|
||||
{
|
||||
switch (style.ToLower())
|
||||
{
|
||||
case "rounded":
|
||||
DrawRoundedRectangle(image, x, y, size, size, size / 4, color);
|
||||
break;
|
||||
case "circle":
|
||||
DrawCircle(image, x + size / 2, y + size / 2, size / 2, color);
|
||||
break;
|
||||
default:
|
||||
DrawRectangle(image, x, y, size, size, color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawRectangle(Image<Rgba32> image, int x, int y, int width, int height, Rgba32 color)
|
||||
{
|
||||
for (int dy = 0; dy < height; dy++)
|
||||
{
|
||||
for (int dx = 0; dx < width; dx++)
|
||||
{
|
||||
int px = x + dx;
|
||||
int py = y + dy;
|
||||
if (px >= 0 && px < image.Width && py >= 0 && py < image.Height)
|
||||
{
|
||||
image[px, py] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawRoundedRectangle(Image<Rgba32> image, int x, int y, int width, int height, int radius, Rgba32 color)
|
||||
{
|
||||
for (int dy = 0; dy < height; dy++)
|
||||
{
|
||||
for (int dx = 0; dx < width; dx++)
|
||||
{
|
||||
int px = x + dx;
|
||||
int py = y + dy;
|
||||
|
||||
if (px < 0 || px >= image.Width || py < 0 || py >= image.Height)
|
||||
continue;
|
||||
|
||||
// Check if pixel is inside rounded rectangle
|
||||
bool inCorner = false;
|
||||
int distX = 0, distY = 0;
|
||||
|
||||
// Top-left corner
|
||||
if (dx < radius && dy < radius)
|
||||
{
|
||||
distX = radius - dx;
|
||||
distY = radius - dy;
|
||||
inCorner = true;
|
||||
}
|
||||
// Top-right corner
|
||||
else if (dx >= width - radius && dy < radius)
|
||||
{
|
||||
distX = dx - (width - radius - 1);
|
||||
distY = radius - dy;
|
||||
inCorner = true;
|
||||
}
|
||||
// Bottom-left corner
|
||||
else if (dx < radius && dy >= height - radius)
|
||||
{
|
||||
distX = radius - dx;
|
||||
distY = dy - (height - radius - 1);
|
||||
inCorner = true;
|
||||
}
|
||||
// Bottom-right corner
|
||||
else if (dx >= width - radius && dy >= height - radius)
|
||||
{
|
||||
distX = dx - (width - radius - 1);
|
||||
distY = dy - (height - radius - 1);
|
||||
inCorner = true;
|
||||
}
|
||||
|
||||
if (inCorner)
|
||||
{
|
||||
// Check if point is inside the rounded corner
|
||||
double distance = Math.Sqrt(distX * distX + distY * distY);
|
||||
if (distance <= radius)
|
||||
{
|
||||
image[px, py] = color;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inside the main rectangle (not in corners)
|
||||
image[px, py] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCircle(Image<Rgba32> image, int centerX, int centerY, int radius, Rgba32 color)
|
||||
{
|
||||
int x0 = centerX - radius;
|
||||
int y0 = centerY - radius;
|
||||
int diameter = radius * 2;
|
||||
|
||||
for (int dy = 0; dy <= diameter; dy++)
|
||||
{
|
||||
for (int dx = 0; dx <= diameter; dx++)
|
||||
{
|
||||
int px = x0 + dx;
|
||||
int py = y0 + dy;
|
||||
|
||||
if (px < 0 || px >= image.Width || py < 0 || py >= image.Height)
|
||||
continue;
|
||||
|
||||
// Calculate distance from center
|
||||
double distance = Math.Sqrt(Math.Pow(dx - radius, 2) + Math.Pow(dy - radius, 2));
|
||||
|
||||
if (distance <= radius)
|
||||
{
|
||||
image[px, py] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// QRModule class removed - was only used for corner styling which is temporarily simplified
|
||||
}
|
||||
}
|
||||
@ -718,13 +718,11 @@
|
||||
{
|
||||
<option value="rounded">@Localizer["Rounded"] 👑</option>
|
||||
<option value="circle">Círculos 👑</option>
|
||||
<option value="leaf">Folha 👑</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="rounded" disabled>@Localizer["Rounded"] - Premium 👑</option>
|
||||
<option value="circle" disabled>Círculos - Premium 👑</option>
|
||||
<option value="leaf" disabled>Folha - Premium 👑</option>
|
||||
}
|
||||
</select>
|
||||
@if (!isPremium)
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@ -8,7 +8,7 @@
|
||||
"name": "qrrapido-app",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"vite": "^5.4.0"
|
||||
"vite": "^5.4.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
@ -879,9 +879,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.20",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz",
|
||||
"integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==",
|
||||
"version": "5.4.21",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@ -8,6 +8,6 @@
|
||||
"preview": "vite preview --config vite.config.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.4.0"
|
||||
"vite": "^5.4.21"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1182,7 +1182,7 @@ class QRRapidoGenerator {
|
||||
|
||||
handleCornerStyleChange(e) {
|
||||
const selectedStyle = e.target.value;
|
||||
const premiumStyles = ['rounded', 'circle', 'leaf'];
|
||||
const premiumStyles = ['rounded', 'circle'];
|
||||
|
||||
if (premiumStyles.includes(selectedStyle)) {
|
||||
// Check if user is premium (we can detect this by checking if the option is disabled)
|
||||
@ -1194,6 +1194,12 @@ class QRRapidoGenerator {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If a QR code already exists, regenerate with new corner style
|
||||
if (this.currentQR) {
|
||||
console.log('[CORNER STYLE] Corner style changed to:', selectedStyle, '- regenerating QR code');
|
||||
this.generateQRWithTimer(e);
|
||||
}
|
||||
}
|
||||
|
||||
applyQuickStyle(e) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user