Minor img processing update

Extract magic pixel shifting numbers for layering imgs -- SWSH will
likely use the large boxsprites, as hinted by LGPE's upsizing.
Remove new[] color array creation, just pass byte values
break up glowedges into steps
This commit is contained in:
Kurt 2019-04-22 22:24:29 -07:00
parent 963cb1aa99
commit 9861128b63
4 changed files with 84 additions and 53 deletions

View file

@ -78,8 +78,7 @@ namespace PKHeX.WinForms.Controls
{
HoverWorker.Stop();
var bgr = new[] { Draw.GlowInitial.B, Draw.GlowInitial.G, Draw.GlowInitial.R };
SpriteUtil.GetSpriteGlow(pk, bgr, out var glowdata, out var GlowBase);
SpriteUtil.GetSpriteGlow(pk, Draw.GlowInitial.B, Draw.GlowInitial.G, Draw.GlowInitial.R, out var glowdata, out var GlowBase);
hover = ImageUtil.LayerImage(GlowBase, Resources.slotHover, 0, 0);
HoverWorker.GlowToColor = Draw.GlowFinal;
HoverWorker.GlowFromColor = Draw.GlowInitial;

View file

@ -164,13 +164,19 @@ namespace PKHeX.WinForms
}
}
public static void GlowEdges(byte[] data, byte[] colors, int width, int reach = 3, double amount = 0.0777)
public static void GlowEdges(byte[] data, byte blue, byte green, byte red, int width, int reach = 3, double amount = 0.0777)
{
PollutePixels(data, width, reach, amount);
CleanPollutedPixels(data, blue, green, red);
}
private static void PollutePixels(byte[] data, int width, int reach, double amount)
{
// dual pass (pollute, de-transparent)
int stride = width * 4;
int height = data.Length / stride;
for (int i = 0; i < data.Length; i += 4)
{
// only pollute outwards if the current pixel isn't transparent
if (data[i + 3] == 0)
continue;
@ -189,23 +195,32 @@ namespace PKHeX.WinForms
{
for (int j = top; j <= bottom; j++)
{
// update one of the color bits
// it is expected that a transparent pixel RGBA value is 0.
var c = 4 * (i + (j * width));
data[c + 0] += (byte)(amount * (0xFF - data[c + 0]));
}
}
}
}
private static void CleanPollutedPixels(byte[] data, byte blue, byte green, byte red)
{
for (int i = 0; i < data.Length; i += 4)
{
// only clean if the current pixel isn't transparent
if (data[i + 3] != 0)
continue;
var flair = data[i + 0];
if (flair == 0)
// grab the transparency from the donor byte
var transparency = data[i + 0];
if (transparency == 0)
continue;
data[i + 3] = flair;
data[i + 0] = colors[0];
data[i + 1] = colors[1];
data[i + 2] = colors[2];
data[i + 0] = blue;
data[i + 1] = green;
data[i + 2] = red;
data[i + 3] = transparency;
}
}

View file

