mirror of
https://github.com/wismna/ModernKeePassLib.git
synced 2025-10-03 15:40:20 -04:00
Setup solution
This commit is contained in:
441
ModernKeePassLib/Utility/GfxUtil.cs
Normal file
441
ModernKeePassLib/Utility/GfxUtil.cs
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Utility
|
||||
{
|
||||
public static class GfxUtil
|
||||
{
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
private sealed class GfxImage
|
||||
{
|
||||
public byte[] Data;
|
||||
|
||||
public int Width;
|
||||
public int Height;
|
||||
|
||||
public GfxImage(byte[] pbData, int w, int h)
|
||||
{
|
||||
this.Data = pbData;
|
||||
this.Width = w;
|
||||
this.Height = h;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// For debugger display
|
||||
public override string ToString()
|
||||
{
|
||||
return (this.Width.ToString() + "x" + this.Height.ToString());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if KeePassUAP
|
||||
public static Image LoadImage(byte[] pb)
|
||||
{
|
||||
if(pb == null) throw new ArgumentNullException("pb");
|
||||
|
||||
MemoryStream ms = new MemoryStream(pb, false);
|
||||
try { return Image.FromStream(ms); }
|
||||
finally { ms.Close(); }
|
||||
}
|
||||
#else
|
||||
public static Image LoadImage(byte[] pb)
|
||||
{
|
||||
if(pb == null) throw new ArgumentNullException("pb");
|
||||
|
||||
#if !KeePassLibSD
|
||||
// First try to load the data as ICO and afterwards as
|
||||
// normal image, because trying to load an ICO using
|
||||
// the normal image loading methods can result in a
|
||||
// low resolution image
|
||||
try
|
||||
{
|
||||
Image imgIco = ExtractBestImageFromIco(pb);
|
||||
if(imgIco != null) return imgIco;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#endif
|
||||
|
||||
MemoryStream ms = new MemoryStream(pb, false);
|
||||
try { return LoadImagePriv(ms); }
|
||||
finally { ms.Close(); }
|
||||
}
|
||||
|
||||
private static Image LoadImagePriv(Stream s)
|
||||
{
|
||||
// Image.FromStream wants the stream to be open during
|
||||
// the whole lifetime of the image; as we can't guarantee
|
||||
// this, we make a copy of the image
|
||||
Image imgSrc = null;
|
||||
try
|
||||
{
|
||||
#if !KeePassLibSD
|
||||
imgSrc = Image.FromStream(s);
|
||||
Bitmap bmp = new Bitmap(imgSrc.Width, imgSrc.Height,
|
||||
PixelFormat.Format32bppArgb);
|
||||
|
||||
try
|
||||
{
|
||||
bmp.SetResolution(imgSrc.HorizontalResolution,
|
||||
imgSrc.VerticalResolution);
|
||||
Debug.Assert(bmp.Size == imgSrc.Size);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#else
|
||||
imgSrc = new Bitmap(s);
|
||||
Bitmap bmp = new Bitmap(imgSrc.Width, imgSrc.Height);
|
||||
#endif
|
||||
|
||||
using(Graphics g = Graphics.FromImage(bmp))
|
||||
{
|
||||
g.Clear(Color.Transparent);
|
||||
|
||||
#if !KeePassLibSD
|
||||
g.DrawImageUnscaled(imgSrc, 0, 0);
|
||||
#else
|
||||
g.DrawImage(imgSrc, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
return bmp;
|
||||
}
|
||||
finally { if(imgSrc != null) imgSrc.Dispose(); }
|
||||
}
|
||||
|
||||
#if !KeePassLibSD
|
||||
private static Image ExtractBestImageFromIco(byte[] pb)
|
||||
{
|
||||
List<GfxImage> l = UnpackIco(pb);
|
||||
if((l == null) || (l.Count == 0)) return null;
|
||||
|
||||
long qMax = 0;
|
||||
foreach(GfxImage gi in l)
|
||||
{
|
||||
if(gi.Width == 0) gi.Width = 256;
|
||||
if(gi.Height == 0) gi.Height = 256;
|
||||
|
||||
qMax = Math.Max(qMax, (long)gi.Width * (long)gi.Height);
|
||||
}
|
||||
|
||||
byte[] pbHdrPng = new byte[] {
|
||||
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A
|
||||
};
|
||||
byte[] pbHdrJpeg = new byte[] { 0xFF, 0xD8, 0xFF };
|
||||
|
||||
Image imgBest = null;
|
||||
int bppBest = -1;
|
||||
|
||||
foreach(GfxImage gi in l)
|
||||
{
|
||||
if(((long)gi.Width * (long)gi.Height) < qMax) continue;
|
||||
|
||||
byte[] pbImg = gi.Data;
|
||||
Image img = null;
|
||||
try
|
||||
{
|
||||
if((pbImg.Length > pbHdrPng.Length) &&
|
||||
MemUtil.ArraysEqual(pbHdrPng,
|
||||
MemUtil.Mid<byte>(pbImg, 0, pbHdrPng.Length)))
|
||||
img = GfxUtil.LoadImage(pbImg);
|
||||
else if((pbImg.Length > pbHdrJpeg.Length) &&
|
||||
MemUtil.ArraysEqual(pbHdrJpeg,
|
||||
MemUtil.Mid<byte>(pbImg, 0, pbHdrJpeg.Length)))
|
||||
img = GfxUtil.LoadImage(pbImg);
|
||||
else
|
||||
{
|
||||
using(MemoryStream ms = new MemoryStream(pb, false))
|
||||
{
|
||||
using(Icon ico = new Icon(ms, gi.Width, gi.Height))
|
||||
{
|
||||
img = ico.ToBitmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
if(img == null) continue;
|
||||
|
||||
if((img.Width < gi.Width) || (img.Height < gi.Height))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
img.Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
int bpp = GetBitsPerPixel(img.PixelFormat);
|
||||
if(bpp > bppBest)
|
||||
{
|
||||
if(imgBest != null) imgBest.Dispose();
|
||||
|
||||
imgBest = img;
|
||||
bppBest = bpp;
|
||||
}
|
||||
else img.Dispose();
|
||||
}
|
||||
|
||||
return imgBest;
|
||||
}
|
||||
|
||||
private static List<GfxImage> UnpackIco(byte[] pb)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return null; }
|
||||
|
||||
const int SizeICONDIR = 6;
|
||||
const int SizeICONDIRENTRY = 16;
|
||||
|
||||
if(pb.Length < SizeICONDIR) return null;
|
||||
if(MemUtil.BytesToUInt16(pb, 0) != 0) return null; // Reserved, 0
|
||||
if(MemUtil.BytesToUInt16(pb, 2) != 1) return null; // ICO type, 1
|
||||
|
||||
int n = MemUtil.BytesToUInt16(pb, 4);
|
||||
if(n < 0) { Debug.Assert(false); return null; }
|
||||
|
||||
int cbDir = SizeICONDIR + (n * SizeICONDIRENTRY);
|
||||
if(pb.Length < cbDir) return null;
|
||||
|
||||
List<GfxImage> l = new List<GfxImage>();
|
||||
int iOffset = SizeICONDIR;
|
||||
for(int i = 0; i < n; ++i)
|
||||
{
|
||||
int w = pb[iOffset];
|
||||
int h = pb[iOffset + 1];
|
||||
if((w < 0) || (h < 0)) { Debug.Assert(false); return null; }
|
||||
|
||||
int cb = MemUtil.BytesToInt32(pb, iOffset + 8);
|
||||
if(cb <= 0) return null; // Data must have header (even BMP)
|
||||
|
||||
int p = MemUtil.BytesToInt32(pb, iOffset + 12);
|
||||
if(p < cbDir) return null;
|
||||
if((p + cb) > pb.Length) return null;
|
||||
|
||||
try
|
||||
{
|
||||
byte[] pbImage = MemUtil.Mid<byte>(pb, p, cb);
|
||||
GfxImage img = new GfxImage(pbImage, w, h);
|
||||
l.Add(img);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); return null; }
|
||||
|
||||
iOffset += SizeICONDIRENTRY;
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
private static int GetBitsPerPixel(PixelFormat f)
|
||||
{
|
||||
int bpp = 0;
|
||||
switch(f)
|
||||
{
|
||||
case PixelFormat.Format1bppIndexed:
|
||||
bpp = 1;
|
||||
break;
|
||||
|
||||
case PixelFormat.Format4bppIndexed:
|
||||
bpp = 4;
|
||||
break;
|
||||
|
||||
case PixelFormat.Format8bppIndexed:
|
||||
bpp = 8;
|
||||
break;
|
||||
|
||||
case PixelFormat.Format16bppArgb1555:
|
||||
case PixelFormat.Format16bppGrayScale:
|
||||
case PixelFormat.Format16bppRgb555:
|
||||
case PixelFormat.Format16bppRgb565:
|
||||
bpp = 16;
|
||||
break;
|
||||
|
||||
case PixelFormat.Format24bppRgb:
|
||||
bpp = 24;
|
||||
break;
|
||||
|
||||
case PixelFormat.Format32bppArgb:
|
||||
case PixelFormat.Format32bppPArgb:
|
||||
case PixelFormat.Format32bppRgb:
|
||||
bpp = 32;
|
||||
break;
|
||||
|
||||
case PixelFormat.Format48bppRgb:
|
||||
bpp = 48;
|
||||
break;
|
||||
|
||||
case PixelFormat.Format64bppArgb:
|
||||
case PixelFormat.Format64bppPArgb:
|
||||
bpp = 64;
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return bpp;
|
||||
}
|
||||
|
||||
public static Image ScaleImage(Image img, int w, int h)
|
||||
{
|
||||
return ScaleImage(img, w, h, ScaleTransformFlags.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resize an image.
|
||||
/// </summary>
|
||||
/// <param name="img">Image to resize.</param>
|
||||
/// <param name="w">Width of the returned image.</param>
|
||||
/// <param name="h">Height of the returned image.</param>
|
||||
/// <param name="f">Flags to customize scaling behavior.</param>
|
||||
/// <returns>Resized image. This object is always different
|
||||
/// from <paramref name="img" /> (i.e. they can be
|
||||
/// disposed separately).</returns>
|
||||
public static Image ScaleImage(Image img, int w, int h,
|
||||
ScaleTransformFlags f)
|
||||
{
|
||||
if(img == null) throw new ArgumentNullException("img");
|
||||
if(w < 0) throw new ArgumentOutOfRangeException("w");
|
||||
if(h < 0) throw new ArgumentOutOfRangeException("h");
|
||||
|
||||
bool bUIIcon = ((f & ScaleTransformFlags.UIIcon) !=
|
||||
ScaleTransformFlags.None);
|
||||
|
||||
// We must return a Bitmap object for UIUtil.CreateScaledImage
|
||||
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
|
||||
using(Graphics g = Graphics.FromImage(bmp))
|
||||
{
|
||||
g.Clear(Color.Transparent);
|
||||
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
g.CompositingQuality = CompositingQuality.HighQuality;
|
||||
|
||||
int wSrc = img.Width;
|
||||
int hSrc = img.Height;
|
||||
|
||||
InterpolationMode im = InterpolationMode.HighQualityBicubic;
|
||||
if((wSrc > 0) && (hSrc > 0))
|
||||
{
|
||||
if(bUIIcon && ((w % wSrc) == 0) && ((h % hSrc) == 0))
|
||||
im = InterpolationMode.NearestNeighbor;
|
||||
// else if((w < wSrc) && (h < hSrc))
|
||||
// im = InterpolationMode.HighQualityBilinear;
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
g.InterpolationMode = im;
|
||||
|
||||
RectangleF rSource = new RectangleF(0.0f, 0.0f, wSrc, hSrc);
|
||||
RectangleF rDest = new RectangleF(0.0f, 0.0f, w, h);
|
||||
AdjustScaleRects(ref rSource, ref rDest);
|
||||
|
||||
g.DrawImage(img, rDest, rSource, GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
internal static void AdjustScaleRects(ref RectangleF rSource,
|
||||
ref RectangleF rDest)
|
||||
{
|
||||
// When enlarging images, apply a -0.5 offset to avoid
|
||||
// the scaled image being cropped on the top/left side;
|
||||
// when shrinking images, do not apply a -0.5 offset,
|
||||
// otherwise the image is cropped on the bottom/right
|
||||
// side; this applies to all interpolation modes
|
||||
if(rDest.Width > rSource.Width)
|
||||
rSource.X = rSource.X - 0.5f;
|
||||
if(rDest.Height > rSource.Height)
|
||||
rSource.Y = rSource.Y - 0.5f;
|
||||
|
||||
// When shrinking, apply a +0.5 offset, such that the
|
||||
// scaled image is less cropped on the bottom/right side
|
||||
if(rDest.Width < rSource.Width)
|
||||
rSource.X = rSource.X + 0.5f;
|
||||
if(rDest.Height < rSource.Height)
|
||||
rSource.Y = rSource.Y + 0.5f;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public static Image ScaleTest(Image[] vIcons)
|
||||
{
|
||||
Bitmap bmp = new Bitmap(1024, vIcons.Length * (256 + 12),
|
||||
PixelFormat.Format32bppArgb);
|
||||
|
||||
using(Graphics g = Graphics.FromImage(bmp))
|
||||
{
|
||||
g.Clear(Color.White);
|
||||
|
||||
int[] v = new int[] { 16, 24, 32, 48, 64, 128, 256 };
|
||||
|
||||
int x;
|
||||
int y = 8;
|
||||
|
||||
foreach(Image imgIcon in vIcons)
|
||||
{
|
||||
if(imgIcon == null) { Debug.Assert(false); continue; }
|
||||
|
||||
x = 128;
|
||||
|
||||
foreach(int q in v)
|
||||
{
|
||||
using(Image img = ScaleImage(imgIcon, q, q,
|
||||
ScaleTransformFlags.UIIcon))
|
||||
{
|
||||
g.DrawImageUnscaled(img, x, y);
|
||||
}
|
||||
|
||||
x += q + 8;
|
||||
}
|
||||
|
||||
y += v[v.Length - 1] + 8;
|
||||
}
|
||||
}
|
||||
|
||||
return bmp;
|
||||
}
|
||||
#endif // DEBUG
|
||||
#endif // !KeePassLibSD
|
||||
#endif // KeePassUAP
|
||||
|
||||
internal static string ImageToDataUri(Image img)
|
||||
{
|
||||
if(img == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
byte[] pb = null;
|
||||
using(MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
img.Save(ms, ImageFormat.Png);
|
||||
pb = ms.ToArray();
|
||||
}
|
||||
|
||||
return StrUtil.DataToDataUri(pb, "image/png");
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user