Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Feb 02 00:48
    Miepee closed #2386
  • Feb 02 00:48
    Miepee commented #2386
  • Feb 02 00:43
    cwensley commented #2386
  • Feb 01 11:37
    Miepee edited #2343
  • Feb 01 11:34
    Miepee opened #2386
  • Feb 01 01:10

    cwensley on develop

    Mac: Check for null Handler whe… Merge pull request #2385 from c… (compare)

  • Feb 01 01:10
    cwensley closed #2385
  • Feb 01 01:06
    cwensley opened #2385
  • Feb 01 01:01
    cwensley milestoned #2385
  • Feb 01 01:00
    cwensley milestoned #2385
  • Feb 01 01:00
    cwensley labeled #2385
  • Jan 30 21:55

    cwensley on develop

    Add Control.TriggerStyleChanged… Merge pull request #2384 from c… (compare)

  • Jan 30 21:55
    cwensley closed #2384
  • Jan 30 21:47
    cwensley milestoned #2384
  • Jan 30 21:47
    cwensley milestoned #2384
  • Jan 30 21:47
    cwensley labeled #2384
  • Jan 30 21:47
    cwensley opened #2384
  • Jan 29 19:03
    Miepee updated the wiki
  • Jan 24 00:20

    cwensley on develop

    Gtk: Fix crash when setting Tre… Merge pull request #2383 from c… (compare)

  • Jan 24 00:20
    cwensley closed #2383
Ararem
@Ararem
Ok so how exactly would that work?
Can i just directly copy the Rgb24 bytes into the BitMapData pointer?
or do i need to convert between bytes and floats somehow
Ararem
@Ararem
update: this seems to work extremely well:
using BitmapData data         = imageWidget.Lock();
        Stopwatch        stop         = Stopwatch.StartNew();
        int              xSize        = imageWidget.Width, ySize = imageWidget.Height;
        Image<Rgb24>     renderBuffer = renderJob.ImageBuffer;
        Verbose("Updating image");
        IntPtr offset = data.Data;
        for (int y = 0; y < ySize; y++)
            unsafe
            {
                Span<Rgb24> renderBufRow = renderBuffer.GetPixelRowSpan(y);
                void*       destPtr      = offset.ToPointer();
                Span<Rgb24> destRow      = new(destPtr, xSize);

                renderBufRow.CopyTo(destRow);
                offset += data.ScanWidth;
            }
One thing though, the image I have created seems to be sizing itself to the buffer, not the available space in the windoww
image.png
image.png
How would I make the image 'fit' the window
@cwensley
By theway thanks for your help :)
Curtis Wensley
@cwensley
You may need to set the size of the ImageView to whatever minimum size you want it to be at.
.. which will still then grow to the container you put it in
Ararem
@Ararem
image.png
Whenever I try to set the MinimumSize or Size properties, the whole app breaks
using
imageContainer.MinimumSize = new Size(160, 90);
imageView.Size = new Size(-1, -1);
Variable declarations:
previewImage   = new Bitmap(renderJob.RenderOptions.Width, renderJob.RenderOptions.Height, PixelFormat.Format24bppRgb);
imageView      = new ImageView { Image = previewImage};
imageContainer = new GroupBox { Text   = "Preview", Content = imageView };
Total code:
using Eto.Drawing;
using Eto.Forms;
using RayTracer.Core.Graphics;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using static Serilog.Log;
using Size = Eto.Drawing.Size;

namespace RayTracer.Display.EtoForms;

internal sealed class RenderProgressDisplayPanel : Panel
{
    private readonly GroupBox       imageContainer;
    private readonly ImageView      imageView;
    private readonly Bitmap         previewImage;
    private readonly AsyncRenderJob renderJob;
    private readonly GroupBox       statsContainer;
    private readonly TableLayout    statsTable;