@ -8,6 +8,15 @@ namespace PKHeX.WinForms
{
public static bool ShowEggSpriteAsItem { get; set; } = true;
private const int ItemShiftX = 22;
private const int ItemShiftY = 15;
private const int ItemMaxSize = 15;
private const int EggItemShiftX = 9;
private const int EggItemShiftY = 2;
private const double UnknownFormTransparency = 0.5;
private const double ShinyTransparency = 0.7;
private const double EggUnderLayerTransparency = 0.33;
public void Initialize(SaveFile sav)
{
if (sav.Generation != 3)
@ -66,8 +75,7 @@ namespace PKHeX.WinForms
private static Image GetBaseImageTotem(int species, int form, int gender, bool shiny, int generation)
{
var baseform = FormConverter.GetTotemBaseForm(species, form);
var file = PKX.GetResourceStringSprite(species, baseform, gender, generation, shiny);
var baseImage = (Image)Resources.ResourceManager.GetObject(file);
var baseImage = GetBaseImageDefault(species, baseform, gender, shiny, generation);
return ImageUtil.ToGrayscale(baseImage);
}
@ -79,20 +87,18 @@ namespace PKHeX.WinForms
private static Image GetBaseImageFallback(int species, int form, int gender, bool shiny, int generation)
{
Image baseImage;
if (shiny) // try again without shiny
{
var file = PKX.GetResourceStringSprite(species, form, gender, generation);
baseImage = (Image)Resources.ResourceManager.GetObject(file);
if (baseImage != null)
return baseImage;
var img = GetBaseImageDefault(species, form, gender, false, generation);
if (img != null)
return img;
}
// try again without form
baseImage = (Image)Resources.ResourceManager.GetObject($"_{species}");
var baseImage = (Image)Resources.ResourceManager.GetObject($"_{species}");
if (baseImage == null) // failed again
return Resources.unknown;
return ImageUtil.LayerImage(baseImage, Resources.unknown, 0, 0, .5);
return ImageUtil.LayerImage(baseImage, Resources.unknown, 0, 0, UnknownFormTransparency);
}
private static Image LayerOverImageItem(Image baseImage, int item, int generation)
@ -101,11 +107,11 @@ namespace PKHeX.WinForms
if (generation >= 2 && generation <= 4 && 328 <= item && item <= 419) // gen2/3/4 TM
itemimg = Resources.item_tm;
// Redraw
int x = 22 + ((15 - itemimg.Width) / 2);
// Redraw item in bottom right corner; since images are cropped, try to not have them at the edge
int x = ItemShiftX + ((ItemMaxSize - itemimg.Width) / 2);
if (x + itemimg.Width > baseImage.Width)
x = baseImage.Width - itemimg.Width;
int y = 15 + (15 - itemimg.Height);
int y = ItemShiftY + (ItemMaxSize - itemimg.Height);
return ImageUtil.LayerImage(baseImage, itemimg, x, y);
}
@ -113,7 +119,7 @@ namespace PKHeX.WinForms
{
// Add shiny star to top left of image.
var rare = isBoxBGRed ? Resources.rare_icon_alt : Resources.rare_icon;
return ImageUtil.LayerImage(baseImage, rare, 0, 0, 0.7);
return ImageUtil.LayerImage(baseImage, rare, 0, 0, ShinyTransparency);
}
private static Image LayerOverImageEgg(Image baseImage, int species, bool hasItem)
@ -125,10 +131,6 @@ namespace PKHeX.WinForms
private static Image GetEggSprite(int species) => species == 490 ? Resources._490_e : Resources.egg;
private const double EggUnderLayerTransparency = 0.33;
private const int EggOverLayerAsItemShiftX = 9;
private const int EggOverLayerAsItemShiftY = 2;
private static Image LayerOverImageEggTransparentSpecies(Image baseImage, int species)
{
// Partially transparent species.
@ -141,7 +143,7 @@ namespace PKHeX.WinForms
private static Image LayerOverImageEggAsItem(Image baseImage, int species)
{
var egg = GetEggSprite(species);
return ImageUtil.LayerImage(baseImage, egg, EggOverLayerAsItemShiftX, EggOverLayerAsItemShiftY); // similar to held item, since they can't have any
return ImageUtil.LayerImage(baseImage, egg, EggItemShiftX, EggItemShiftY); // similar to held item, since they can't have any
}
}
}

View file

