Split off image generation to separate project (#2395)
With the approaching games, PKM sprites are a different size from the 3DS era (as already hinted by LGPE, which has 56x68). It'll be a little easier to manage with this portion of the library walled off from the rest of the codebase. Eventually the net46 target will use fody or something to merge in these extra dependency dll's automatically to not disturb the usual exe/dll experience.
|
@ -38,7 +38,7 @@ namespace PKHeX.Core
|
|||
if (pkm is PK7 pk7)
|
||||
{
|
||||
byte[] payload = QR7.GenerateQRData(pk7);
|
||||
return string.Concat(payload.Select(z => (char)z));
|
||||
return GetMessage(payload);
|
||||
}
|
||||
|
||||
var server = GetExploitURLPrefixPKM(pkm.Format);
|
||||
|
@ -46,6 +46,13 @@ namespace PKHeX.Core
|
|||
return GetMessageBase64(data, server);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a QR Message from the input <see cref="byte"/> data.
|
||||
/// </summary>
|
||||
/// <param name="payload">Data to encode</param>
|
||||
/// <returns>QR Message</returns>
|
||||
public static string GetMessage(byte[] payload) => string.Concat(payload.Select(z => (char) z));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a QR Message from the input <see cref="MysteryGift"/> data.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PKHeX.WinForms
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public static class NetUtil
|
||||
{
|
||||
private static readonly Regex LatestGitTagRegex = new Regex("\\\"tag_name\"\\s*\\:\\s*\\\"([0-9]+\\.[0-9]+\\.[0-9]+)\\\""); // Match `"tag_name": "18.12.02"`. Group 1 is `18.12.02`
|
||||
|
||||
public static string GetStringFromURL(string webURL)
|
||||
{
|
||||
try
|
||||
|
@ -37,19 +34,7 @@ namespace PKHeX.WinForms
|
|||
return httpWebResponse.GetResponseStream();
|
||||
}
|
||||
|
||||
public static Image GetImageFromURL(string webURL)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stream = GetStreamFromURL(webURL);
|
||||
return stream != null ? Image.FromStream(stream) : null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private static readonly Regex LatestGitTagRegex = new Regex("\\\"tag_name\"\\s*\\:\\s*\\\"([0-9]+\\.[0-9]+\\.[0-9]+)\\\""); // Match `"tag_name": "18.12.02"`. Group 1 is `18.12.02`
|
||||
|
||||
/// <summary>
|
||||
/// Gets the latest version of PKHeX according to the Github API
|
|
@ -3,7 +3,7 @@ using System.Drawing;
|
|||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PKHeX.WinForms
|
||||
namespace PKHeX.Drawing
|
||||
{
|
||||
/// <summary>
|
||||
/// Image Layering/Blending Utility
|
40
PKHeX.Drawing/PKHeX.Drawing.csproj
Normal file
|
@ -0,0 +1,40 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp3.0;net46</TargetFrameworks>
|
||||
<LangVersion>8</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="QRCoder" Version="1.3.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PKHeX.Core\PKHeX.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
|
||||
<PackageReference Include="System.Drawing.Common">
|
||||
<Version>4.6.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Resources.Extensions">
|
||||
<Version>4.6.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
29713
PKHeX.Drawing/Properties/Resources.Designer.cs
generated
Normal file
9016
PKHeX.Drawing/Properties/Resources.resx
Normal file
101
PKHeX.Drawing/QR/QRDecode.cs
Normal file
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using PKHeX.Core;
|
||||
|
||||
namespace PKHeX.Drawing
|
||||
{
|
||||
public static class QRDecode
|
||||
{
|
||||
// QR Utility
|
||||
private const string DecodeAPI = "http://api.qrserver.com/v1/read-qr-code/?fileurl=";
|
||||
|
||||
public static QRDecodeMsg GetQRData(string address, out byte[] result)
|
||||
{
|
||||
result = Array.Empty<byte>();
|
||||
// Fetch data from QR code...
|
||||
|
||||
if (!address.StartsWith("http"))
|
||||
return QRDecodeMsg.BadPath;
|
||||
|
||||
string webURL = DecodeAPI + WebUtility.UrlEncode(address);
|
||||
string data;
|
||||
try
|
||||
{
|
||||
data = NetUtil.GetStringFromURL(webURL);
|
||||
if (data.Contains("could not find"))
|
||||
return QRDecodeMsg.BadImage;
|
||||
|
||||
if (data.Contains("filetype not supported"))
|
||||
return QRDecodeMsg.BadType;
|
||||
}
|
||||
catch { return QRDecodeMsg.BadConnection; }
|
||||
|
||||
// Quickly convert the json response to a data string
|
||||
try
|
||||
{
|
||||
result = DecodeQRJson(data);
|
||||
return QRDecodeMsg.Success;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e.Message);
|
||||
return QRDecodeMsg.BadConversion;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] DecodeQRJson(string data)
|
||||
{
|
||||
const string cap = "\",\"error\":null}]}]";
|
||||
const string intro = "[{\"type\":\"qrcode\",\"symbol\":[{\"seq\":0,\"data\":\"";
|
||||
const string qrcode = "nQR-Code:";
|
||||
if (!data.StartsWith(intro))
|
||||
throw new FormatException();
|
||||
|
||||
string pkstr = data.Substring(intro.Length);
|
||||
if (pkstr.Contains(qrcode)) // Remove multiple QR codes in same image
|
||||
pkstr = pkstr.Substring(0, pkstr.IndexOf(qrcode, StringComparison.Ordinal));
|
||||
pkstr = pkstr.Substring(0, pkstr.IndexOf(cap, StringComparison.Ordinal)); // Trim outro
|
||||
|
||||
if (!pkstr.StartsWith("http") && !pkstr.StartsWith("null")) // G7
|
||||
{
|
||||
string fstr = Regex.Unescape(pkstr);
|
||||
byte[] raw = Encoding.Unicode.GetBytes(fstr);
|
||||
// Remove 00 interstitials and retrieve from offset 0x30, take PK7 Stored Size (always)
|
||||
return raw.Where((_, i) => i % 2 == 0).Skip(0x30).Take(0xE8).ToArray();
|
||||
}
|
||||
// All except G7
|
||||
pkstr = pkstr.Substring(pkstr.IndexOf('#') + 1); // Trim URL
|
||||
pkstr = pkstr.Replace("\\", string.Empty); // Rectify response
|
||||
|
||||
return Convert.FromBase64String(pkstr);
|
||||
}
|
||||
|
||||
public static string ConvertMsg(this QRDecodeMsg msg)
|
||||
{
|
||||
return msg switch
|
||||
{
|
||||
QRDecodeMsg.Success => string.Empty,
|
||||
QRDecodeMsg.BadPath => MessageStrings.MsgQRUrlFailPath,
|
||||
QRDecodeMsg.BadImage => MessageStrings.MsgQRUrlFailImage,
|
||||
QRDecodeMsg.BadType => MessageStrings.MsgQRUrlFailType,
|
||||
QRDecodeMsg.BadConnection => MessageStrings.MsgQRUrlFailConnection,
|
||||
QRDecodeMsg.BadConversion => MessageStrings.MsgQRUrlFailConvert,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(msg), msg, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum QRDecodeMsg
|
||||
{
|
||||
Success,
|
||||
BadPath,
|
||||
BadImage,
|
||||
BadType,
|
||||
BadConnection,
|
||||
BadConversion,
|
||||
}
|
||||
}
|
27
PKHeX.Drawing/QR/QREncode.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System.Drawing;
|
||||
using PKHeX.Core;
|
||||
using QRCoder;
|
||||
|
||||
namespace PKHeX.Drawing
|
||||
{
|
||||
public static class QREncode
|
||||
{
|
||||
public static Image GenerateQRCode(MysteryGift mg) => GenerateQRCode(QRMessageUtil.GetMessage(mg));
|
||||
public static Image GenerateQRCode(PKM pkm) => GenerateQRCode(QRMessageUtil.GetMessage(pkm));
|
||||
|
||||
public static Image GenerateQRCode7(PK7 pk7, int box = 0, int slot = 0, int num_copies = 1)
|
||||
{
|
||||
byte[] data = QR7.GenerateQRData(pk7, box, slot, num_copies);
|
||||
var msg = QRMessageUtil.GetMessage(data);
|
||||
return GenerateQRCode(msg, ppm: 4);
|
||||
}
|
||||
|
||||
private static Image GenerateQRCode(string msg, int ppm = 4)
|
||||
{
|
||||
using var generator = new QRCodeGenerator();
|
||||
using var qr_data = generator.CreateQrCode(msg, QRCodeGenerator.ECCLevel.Q);
|
||||
using var qr_code = new QRCode(qr_data);
|
||||
return qr_code.GetGraphic(ppm);
|
||||
}
|
||||
}
|
||||
}
|
53
PKHeX.Drawing/QR/QRImageUtil.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace PKHeX.Drawing
|
||||
{
|
||||
public static class QRImageUtil
|
||||
{
|
||||
public static Bitmap GetQRImage(Image qr, Image pkm)
|
||||
{
|
||||
// create a small area with the pkm sprite, with a white background
|
||||
var foreground = new Bitmap(45, 45);
|
||||
using (Graphics gfx = Graphics.FromImage(foreground))
|
||||
{
|
||||
gfx.FillRectangle(new SolidBrush(Color.White), 0, 0, foreground.Width, foreground.Height);
|
||||
int x = (foreground.Width / 2) - (pkm.Width / 2);
|
||||
int y = (foreground.Height / 2) - (pkm.Height / 2);
|
||||
gfx.DrawImage(pkm, x, y);
|
||||
}
|
||||
|
||||
// Layer on Preview Image
|
||||
{
|
||||
int x = (qr.Width / 2) - (foreground.Width / 2);
|
||||
int y = (qr.Height / 2) - (foreground.Height / 2);
|
||||
return ImageUtil.LayerImage(qr, foreground, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap GetQRImageExtended(Font font, Image qr, Image pkm, int width, int height, string[] lines, string extraText)
|
||||
{
|
||||
var pic = GetQRImage(qr, pkm);
|
||||
return ExtendImage(font, qr, width, height, pic, lines, extraText);
|
||||
}
|
||||
|
||||
private static Bitmap ExtendImage(Font font, Image qr, int width, int height, Image pic, string[] lines, string extraText)
|
||||
{
|
||||
var newpic = new Bitmap(width, height);
|
||||
using (Graphics g = Graphics.FromImage(newpic))
|
||||
{
|
||||
g.FillRectangle(new SolidBrush(Color.White), 0, 0, newpic.Width, newpic.Height);
|
||||
g.DrawImage(pic, 0, 0);
|
||||
|
||||
g.DrawString(GetLine(lines, 0), font, Brushes.Black, new PointF(18, qr.Height - 5));
|
||||
g.DrawString(GetLine(lines, 1), font, Brushes.Black, new PointF(18, qr.Height + 8));
|
||||
g.DrawString(GetLine(lines, 2).Replace(Environment.NewLine, "/").Replace("//", " ").Replace(":/", ": "), font,
|
||||
Brushes.Black, new PointF(18, qr.Height + 20));
|
||||
g.DrawString(GetLine(lines, 3) + extraText, font, Brushes.Black, new PointF(18, qr.Height + 32));
|
||||
}
|
||||
return newpic;
|
||||
}
|
||||
|
||||
private static string GetLine(string[] lines, int line) => lines.Length <= line ? string.Empty : lines[line];
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 772 B |
Before Width: | Height: | Size: 848 B After Width: | Height: | Size: 848 B |
Before Width: | Height: | Size: 845 B After Width: | Height: | Size: 845 B |
Before Width: | Height: | Size: 831 B After Width: | Height: | Size: 831 B |
Before Width: | Height: | Size: 874 B After Width: | Height: | Size: 874 B |
Before Width: | Height: | Size: 874 B After Width: | Height: | Size: 874 B |
Before Width: | Height: | Size: 226 B After Width: | Height: | Size: 226 B |
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 304 B |
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 790 B |
Before Width: | Height: | Size: 555 B After Width: | Height: | Size: 555 B |
Before Width: | Height: | Size: 151 B After Width: | Height: | Size: 151 B |
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 407 B |
Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 351 B |
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 327 B |
Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 434 B |
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 503 B |
Before Width: | Height: | Size: 474 B After Width: | Height: | Size: 474 B |
Before Width: | Height: | Size: 402 B After Width: | Height: | Size: 402 B |
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
Before Width: | Height: | Size: 448 B After Width: | Height: | Size: 448 B |
Before Width: | Height: | Size: 424 B After Width: | Height: | Size: 424 B |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 406 B |
Before Width: | Height: | Size: 430 B After Width: | Height: | Size: 430 B |
Before Width: | Height: | Size: 537 B After Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 278 B |
Before Width: | Height: | Size: 606 B After Width: | Height: | Size: 606 B |
Before Width: | Height: | Size: 410 B After Width: | Height: | Size: 410 B |
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 510 B |
Before Width: | Height: | Size: 412 B After Width: | Height: | Size: 412 B |
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 409 B |
Before Width: | Height: | Size: 530 B After Width: | Height: | Size: 530 B |
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 456 B |
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 369 B |
Before Width: | Height: | Size: 444 B After Width: | Height: | Size: 444 B |
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
Before Width: | Height: | Size: 470 B After Width: | Height: | Size: 470 B |
Before Width: | Height: | Size: 447 B After Width: | Height: | Size: 447 B |
Before Width: | Height: | Size: 375 B After Width: | Height: | Size: 375 B |
Before Width: | Height: | Size: 417 B After Width: | Height: | Size: 417 B |
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 425 B After Width: | Height: | Size: 425 B |
Before Width: | Height: | Size: 471 B After Width: | Height: | Size: 471 B |
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 436 B |
Before Width: | Height: | Size: 476 B After Width: | Height: | Size: 476 B |
Before Width: | Height: | Size: 664 B After Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 447 B After Width: | Height: | Size: 447 B |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 500 B |
Before Width: | Height: | Size: 348 B After Width: | Height: | Size: 348 B |
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 673 B |
Before Width: | Height: | Size: 621 B After Width: | Height: | Size: 621 B |
Before Width: | Height: | Size: 471 B After Width: | Height: | Size: 471 B |
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 296 B |
Before Width: | Height: | Size: 483 B After Width: | Height: | Size: 483 B |
Before Width: | Height: | Size: 394 B After Width: | Height: | Size: 394 B |
Before Width: | Height: | Size: 496 B After Width: | Height: | Size: 496 B |
Before Width: | Height: | Size: 441 B After Width: | Height: | Size: 441 B |
Before Width: | Height: | Size: 453 B After Width: | Height: | Size: 453 B |
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 418 B |
Before Width: | Height: | Size: 370 B After Width: | Height: | Size: 370 B |
Before Width: | Height: | Size: 423 B After Width: | Height: | Size: 423 B |
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 326 B |
Before Width: | Height: | Size: 394 B After Width: | Height: | Size: 394 B |
Before Width: | Height: | Size: 563 B After Width: | Height: | Size: 563 B |
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 477 B |
Before Width: | Height: | Size: 499 B After Width: | Height: | Size: 499 B |
Before Width: | Height: | Size: 441 B After Width: | Height: | Size: 441 B |
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 466 B |
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 477 B |
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 500 B |
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
Before Width: | Height: | Size: 470 B After Width: | Height: | Size: 470 B |
Before Width: | Height: | Size: 518 B After Width: | Height: | Size: 518 B |
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 524 B |
Before Width: | Height: | Size: 460 B After Width: | Height: | Size: 460 B |
Before Width: | Height: | Size: 423 B After Width: | Height: | Size: 423 B |
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 385 B |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 533 B After Width: | Height: | Size: 533 B |
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 418 B |
Before Width: | Height: | Size: 468 B After Width: | Height: | Size: 468 B |
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 509 B |
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 480 B After Width: | Height: | Size: 480 B |
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 409 B |