    public RenderProgressDisplayPanel(AsyncRenderJob renderJob)
    {
        //TODO
        this.renderJob = renderJob;
        Verbose("Creating StackPanelLayout for content");
        statsTable     = new TableLayout();
        statsContainer = new GroupBox { Text = "Statistics", Content = statsTable };
        previewImage   = new Bitmap(renderJob.RenderOptions.Width, renderJob.RenderOptions.Height, PixelFormat.Format24bppRgb);
        imageView      = new ImageView { Image = previewImage};
        imageContainer = new GroupBox { Text   = "Preview", Content = imageView };
        Content = new StackLayout
        {
                Items       = { statsContainer, imageContainer },
                Orientation = Orientation.Horizontal,
                Spacing     = 10
        };

        Task.Run(UpdatePreviewWorker);
    }

    private async Task UpdatePreviewWorker()
    {
        while (!renderJob.RenderCompleted)
        {
            UpdateImagePreview();
            UpdateStatsTable();
            await Task.Delay(1000);
        }

        UpdateImagePreview(); //Do final run to ensure image isn't half updated (if render completed partway through update)
        UpdateStatsTable();
    }

    private void UpdateImagePreview()
    {
        imageContainer.MinimumSize = new Size(160, 90);
        imageView.Size = new Size(-1, -1);

        using BitmapData data         = previewImage.Lock();
        Stopwatch        stop         = Stopwatch.StartNew();
        int              xSize        = previewImage.Width, ySize = previewImage.Height;
        Image<Rgb24>     renderBuffer = renderJob.ImageBuffer;
        Verbose("Updating image");
        IntPtr offset = data.Data;
        for (int y = 0; y < ySize; y++)
            unsafe
            {
                Span<Rgb24> renderBufRow = renderBuffer.GetPixelRowSpan(y);
                void*       destPtr      = offset.ToPointer();
                Span<Rgb24> destRow      = new(destPtr, xSize);

                renderBufRow.CopyTo(destRow);
                offset += data.ScanWidth;
            }

        //Mark this object as requiring a redraw
        Invalidate();

        Verbose("Finished updating image in {Elapsed}", stop.Elapsed);
    }

    private void UpdateStatsTable()
    {
        /*Unrelated*/
    }
}
@cwensley Not sure what's going on here.
Ararem
@Ararem
Ok I think I'm going crazy. Now it seems to be happening due to the call to Invalidate()???????
Very reproducible, as soon as I invalidate one of the controls the app gets buggered
Ararem
@Ararem
Ok nvm im fixing that it's cause of async and exceptions no being nice
Ararem
@Ararem
Update: Fixed the weird freezing issue, was caused by UI acess on wrong thread and exceptions not showing up.
Ararem
@Ararem
Still can't get the sizing to work though :(
Ararem
@Ararem

@cwensley Really sorry to keep bothering you, but I've had a look through the repo issues and done some googling, and I can't find a solution.
I followed what you said, and I set the sizes like so:

        this.MinimumSize                = new Size(160, 90);
        imageContainer.MinimumSize = new Size(160, 90);
        imageContainer.Size        = new Size(-1,  -1);
        imageContainer.ClientSize  = new Size(-1,  -1);
        imageView.Size             = new Size(-1,  -1);

But the image still scales itself to 1920*1080 not the available space.

Curtis Wensley
@cwensley
-1, -1 means auto size. Set imageView.Size = new Size(100, 100), or the minimum size you want it to be.
the ImageView, when auto sized, will default to the size of the image, which is your problem here.
you have to override that and tell it "hey control, it's okay if you're smaller dude"
Ararem
@Ararem
But If I set it to new Size(100,100), then it's too small. What I want is for it to go as big as it can, just not outside the available space it has. So if the available space is 19201080, then it will be 19201080. If the screen is half the size, it'll be 960*540, etc. Like i need auto scaling that changes when i resize the app window, not a fixed size
image.png
Like this is too small now, and I can't just hardcode something in
Ararem
@Ararem

Actually, the docs say

By default, the ImageView will automatically size to the size of the specified Image, otherwise the image will be scaled to fit inside the available area for the control.

