mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 04:23:12 +00:00
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:
parent
963cb1aa99
commit
9861128b63
4 changed files with 84 additions and 53 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,26 +166,31 @@ 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)
|
||||
{
|
||||
pixels = ImageUtil.GetPixelData((Bitmap) baseSprite);
|
||||
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;
|
||||
}
|
||||
|
||||
// If the image has any transparency, any derived background will bleed into it.
|
||||
// Need to undo any transparency values if any present.
|
||||
// Remove opaque pixels from original image, leaving only the glow effect pixels.
|
||||
var original = (byte[]) pixels.Clone();
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue