Search code examples
bitmapdialogdm-script

Has deployment of dialog graphics changed in GMS3?


I make extensive use of graphic elements in DM dialogs, mainly for visual feedback. Although I have used these successfully in GMS1 and GMS2, I have not yet gotten these to behave correctly in GMS3. I wonder whether I am deploying them incorrectly. The example script below illustrates my two main problems, a mismatch between the size of the graphic element and its associated bit map, and a strange contrast mapping (to black) for bit map values of 240 or RGB(240, 240, 240):

    class ModelessDialogWithGraphic : UIFrame
    {
        Object Init(Object self)
        {
            TagGroup dialogSpec = DLGCreateDialog("");
            TagGroup dialogItems = dialogSpec.DLGGetItems();

            Number graphicW = 256;
            Number graphicH = graphicW / 4;
            Image graphicImage := RealImage("Graphic Image", 4, graphicW, graphicH);
            graphicImage = icol;

            // Add labeled box with graphic
            TagGroup boxSpec = DLGCreateBox("Graphic");
            TagGroup boxItems = boxSpec.DLGGetItems();
            TagGroup graphicSpec = DLGCreateGraphic(graphicW, graphicH);
            graphicSpec.DLGAddBitMap(graphicImage);
            boxItems.DLGAddElement(graphicSpec);
            dialogItems.DLGAddElement(boxSpec);

            return self.super.Init(dialogSpec);
        }
    }

    void main()
    {
        Object dialog = Alloc(ModelessDialogWithGraphic).Init();
        dialog.Display("Dialog Graphic Test");
    }

    main();

Output of script in GMS 3.4

At least in GMS 3.4, the bitmap seems only to fill the top-left quarter of the specified graphic area. However, this issue is complicated because the behavior I observe seems to change with the Windows display scaling option and the particular version of Windows. For now, with GMS 3.4 and the latest Win10 update, I have found the following altered Init method provides serviceable (though ugly) workarounds:

        Object Init(Object self)
        {
            TagGroup dialogSpec = DLGCreateDialog("");
            TagGroup dialogItems = dialogSpec.DLGGetItems();

            Number graphicW = 256;
            Number graphicH = graphicW / 4;
            Image graphicImage := RealImage("Graphic Image", 4, graphicW, graphicH);
            graphicImage = icol;
            graphicImage = (graphicImage == 240) ? 241 : graphicImage

            // Add labeled box with graphic
            Number scaler = 0.5;
            TagGroup boxSpec = DLGCreateBox("Graphic");
            TagGroup boxItems = boxSpec.DLGGetItems();
            TagGroup graphicSpec = DLGCreateGraphic(scaler * graphicW, scaler * graphicH);
            graphicSpec.DLGAddBitMap(graphicImage);
            boxItems.DLGAddElement(graphicSpec);
            dialogItems.DLGAddElement(boxSpec);

            return self.super.Init(dialogSpec);
        }

Output of script in GMS 3.4

I'm concerned, however, that these workarounds may break in a future GMS release. Has anyone found a better or more correct way to deploy dialog graphics in GMS3?


Solution

  • After some recent experimentation, I finally managed to work out what the DLGCreateGraphic function does, at least through GMS 3.5.3. It does respond to the Windows display scaling, but not quite as one might expect, given that DM itself immediately responds to any changes in this Windows Display setting, as shown below:

    Windows 10 display settings

    DLGCreateGraphic enlarges the dimensions specified for a graphic by the display scaling factor. So if you ask for a graphic of width 100 and the current Display scaling is 200%, then it will actually reserve a space of width 200 (pixels) in the dialog. The unexpected part of this behavior is that what DLGCreateGraphic takes as the Windows Display scaling is the one that was in effect at the time the user last logged into their account, i.e. this internal DM value does not change by relaunching DM. One must log out and log back into one's Windows account.

    The script below allows one to "calibrate" the current value of DM's dialog graphic scaling factor and save it to a global tag:

    class GraphicScalingCalibrator : UIFrame
    {
        Number dialogWidth;
        
        Object Init(Object self, Number graphicWidth)
        {
            TagGroup dialogSpec = DLGCreateDialog("");
            TagGroup dialogItems = dialogSpec.DLGGetItems();
            
            // Add graphic
            TagGroup graphicSpec = DLGCreateGraphic(graphicWidth, 8);
            dialogItems.DLGAddElement(graphicSpec);
            
            self = self.super.Init(dialogSpec);
            self.Display("Graphic Scale Calibration");
            
            Number frameT, frameL, frameB, frameR;
            self.GetFrameBounds(frameT, frameL, frameB, frameR);
            dialogWidth = frameR - frameL;
            
            return self;
        }
        
        Number GetDialogWidth(Object self)
        {
            return dialogWidth;
        }
    }
    
    void main()
    {
        Object testDialog;
    
        Number graphicWidth1 = 200;
        testDialog = Alloc(GraphicScalingCalibrator).Init(graphicWidth1);
        Number dialogWidth1 = testDialog.GetDialogWidth();
        testDialog.Close();
        
        Number graphicWidth2 = 400;
        testDialog = Alloc(GraphicScalingCalibrator).Init(graphicWidth2);
        Number dialogWidth2 = testDialog.GetDialogWidth();
        testDialog.Close();
        
        Number graphicScalingFactor = (dialogWidth2 - dialogWidth1) / (graphicWidth2 - graphicWidth1);
        GetPersistentTagGroup().TagGroupSetTagAsNumber("Graphic scaling factor", graphicScalingFactor);
        Result("Graphic scaling factor: " + graphicScalingFactor + "\n");
    }
    
    main();
    

    This calibrated value can then be used to adjust the size of any allocated dialog graphics to match the sizes of their associated bitmaps, as shown in the following example:

    class DialogWithGraphic : UIFrame
    {
        Object Init(Object self, Number bitmapWidth)
        {
            TagGroup dialogSpec = DLGCreateDialog("");
            TagGroup dialogItems = dialogSpec.DLGGetItems();
            TagGroup boxSpec = DLGCreateBox("Graphic");
            TagGroup boxItems = boxSpec.DLGGetItems();
            dialogItems.DLGAddElement(boxSpec);
                    
            // Get graphic scaling factor
            Number scalingFactor;
            GetPersistentTagGroup().TagGroupGetTagAsNumber("Graphic scaling factor", scalingFactor);
    
            // Add graphic
            Number graphicWidth = Round(bitmapWidth / scalingFactor);
            Number graphicHeight = Round(graphicWidth / 4);
            TagGroup graphicSpec = DLGCreateGraphic(graphicWidth, graphicHeight);
            boxItems.DLGAddElement(graphicSpec);
            
            // Add bitmap to graphic
            Number bitmapHeight = Round(bitmapWidth / 4);
            Image bitmap := RealImage("Graphic bitmap", 4, bitmapWidth, bitmapHeight);
            bitmap = 255 * Sin(Pi() * icol / 20) * Sin(Pi() * irow / 20);
            graphicSpec.DLGAddBitmap(bitmap);
            
            self = self.super.Init(dialogSpec);
            self.Display("Dialog Graphic Test");
            
            return self;
        }
    }
    
    void main()
    {
        Number bitmapWidth = 400;
        Alloc(DialogWithGraphic).Init(bitmapWidth);
    }
    
    main();
    

    Thus yielding the following result:

    dialog with scaled graphic