How do i get it to limit the available are to the onscreen area.
I would have to constrain the GroupBox right?
Curtis Wensley
@cwensley
No, it depends on where you are placing the ImageView. If you are placing it in a non-scaled row/column in a TableLayout or something then yes, it won't grow. You need to put it in a container that will allow it to expand.
Seeing your entire layout code would help.
Ararem
@Ararem
RenderProgressDisplayPanel:
using Eto.Drawing;
using Eto.Forms;
using Gtk;
using LibEternal.ObjectPools;
using RayTracer.Core.Graphics;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using static Serilog.Log;
using Application = Eto.Forms.Application;
using Orientation = Eto.Forms.Orientation;
using Size = Eto.Drawing.Size;

namespace RayTracer.Display.EtoForms;

internal sealed class RenderProgressDisplayPanel : Panel
{
    /*Properties storing sub-controls*/

    public RenderProgressDisplayPanel(AsyncRenderJob renderJob)
    {
        //TODO
        this.renderJob = renderJob;
        Verbose("Creating StackPanelLayout for content");
        statsTable = new TableLayout
                { ID = "Stats Table" };
        statsContainer = new GroupBox
                { ID = "Stats Container", Text = "Statistics", Content = statsTable };
        previewImage = new Bitmap(renderJob.RenderOptions.Width, renderJob.RenderOptions.Height, PixelFormat.Format24bppRgb)
                { ID = "Preview Image" };
        imageView = new ImageView
                { ID = "Image View", Image = previewImage };
        imageContainer = new GroupBox
                { Text = "Preview", Content = imageView, ID = "Image Container" };
        Content = new StackLayout
        {
                Items       = { statsContainer, imageContainer },
                Orientation = Orientation.Horizontal,
                Spacing     = 10,
                ID          = "Main Content StackLayout"
        };

        TaskWatcher.Watch(Task.Run(UpdatePreviewWorker), false);
    }

    private async Task UpdatePreviewWorker()
    {
        //Updates the content of the image and table
    }

    private void UpdateImagePreview()
    {
        //HACK WARN: Auto sizing doesn't work so need to manually set it
        imageView.Size = new Size(1000, 700);
        using BitmapData data         = previewImage.Lock();
        Stopwatch        stop         = Stopwatch.StartNew();
        int              xSize        = previewImage.Width, ySize = previewImage.Height;
        Image<Rgb24>     renderBuffer = renderJob.ImageBuffer;
        Verbose("Updating image");
        IntPtr offset = data.Data;
        for (int y = 0; y < ySize; y++)
            unsafe
            {
                Span<Rgb24> renderBufRow = renderBuffer.GetPixelRowSpan(y);
                void*       destPtr      = offset.ToPointer();
                Span<Rgb24> destRow      = new(destPtr, xSize);

                renderBufRow.CopyTo(destRow);
                offset += data.ScanWidth;
            }

        Verbose("Finished updating image in {Elapsed}", stop.Elapsed);
    }

    private void UpdateStatsTable()
    {
        //...
    }
Main Form:
using Eto.Drawing;
using Eto.Forms;
using RayTracer.Core.Graphics;
using RayTracer.Core.Scenes;
using System;
using System.Reflection;
using System.Threading.Tasks;
using static Serilog.Log;

namespace RayTracer.Display.EtoForms;

public sealed class MainForm : Form
{
    private readonly RenderOptionSelectorPanel? selectorPanel = null;
    private readonly Label                      titleLabel;
    private          AsyncRenderJob?            renderJob = null;

