mirror of
https://github.com/Leonmmcoset/CMLeonOS.git
synced 2026-04-21 19:24:00 +00:00
bmp图片查看器
This commit is contained in:
@@ -1 +1 @@
|
|||||||
2026-03-23 21:30:51
|
2026-03-23 22:09:30
|
||||||
@@ -1 +1 @@
|
|||||||
9162e2d
|
9197dfb
|
||||||
@@ -137,6 +137,7 @@ namespace CMLeonOS.Gui
|
|||||||
RegisterApp(new AppMetadata("Paint", () => { return new Apps.Paint.Paint(); }, Icons.Icon_Paint, Color.FromArgb(0, 115, 186)));
|
RegisterApp(new AppMetadata("Paint", () => { return new Apps.Paint.Paint(); }, Icons.Icon_Paint, Color.FromArgb(0, 115, 186)));
|
||||||
RegisterApp(new AppMetadata("Memory Statistics", () => { return new Apps.MemoryStatistics(); }, Icons.Icon_MemoryStatistics, Color.FromArgb(25, 25, 25)));
|
RegisterApp(new AppMetadata("Memory Statistics", () => { return new Apps.MemoryStatistics(); }, Icons.Icon_MemoryStatistics, Color.FromArgb(25, 25, 25)));
|
||||||
RegisterApp(new AppMetadata("CodeStudio", () => { return new Apps.CodeStudio.CodeStudio(); }, Icons.Icon_CodeStudio, Color.FromArgb(14, 59, 76)));
|
RegisterApp(new AppMetadata("CodeStudio", () => { return new Apps.CodeStudio.CodeStudio(); }, Icons.Icon_CodeStudio, Color.FromArgb(14, 59, 76)));
|
||||||
|
RegisterApp(new AppMetadata("Image Viewer", () => { return new ImageViewer(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
|
||||||
|
|
||||||
Logger.Logger.Instance.Info("AppManager", $"{AppMetadatas.Count} apps were registered.");
|
Logger.Logger.Instance.Info("AppManager", $"{AppMetadatas.Count} apps were registered.");
|
||||||
|
|
||||||
|
|||||||
274
Gui/Apps/ImageViewer.cs
Normal file
274
Gui/Apps/ImageViewer.cs
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
// The CMLeonOS Project (https://github.com/Leonmmcoset/CMLeonOS)
|
||||||
|
// Copyright (C) 2025-present LeonOS 2 Developer Team
|
||||||
|
//
|
||||||
|
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
using Cosmos.System.Graphics;
|
||||||
|
using CMLeonOS;
|
||||||
|
using CMLeonOS.Gui.UILib;
|
||||||
|
using CMLeonOS.Logger;
|
||||||
|
using CMLeonOS.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace CMLeonOS.Gui.Apps
|
||||||
|
{
|
||||||
|
internal class ImageViewer : Process
|
||||||
|
{
|
||||||
|
internal ImageViewer() : base("Image Viewer", ProcessType.Application) { }
|
||||||
|
|
||||||
|
internal ImageViewer(string path) : base("Image Viewer", ProcessType.Application)
|
||||||
|
{
|
||||||
|
initialPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppWindow window;
|
||||||
|
private Window canvas;
|
||||||
|
private TextBox pathBox;
|
||||||
|
private Button openButton;
|
||||||
|
private Button loadButton;
|
||||||
|
private Button clearButton;
|
||||||
|
|
||||||
|
private WindowManager wm = ProcessManager.GetProcess<WindowManager>();
|
||||||
|
private FileBrowser fileBrowser;
|
||||||
|
|
||||||
|
private Bitmap currentBitmap;
|
||||||
|
private string currentPath = string.Empty;
|
||||||
|
private string initialPath = string.Empty;
|
||||||
|
|
||||||
|
private const int toolbarHeight = 30;
|
||||||
|
private const int padding = 6;
|
||||||
|
private const int buttonWidth = 64;
|
||||||
|
|
||||||
|
private void ShowError(string text)
|
||||||
|
{
|
||||||
|
MessageBox messageBox = new MessageBox(this, "Image Viewer", text);
|
||||||
|
messageBox.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Relayout()
|
||||||
|
{
|
||||||
|
int y = padding;
|
||||||
|
int buttonY = y;
|
||||||
|
|
||||||
|
openButton.MoveAndResize(window.Width - (buttonWidth * 3 + padding * 3), buttonY, buttonWidth, toolbarHeight);
|
||||||
|
loadButton.MoveAndResize(window.Width - (buttonWidth * 2 + padding * 2), buttonY, buttonWidth, toolbarHeight);
|
||||||
|
clearButton.MoveAndResize(window.Width - (buttonWidth + padding), buttonY, buttonWidth, toolbarHeight);
|
||||||
|
|
||||||
|
int pathWidth = window.Width - (buttonWidth * 3 + padding * 5);
|
||||||
|
pathBox.MoveAndResize(padding, y + 3, pathWidth, toolbarHeight - 6);
|
||||||
|
|
||||||
|
int canvasY = toolbarHeight + (padding * 2);
|
||||||
|
int canvasHeight = window.Height - canvasY - padding;
|
||||||
|
canvas.MoveAndResize(padding, canvasY, window.Width - (padding * 2), canvasHeight);
|
||||||
|
RenderCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawBitmapFit(Bitmap bitmap, Window target)
|
||||||
|
{
|
||||||
|
int srcWidth = (int)bitmap.Width;
|
||||||
|
int srcHeight = (int)bitmap.Height;
|
||||||
|
if (srcWidth <= 0 || srcHeight <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dstWidth = target.Width - 2;
|
||||||
|
int dstHeight = target.Height - 2;
|
||||||
|
if (dstWidth <= 0 || dstHeight <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scaledWidth = dstWidth;
|
||||||
|
int scaledHeight = (srcHeight * dstWidth) / srcWidth;
|
||||||
|
if (scaledHeight > dstHeight)
|
||||||
|
{
|
||||||
|
scaledHeight = dstHeight;
|
||||||
|
scaledWidth = (srcWidth * dstHeight) / srcHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaledWidth = Math.Max(1, scaledWidth);
|
||||||
|
scaledHeight = Math.Max(1, scaledHeight);
|
||||||
|
|
||||||
|
int startX = (target.Width - scaledWidth) / 2;
|
||||||
|
int startY = (target.Height - scaledHeight) / 2;
|
||||||
|
|
||||||
|
for (int y = 0; y < scaledHeight; y++)
|
||||||
|
{
|
||||||
|
int srcY = (y * srcHeight) / scaledHeight;
|
||||||
|
int sourceRowOffset = srcY * srcWidth;
|
||||||
|
int targetY = startY + y;
|
||||||
|
for (int x = 0; x < scaledWidth; x++)
|
||||||
|
{
|
||||||
|
int srcX = (x * srcWidth) / scaledWidth;
|
||||||
|
int sourceIndex = sourceRowOffset + srcX;
|
||||||
|
Color color = Color.FromArgb(bitmap.RawData[sourceIndex]);
|
||||||
|
target.DrawPoint(startX + x, targetY, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderCanvas()
|
||||||
|
{
|
||||||
|
canvas.Clear(Color.FromArgb(20, 20, 20));
|
||||||
|
canvas.DrawRectangle(0, 0, canvas.Width, canvas.Height, Color.FromArgb(70, 70, 70));
|
||||||
|
|
||||||
|
if (currentBitmap == null)
|
||||||
|
{
|
||||||
|
const string hint = "Open a BMP file to preview.";
|
||||||
|
canvas.DrawString(hint, Color.LightGray, Math.Max(2, (canvas.Width / 2) - ((hint.Length * 8) / 2)), canvas.Height / 2 - 8);
|
||||||
|
wm.Update(canvas);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawBitmapFit(currentBitmap, canvas);
|
||||||
|
|
||||||
|
string fileName = Path.GetFileName(currentPath);
|
||||||
|
int srcWidth = (int)currentBitmap.Width;
|
||||||
|
int srcHeight = (int)currentBitmap.Height;
|
||||||
|
string info = $"{fileName} {srcWidth}x{srcHeight}";
|
||||||
|
canvas.DrawFilledRectangle(6, canvas.Height - 22, Math.Min(canvas.Width - 12, (info.Length * 8) + 8), 16, Color.FromArgb(0, 0, 0));
|
||||||
|
canvas.DrawString(info, Color.White, 10, canvas.Height - 20);
|
||||||
|
|
||||||
|
wm.Update(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool LoadImage(string path, bool showPopup = true)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
if (showPopup)
|
||||||
|
{
|
||||||
|
ShowError("Path is empty.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string sanitizedPath = PathUtil.Sanitize(path.Trim());
|
||||||
|
if (!File.Exists(sanitizedPath))
|
||||||
|
{
|
||||||
|
if (showPopup)
|
||||||
|
{
|
||||||
|
ShowError("File not found.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Path.GetExtension(sanitizedPath).Equals(".bmp", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (showPopup)
|
||||||
|
{
|
||||||
|
ShowError("Only BMP images are supported.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] bmpBytes = File.ReadAllBytes(sanitizedPath);
|
||||||
|
currentBitmap = new Bitmap(bmpBytes);
|
||||||
|
currentPath = sanitizedPath;
|
||||||
|
pathBox.Text = sanitizedPath;
|
||||||
|
window.Title = $"Image Viewer - {Path.GetFileName(sanitizedPath)}";
|
||||||
|
RenderCanvas();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Logger.Instance.Error("ImageViewer", $"Failed to load BMP: {ex.Message}");
|
||||||
|
if (showPopup)
|
||||||
|
{
|
||||||
|
ShowError("Cannot load this BMP file.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenDialog()
|
||||||
|
{
|
||||||
|
fileBrowser = new FileBrowser(this, wm, (string selectedPath) =>
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(selectedPath))
|
||||||
|
{
|
||||||
|
LoadImage(selectedPath);
|
||||||
|
}
|
||||||
|
}, selectDirectoryOnly: false);
|
||||||
|
fileBrowser.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadFromPath()
|
||||||
|
{
|
||||||
|
LoadImage(pathBox.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearImage()
|
||||||
|
{
|
||||||
|
currentBitmap = null;
|
||||||
|
currentPath = string.Empty;
|
||||||
|
window.Title = "Image Viewer";
|
||||||
|
RenderCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Start()
|
||||||
|
{
|
||||||
|
base.Start();
|
||||||
|
|
||||||
|
window = new AppWindow(this, 170, 90, 760, 520);
|
||||||
|
window.Title = "Image Viewer";
|
||||||
|
window.Icon = AppManager.DefaultAppIcon;
|
||||||
|
window.CanResize = true;
|
||||||
|
window.UserResized = Relayout;
|
||||||
|
window.Closing = TryStop;
|
||||||
|
wm.AddWindow(window);
|
||||||
|
|
||||||
|
pathBox = new TextBox(window, 0, 0, 1, 1);
|
||||||
|
pathBox.PlaceholderText = @"0:\path\image.bmp";
|
||||||
|
pathBox.Submitted = LoadFromPath;
|
||||||
|
wm.AddWindow(pathBox);
|
||||||
|
|
||||||
|
openButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
openButton.Text = "Open";
|
||||||
|
openButton.OnClick = (_, _) => OpenDialog();
|
||||||
|
wm.AddWindow(openButton);
|
||||||
|
|
||||||
|
loadButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
loadButton.Text = "Load";
|
||||||
|
loadButton.OnClick = (_, _) => LoadFromPath();
|
||||||
|
wm.AddWindow(loadButton);
|
||||||
|
|
||||||
|
clearButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
clearButton.Text = "Clear";
|
||||||
|
clearButton.OnClick = (_, _) => ClearImage();
|
||||||
|
wm.AddWindow(clearButton);
|
||||||
|
|
||||||
|
canvas = new Window(this, window, 0, 0, 1, 1);
|
||||||
|
wm.AddWindow(canvas);
|
||||||
|
|
||||||
|
Relayout();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(initialPath))
|
||||||
|
{
|
||||||
|
LoadImage(initialPath, showPopup: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
wm.Update(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Run()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user