@ -10,8 +10,8 @@ namespace PKHeX.WinForms
public static Image GetBallSprite(int ball)
{
string str = PKX.GetResourceStringBall(ball);
return (Image)Resources.ResourceManager.GetObject(str) ?? Resources._ball4; // Poké Ball (default)
string resource = PKX.GetResourceStringBall(ball);
return (Image)Resources.ResourceManager.GetObject(resource) ?? Resources._ball4; // Poké Ball (default)
}
public static Image GetSprite(int species, int form, int gender, int item, bool isegg, bool shiny, int generation = -1, bool isBoxBGRed = false)
@ -21,8 +21,8 @@ namespace PKHeX.WinForms
public static Image GetRibbonSprite(string name)
{
var sprite = name.Replace("CountG3", "G3").ToLower();
return Resources.ResourceManager.GetObject(sprite) as Image;
var resource = name.Replace("CountG3", "G3").ToLower();
return (Bitmap)Resources.ResourceManager.GetObject(resource);
}
public static Image GetRibbonSprite(string name, int max, int value)
@ -57,7 +57,7 @@ namespace PKHeX.WinForms
{
if (generation <= 2)
type = (int)((MoveType)type).GetMoveTypeGeneration(generation);
return Resources.ResourceManager.GetObject($"type_icon_{type:00}") as Image;
return (Bitmap)Resources.ResourceManager.GetObject($"type_icon_{type:00}");
}
private static Image GetSprite(MysteryGift gift)
@ -82,7 +82,7 @@ namespace PKHeX.WinForms
int item = gift.ItemID;
if (Legal.ZCrystalDictionary.TryGetValue(item, out int value))
item = value;
return (Image)(Resources.ResourceManager.GetObject("item_" + item) ?? Resources.Bag_Key);
return (Image)(Resources.ResourceManager.GetObject($"item_{item}") ?? Resources.Bag_Key);
}
return Resources.unknown;
}
@ -92,9 +92,10 @@ namespace PKHeX.WinForms
var img = GetSprite(pk.Species, pk.AltForm, pk.Gender, pk.SpriteItem, pk.IsEgg, pk.IsShiny, pk.Format, isBoxBGRed);
if (pk is IShadowPKM s && s.Purification > 0)
{
if (pk.Species == 249) // Lugia
img = Spriter.GetSprite(Resources._249x, 249, pk.HeldItem, pk.IsEgg, pk.IsShiny, pk.Format, isBoxBGRed);
GetSpriteGlow(pk, new byte[] { 75, 0, 130 }, out var pixels, out var baseSprite, true);
const int Lugia = 249;
if (pk.Species == Lugia) // show XD shadow sprite
img = Spriter.GetSprite(Resources._249x, Lugia, pk.HeldItem, pk.IsEgg, pk.IsShiny, pk.Format, isBoxBGRed);
GetSpriteGlow(pk, 75, 0, 130, out var pixels, out var baseSprite, true);
var glowImg = ImageUtil.GetBitmap(pixels, baseSprite.Width, baseSprite.Height, baseSprite.PixelFormat);
img = ImageUtil.LayerImage(glowImg, img, 0, 0);
}
@ -120,28 +121,29 @@ namespace PKHeX.WinForms
if (!pk.Valid)
return null;
bool inBox = slot >= 0 && slot < 30;
var sprite = pk.Species == 0 ? null : pk.Sprite(isBoxBGRed: inBox && BoxWallpaper.IsWallpaperRed(sav.Version, sav.GetBoxWallpaper(box)));
bool inBox = (uint)slot < MaxSlotCount;
bool empty = pk.Species == 0;
var sprite = empty ? null : pk.Sprite(isBoxBGRed: inBox && BoxWallpaper.IsWallpaperRed(sav.Version, sav.GetBoxWallpaper(box)));
if (flagIllegal)
if (!empty && flagIllegal)
{
if (box >= 0)
pk.Box = box;
var la = new LegalityAnalysis(pk, sav.Personal);
if (!la.Valid && pk.Species != 0)
sprite = ImageUtil.LayerImage(sprite, Resources.warn, 0, 14);
if (!la.Valid)
sprite = ImageUtil.LayerImage(sprite, Resources.warn, 0, FlagIllegalShiftY);
}
if (inBox) // in box
{
var flags = sav.GetSlotFlags(box, slot);
if (flags.HasFlagFast(StorageSlotFlag.Locked))
sprite = ImageUtil.LayerImage(sprite, Resources.locked, 26, 0);
sprite = ImageUtil.LayerImage(sprite, Resources.locked, SlotLockShiftX, 0);
int team = flags.IsBattleTeam();
if (team >= 0)
sprite = ImageUtil.LayerImage(sprite, Resources.team, 21, 0);
sprite = ImageUtil.LayerImage(sprite, Resources.team, SlotTeamShiftX, 0);
int party = flags.IsParty();
if (party >= 0)
sprite = ImageUtil.LayerImage(sprite, PartyMarks[party], 24, 0);
sprite = ImageUtil.LayerImage(sprite, PartyMarks[party], PartyMarkShiftX, 0);
if (flags.HasFlagFast(StorageSlotFlag.Starter))
sprite = ImageUtil.LayerImage(sprite, Resources.starter, 0, 0);
}
@ -149,6 +151,14 @@ namespace PKHeX.WinForms
return sprite;
}
private const int MaxSlotCount = 30; // slots in a box
private const int SpriteWidth = 40;
private const int SpriteHeight = 30;
private const int PartyMarkShiftX = SpriteWidth - 16;
private const int SlotLockShiftX = SpriteWidth - 14;
private const int SlotTeamShiftX = SpriteWidth - 19;
private const int FlagIllegalShiftY = SpriteHeight - 16;
private static readonly Image[] PartyMarks =
{
Resources.party1, Resources.party2, Resources.party3, Resources.party4, Resources.party5, Resources.party6,
@ -156,17 +166,22 @@ namespace PKHeX.WinForms
public static void GetSpriteGlow(PKM pk, byte[] bgr, out byte[] pixels, out Image baseSprite, bool forceHollow = false)
{
bool egg = pk.IsEgg;
baseSprite = GetSprite(pk.Species, pk.AltForm, pk.Gender, 0, egg, false, pk.Format);
GetSpriteGlow(baseSprite, bgr, out pixels, forceHollow || egg);
GetSpriteGlow(pk, bgr[0], bgr[1], bgr[2], out pixels, out baseSprite, forceHollow);
}
public static void GetSpriteGlow(Image baseSprite, byte[] bgr, out byte[] pixels, bool forceHollow = false)
public static void GetSpriteGlow(PKM pk, byte blue, byte green, byte red, out byte[] pixels, out Image baseSprite, bool forceHollow = false)
{
bool egg = pk.IsEgg;
baseSprite = GetSprite(pk.Species, pk.AltForm, pk.Gender, 0, egg, false, pk.Format);
GetSpriteGlow(baseSprite, blue, green, red, out pixels, forceHollow || egg);
}
public static void GetSpriteGlow(Image baseSprite, byte blue, byte green, byte red, out byte[] pixels, bool forceHollow = false)
{
pixels = ImageUtil.GetPixelData((Bitmap)baseSprite);
if (!forceHollow)
{
ImageUtil.GlowEdges(pixels, bgr, baseSprite.Width);
ImageUtil.GlowEdges(pixels, blue, green, red, baseSprite.Width);
return;
}
@ -175,7 +190,7 @@ namespace PKHeX.WinForms
// Remove opaque pixels from original image, leaving only the glow effect pixels.
var original = (byte[])pixels.Clone();
ImageUtil.SetAllUsedPixelsOpaque(pixels);
ImageUtil.GlowEdges(pixels, bgr, baseSprite.Width);
ImageUtil.GlowEdges(pixels, blue, green, red, baseSprite.Width);
ImageUtil.RemovePixels(pixels, original);
}