    public MainForm()
    {
        Verbose("MainForm.Ctor()");

        string title = $"RayTracer.Display - v{Assembly.GetEntryAssembly()!.GetName().Version}";
        Title = title;
        Verbose("Title is {Title}", Title);
        MinimumSize = new Size(200, 200);
        Verbose("Minimum app size set to {MinSize}", MinimumSize);
        Padding = 10;

        Verbose("Creating UI elements");
        titleLabel = new Label { Text = title, Font = new Font(FontFamilies.Sans!, 32f, FontStyle.Bold) };
        StackLayoutItem displayedWindowItem = new() { HorizontalAlignment = HorizontalAlignment.Stretch, Expand = true };
        StackLayoutItem titleItem           = new(titleLabel, HorizontalAlignment.Center);
        Content = new StackLayout
        {
                Items   = { titleItem, displayedWindowItem },
                Spacing = 10
        };


        Verbose("Generating commands");
        Command quitCommand = new() { MenuText = "Quit", Shortcut = Application.Instance!.CommonModifier | Keys.Q };
        quitCommand.Executed += (_, _) => Application.Instance.Quit();

        Command aboutCommand = new() { MenuText = "About..." };
        aboutCommand.Executed += (_, _) => new AboutDialog().ShowDialog(this);

        // create menu
        Verbose("Creating menu bar");
        Menu = new MenuBar
        {
                Items =
                {
                        // File submenu
                        // new SubMenuItem { Text = "&File", Items = { clickMe } }
                        // new SubMenuItem { Text = "&Edit", Items = { /* commands/items */ } },
                        // new SubMenuItem { Text = "&View", Items = { /* commands/items */ } },
                },
                ApplicationItems =
                {
                        // application (OS X) or file menu (others)
                        // new ButtonMenuItem { Text = "&Preferences..." }
                },
                QuitItem  = quitCommand,
                AboutItem = aboutCommand
        };
        const string iconPath = "RayTracer.Display.EtoForms.Appearance.icon.png";
        Verbose("Loading and setting icon from {IconPath}", iconPath);
        Icon = Icon.FromResource(iconPath);

        Verbose("Toolbar disabled");
        ToolBar = null; //new ToolBar { Items = { clickMe } };

        //We start off with an options selection panel, then once the user clicks the 'continue' button, we start the render and change it to the render progress panel
        Verbose("Initializing render options selector subview");
        selectorPanel               = new RenderOptionSelectorPanel((_, _) =>  StartRenderButtonClicked());
        displayedWindowItem.Control = selectorPanel;
    }

    private void StartRenderButtonClicked()
    {
        //Assume that the sender is the same selector panel we have stored
        //Might be bad practice but hey who cares it's easier
        RenderOptions options = selectorPanel!.RenderOptions;
        Scene         scene   = selectorPanel.Scene;

        Information("Render start button clicked");
        Debug("Scene is {Scene}, Options are {Options}", scene, options);

        Debug("Creating render job");
        renderJob = new AsyncRenderJob(scene, options);
        Debug("Starting render job");
        Task renderTask = renderJob.TryStartAsync();
        TaskWatcher.Watch(renderTask, true);

        //Create the display panel
        //TODO: Honestly this is a really bad way to do it and I don't like it, but for some reason removing the children from the stack panel
        // does not work (some weird logical child not equal behaviour) so i gotta create a new layout instead :(
        Verbose("Creating render progress display panel");
        RenderProgressDisplayPanel displayPanel = new(renderJob);
        StackLayoutItem            titleItem    = new(titleLabel, HorizontalAlignment.Center);
        Content = new StackLayout
        {
                Items   = { titleItem, displayPanel },
                Spacing = 10
        };
    }
}
Curtis Wensley
@cwensley
Yikes.. gist maybe?
Ararem
@Ararem
Haha sure
1 second
As you can tell, I'm quite a noob at creating UI's, especially in ETO
Curtis Wensley
@cwensley
The problem here is you're using StackLayout without specifying which item(s) should Expand to fill/shrink to the space available.
you need to use new StackLayoutItem(displayPanel, HorizontalAlignment.Stretch, true), and new StackLayoutItem(imageContainer, VerticalAlignment.Stretch, true) for the respective places those are added to the StackLayout.Items.
Curtis Wensley
@cwensley
The *.Stretch tells it to stretch horizontally/vertically (opposite to the orientation), and the True tells it to fill the available space in the direction/orientation of the stack.
Ararem
@Ararem
Oh my god that was it. Thanks so much it's done now
Curtis Wensley
@cwensley
No problem! One difference between other UI's and Eto is that the container is responsible for the final layout of the control, not the control itself.
Ararem
@Ararem
Ah ok
Ararem
@Ararem
Hey very sorry to do this to you again lol but I'm getting super confused from this - adding items to a table causes NullReferenceExcetpions to be thrown by GTK (see picoe/Eto#2181)
Brian Gallaway
@greatawesome
I ported ZedGraph to Eto: https://github.com/greatawesome/ZedGraph