Files
AX-Copilot-Codex/tools/IconGenerator/Program.cs

206 lines
7.1 KiB
C#

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
// 다이아몬드 픽셀 아이콘 생성기 v4
// 보석 다이아몬드 컷 실루엣 (flat top → wide girdle → bottom point)
// 내부 facet 선 + RGBG 4색 채움
// 참고: Samsung Diamond Pixel 구조 (흰색선 다이아몬드 도형)
var outputPath = args.Length > 0 ? args[0]
: Path.Combine(AppContext.BaseDirectory, "icon.ico");
int[] sizes = [16, 24, 32, 48, 64, 128, 256];
var pngList = new List<byte[]>();
foreach (var sz in sizes)
{
using var bmp = DrawGemDiamond(sz);
using var ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Png);
pngList.Add(ms.ToArray());
Console.WriteLine($" {sz}x{sz} OK");
}
CreateIco(pngList, sizes, outputPath);
Console.WriteLine($"Icon saved: {outputPath}");
static Bitmap DrawGemDiamond(int size)
{
var bmp = new Bitmap(size, size, PixelFormat.Format32bppArgb);
using var g = Graphics.FromImage(bmp);
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.Clear(Color.Transparent);
float s = size;
float cx = s / 2f;
// ── 보석 다이아몬드 외곽 좌표 ──
// Table (평평한 윗면)
float tableL = s * 0.22f;
float tableR = s * 0.78f;
float tableY = s * 0.18f;
// Girdle (가장 넓은 부분)
float girdleL = s * 0.06f;
float girdleR = s * 0.94f;
float girdleY = s * 0.40f;
// Culet (바닥 뾰족한 점)
float culetX = cx;
float culetY = s * 0.92f;
// 주요 꼭짓점
PointF TL = new(tableL, tableY); // 테이블 좌
PointF TR = new(tableR, tableY); // 테이블 우
PointF GL = new(girdleL, girdleY); // 거들 좌
PointF GR = new(girdleR, girdleY); // 거들 우
PointF BT = new(culetX, culetY); // 바닥 점
// Crown 내부 포인트 (table → girdle 사이 facet)
PointF CM = new(cx, girdleY); // 거들 중앙
PointF CT = new(cx, tableY); // 테이블 중앙
// Crown facet 분할점
PointF CL = new(s * 0.14f, girdleY); // 거들 좌측 근처
PointF CR = new(s * 0.86f, girdleY); // 거들 우측 근처
// Pavilion facet 내부 교차점들
float pavMidY = s * 0.62f;
PointF PL = new(s * 0.28f, pavMidY); // 파빌리온 좌 중간
PointF PR = new(s * 0.72f, pavMidY); // 파빌리온 우 중간
PointF PM = new(cx, pavMidY); // 파빌리온 중앙
// ── 색상 ──
var blue = Color.FromArgb(50, 110, 230);
var blueBright = Color.FromArgb(80, 150, 255);
var green = Color.FromArgb(60, 200, 80);
var greenBright = Color.FromArgb(100, 235, 110);
var red = Color.FromArgb(230, 50, 65);
var redBright = Color.FromArgb(255, 90, 100);
var greenDk = Color.FromArgb(45, 170, 75);
var greenDkBr = Color.FromArgb(80, 210, 100);
// ── Crown (상단부) 채우기 ──
// Crown 좌: Blue
FillPoly(g, new[]{TL, GL, CM, CT}, blueBright, blue);
// Crown 우: Green
FillPoly(g, new[]{TR, CT, CM, GR}, greenBright, green);
// ── Pavilion (하단부) 채우기 ──
// Pavilion 좌: Red
FillPoly(g, new[]{GL, CM, BT}, redBright, red);
// Pavilion 우: Green (darker)
FillPoly(g, new[]{CM, GR, BT}, greenDkBr, greenDk);
// ── Facet 내부 색상 변화 (깊이감) ──
// Crown 좌측 어두운 삼각형
using var crownShadow = new SolidBrush(Color.FromArgb(25, 0, 0, 0));
g.FillPolygon(crownShadow, new[]{TL, GL, new PointF(cx * 0.7f, girdleY * 0.85f)});
// Pavilion 중앙 밝은 삼각형 (반사)
using var pavHighlight = new SolidBrush(Color.FromArgb(20, 255, 255, 255));
g.FillPolygon(pavHighlight, new[]{CM, BT, new PointF(cx - s*0.08f, pavMidY)});
// ── Facet 선 (흰색) ──
float lw = Math.Max(0.8f, s / 140f);
using var facetPen = new Pen(Color.FromArgb(180, 255, 255, 255), lw)
{
LineJoin = LineJoin.Round,
StartCap = LineCap.Round,
EndCap = LineCap.Round
};
// Crown facet lines
// 테이블 윗변
g.DrawLine(facetPen, TL, TR);
// 테이블 → 거들 대각선
g.DrawLine(facetPen, TL, GL);
g.DrawLine(facetPen, TR, GR);
// 세로 중심선 (table → girdle)
g.DrawLine(facetPen, CT, CM);
// Crown 크로스 facet
g.DrawLine(facetPen, TL, CM);
g.DrawLine(facetPen, TR, CM);
// 추가 Crown facet (table 모서리 → 거들 중간)
g.DrawLine(facetPen, TL, new PointF(girdleL + (cx - girdleL) * 0.5f, girdleY));
g.DrawLine(facetPen, TR, new PointF(cx + (girdleR - cx) * 0.5f, girdleY));
// Girdle 수평선
g.DrawLine(facetPen, GL, GR);
// Pavilion facet lines
// 거들 → 바닥 점
g.DrawLine(facetPen, GL, BT);
g.DrawLine(facetPen, GR, BT);
// 중심 → 바닥
g.DrawLine(facetPen, CM, BT);
// Pavilion 크로스 facets
float crossY = girdleY + (culetY - girdleY) * 0.45f;
PointF crossL = new(girdleL + (culetX - girdleL) * 0.45f, crossY);
PointF crossR = new(girdleR - (girdleR - culetX) * 0.45f, crossY);
g.DrawLine(facetPen, GL, crossR);
g.DrawLine(facetPen, GR, crossL);
// 거들 중간점에서 바닥으로
g.DrawLine(facetPen, new PointF(girdleL + (cx - girdleL) * 0.5f, girdleY), BT);
g.DrawLine(facetPen, new PointF(cx + (girdleR - cx) * 0.5f, girdleY), BT);
// ── 외곽선 (두꺼운 흰색) ──
float olw = Math.Max(1.2f, s / 100f);
using var outlinePen = new Pen(Color.FromArgb(220, 255, 255, 255), olw)
{
LineJoin = LineJoin.Round
};
g.DrawPolygon(outlinePen, new[]{TL, TR, GR, BT, GL});
// ── 상단 하이라이트 (테이블 면 빛 반사) ──
using var tableHL = new LinearGradientBrush(
TL, new PointF(cx, girdleY),
Color.FromArgb(45, 255, 255, 255),
Color.Transparent);
g.FillPolygon(tableHL, new[]{TL, TR, CT});
return bmp;
}
static void FillPoly(Graphics g, PointF[] pts, Color c1, Color c2)
{
float minX = pts.Min(p => p.X), maxX = pts.Max(p => p.X);
float minY = pts.Min(p => p.Y), maxY = pts.Max(p => p.Y);
var rect = new RectangleF(minX, minY, Math.Max(1, maxX - minX), Math.Max(1, maxY - minY));
try
{
using var brush = new LinearGradientBrush(rect, c1, c2, LinearGradientMode.Vertical);
g.FillPolygon(brush, pts);
}
catch
{
using var brush = new SolidBrush(c1);
g.FillPolygon(brush, pts);
}
}
static void CreateIco(List<byte[]> pngs, int[] sizes, string path)
{
using var ms = new MemoryStream();
using var bw = new BinaryWriter(ms);
bw.Write((short)0);
bw.Write((short)1);
bw.Write((short)pngs.Count);
int offset = 6 + 16 * pngs.Count;
for (int i = 0; i < pngs.Count; i++)
{
byte dim = (byte)(sizes[i] >= 256 ? 0 : sizes[i]);
bw.Write(dim); bw.Write(dim);
bw.Write((byte)0); bw.Write((byte)0);
bw.Write((short)1); bw.Write((short)32);
bw.Write(pngs[i].Length); bw.Write(offset);
offset += pngs[i].Length;
}
foreach (var png in pngs) bw.Write(png);
File.WriteAllBytes(path, ms.ToArray());
}