[M3devel] pixmap problem (some success)

Jay jayk123 at hotmail.com
Fri Aug 8 16:05:15 CEST 2008


I committed a possible fix here.
Please see how it fairs.

I have a few Modula-3 questions related to the fix.

 - Did I have to expose the functions in the .i3 file
  that implement the methods? That seems "wrong".


   - Could I have used "stronger language opacity" instead
    of "informal privacy"? That is, could I have used an
    opaque type?

   - Will the change break pickles?
     "Both" due to the addition of data "or" methods?
     ie: What breaks pickles?
     Do I need to think in the mindset of
      C:
       typedef struct {
         int a,b;
       } foo_t;

       foo_t f;
       fwrite(&f, sizeof(f), 1, file);

      and not breaking such code? 
     Only if the type is "branded"? Or if types derived from it are branded?
        There could be derived types not in the cm3 tree though.
        How much do we care about breaking code outside the cm3 tree?
        e.g. in this change, I had to change every use of .xres and .yres.
       I did that, but only in the code "we have". There could be more out there.
       Personally, I get quite frustrated by this issue. I'm very willng to "fix"
        and maybe test pretty large amounts of code -- pay the price for fixing things with
        "breaking" changes, or else not make the change, but if I don't have the code, I can't.

  If this breaks pickles, I can restate the fix in a slightly "weaker" way,
    that has some risk of missing code paths, but will likely suffice.

    That is, initialize to a distinguished value like 0 or -1
     and get rid of the booleans. And, i necessary for pickling,
     put the fields back out in "public".


 The name of the ImageInit module, I wonder what it should be.


   - Jay
     


________________________________

Date: Thu, 7 Aug 2008 21:59:07 -0400
From: rcoleburn at scires.com
To: m3devel at elegosoft.com; jayk123 at hotmail.com; dabenavidesd at yahoo.es
Subject: RE: [M3devel] pixmap problem (some success)



Jay,



The splash screen is not the major issue.  The pixmaps used for boolean choices, radio choices, numerics, etc. get enlarged to the point that some windows won't fit on the screen.  Also, it looks really bad--like both a first-grader with a big marker and a college-student with a fine tip pencil worked on the same window.



I've tried very hard to make Trestle/FormsVBT look and act like Windows GUI, but it is indeed different.  For example, on menus, you have to hold down the mouse to keep the menu open.  All these were small issues 10 years ago, but now they seem to be looming large in the customer's mind.



So, bottom line is that if I can't fix what they deem to be functional problems, they will indeed call for a rewrite.



Now that we've got the typein problem solved (thanks Olaf) and are on track for the pixmap issue, I feel better.  I have recently discovered that the numeric keys on the numeric keypad aren't accepted, so I have yet to track that problem down.



As for your idea of using a variable as the default initializer for xres/yres, this is not legal Modula-3.  You will get a compile error that the default is not constant.  Otherwise, it was a good idea.



Since Raw is an object and objects are created dynamically and new subtypes can be created (inheritance), I think the problem of how to properly set xres/yres is complicated.



Seems like what we need is some way to adjust these fields when the object is created initially.  Alas, Raw does not have an init() method!



Regards,

Randy

>>> Jay  8/7/2008 9:35 PM>>>

Computing the values is probably easy. It appears we already have the code to do that -- the program I had you run and report the numbers.

It is true there is a *small* problem of computing them early enough, but..yeah, I expect that's just done in the module initializer. I really don't like "global initialization" code ala C++ global constructors/destructors, but Modula-3 is rife with them and they aren't going away, so I'll just pile in a few more bits.

I don't know if is legal Modula-3 code, but if fields can be initialized from globals, that's about all it takes.

SOMETHING LIKE (don't castigate me where I am wrong)
In the platform-independent code, add two private globals:
DefaultXRes : INTEGER;
DefaultYRes : INTEGER;

heck, default them to 75, for the Posix case.

In the platform independent interface, functions to set them:
PROCEDURE SetDefaultXRes(a: INTEGER);
PROCEDURE SetDefaultYRes(a: INTEGER);

Nothing else on Posix, for now.

On Windows, add another module WinImage.m3 that imports Image and just has a module initializer:

BEGIN
  Image.SetDefaultXRes(ComputedSomehowNotDifficult());
  Image.SetDefaultYRes(ComputedSomehowNotDifficult());
END.

If fields cannot be initialized from globals (or function calls..heh, that has a niceness!), then you have to search out all the uses and change them to function calls, possibly a lot, possibly tedious, definitely a "breaking change", but probably just fine, really.

And then, if fields can be initialized from functions, that'd be better -- that way no module initializer.


Module initializers are "bad" in that they cause a startup cost for "everyone", even if they aren't needed for a particular scenario. It is "best" imho to have constant initialization as much as possible, and where values must be computed, use an explicit function call when needed. You can still cache the values upon first computation.


> would seem that most code must not be computing these, but relying on the default values to be correct.

Or that "most code" doesn't care what the values are, doesn't use them at all.
Like, maybe they are only used for pixmaps and not text or lines?

Anyway, I can't look into this now, but hopefully "soon".
If your customer is antsy, "the problem is mostly understood and a fix will definitely be available soon".
I think "patch Tuesday" is coming up. Let's make it by then? :)


Really, I can't believe they'd call for a rewrite over this small issue.
Presumably they really want the rewrite and are looking for any excuse they can find.
Also, if you removed the "advertising" from the splash screen, they might not have even noticed?
Or, I guess if this is Windows only, the odd look could not be noticed.
But if they also want it on X and you showed it there, and it looked the same on Windows, well...
(Really this is just a general dilemna -- app looks the same as app on all platforms, or app looks like the platform on all platforms.)


- Jay

________________________________

Date: Thu, 7 Aug 2008 20:26:13 -0400
From: rcoleburn at scires.com
To: m3devel at elegosoft.com; jayk123 at hotmail.com; dabenavidesd at yahoo.es
Subject: RE: [M3devel] pixmap problem (some success)



Well, I hope to soon share your optimism for "easy".



Since these numbers wind up being default values for the xres/yres and since changing the defaults has made a big difference in the results, it would seem that most code must not be computing these, but relying on the default values to be correct.



Since these numbers may be different for every program run on a different monitor (potentially), the question becomes how to effect a change that will work in all cases without having to recompile vbtkit.



Does this mean we have to track down each use of Image.Raw and put in code to compute the dpi?  Or is there some way we can do this during an initialization sequence? but alas, how to deal with the defaults supplied in the declaration?



Regards,

Randy

>>> Jay  8/7/2008 8:16 PM>>>

Awesome. I expect it is easy from here. Thanks David!

I *assume* the 75 should have been 86 in the one case,
but close enough that nobody noticed.
So these numbers come pretty directly from the GetDeviceCaps / GetSystemMetrics.

And then X Windows too.
Does anyone have a high DPI monitor running X Windows?
Probably easy enough to do this blind.

I'm trying to ignore the fact that systems have multiple
monitors, with varying dpi. You are supposed to loop your
drawing over monitors and compute what it looks like per-monitor.
I'm just making that up, right? :)

- Jay

________________________________

Date: Thu, 7 Aug 2008 19:42:33 -0400
From: rcoleburn at scires.com
To: m3devel at elegosoft.com; jayk123 at hotmail.com; dabenavidesd at yahoo.es
Subject: Re: [M3devel] pixmap problem (some success)



Ok, I solved the Microsoft problem.



Here are the results on Dell M4300 at 1920x1200:



horizonal pixels 1920
veritical pixels SM_CYSCREEN 1200
horizontal millimeters 330
veritical millimeters 206
horizontal pixels per millimeter 5.818182
vertical pixels per millimeter 5.825243
horizontal pixels per inch 147.781818
vertical pixels per inch 147.961165

Wow!  these numbers are radically different than the IBM T60 at 1280x1024:



horizonal pixels 1280
veritical pixels SM_CYSCREEN 1024
horizontal millimeters 375
veritical millimeters 300
horizontal pixels per millimeter 3.413333
vertical pixels per millimeter 3.413333
horizontal pixels per inch 86.698667
vertical pixels per inch 86.698667

Now, if you look at the Windows dpi setting for the 1920x1200 machine, it says 96dpi, but this is in the General tab of the Display properties Advanced settings.  I suspect this is just the dpi setting applied to fonts.



I tried plugging the numbers into Image.i3 as suggested by Daniel:

TYPE
  Raw = OBJECT
      width, height: INTEGER;
      xres: REAL := 147.781818; (* in pixels per inch *)
      yres: REAL := 147.961165; (* in pixels per inch *)
    METHODS
      get (h, v: INTEGER): Pixel;
      set (h, v: INTEGER; pixel: Pixel);
    END;

When I do this, my pixmaps look correct on the 1920x1200 display!!!!!



Now, the problem I am faced with is how to come up with a way that the code will work on any type of monitor.  I can't edit Image.i3 for every resolution and produce a different binary.  Any ideas?



Regards,

Randy


>>> Jay  8/5/2008 9:01 AM>>>


Randy, I'm pretty clueless here.
I don't do gui or graphics.
If anyone has a clue, please stand up.
If you can get us code to run, please do.
But I think I need multiple particularly configured machines too.

I'm curious what this code prints on the systems:

#include
#include

int main()
{
int pix_ver = { 0 };
int pix_hor = { 0 };
int mm_hor = { 0 };
int mm_ver = { 0 };
HWND hwnd = { 0 };
HDC hdc = { 0 };

hwnd = GetDesktopWindow();
hdc = GetDC(hwnd);
mm_hor = GetDeviceCaps(hdc, HORZSIZE);
mm_ver = GetDeviceCaps(hdc, VERTSIZE);
pix_hor = GetSystemMetrics(SM_CXSCREEN);
pix_ver = GetSystemMetrics(SM_CYSCREEN);

printf("horizonal pixels %d\n", pix_hor);
printf("veritical pixels SM_CYSCREEN %d\n", pix_ver);
printf("horizontal millimeters %d\n", mm_hor);
printf("veritical millimeters %d\n", mm_ver);

printf("horizontal pixels per millimeter %f\n", (((float) pix_hor) / ((float) mm_hor)));
printf("vertical pixels per millimeter %f\n", (((float) pix_ver) / ((float) mm_ver)));

printf("horizontal pixels per inch %f\n", (((float) pix_hor) / ((float) mm_hor) * 10.0 * 2.54));
printf("vertical pixels per inch %f\n", (((float) pix_ver) / ((float) mm_ver) * 10.0 * 2.54));

return 0;
}


for me:

$ gcc dpi.c -luser32 -lgdi32

jay at jay-win9 /dev2/j/dpi
$ ./a
horizonal pixels 1280
veritical pixels SM_CYSCREEN 800
horizontal millimeters 384
veritical millimeters 240
horizontal pixels per millimeter 3.333333
vertical pixels per millimeter 3.333333
horizontal pixels per inch 84.666667
vertical pixels per inch 84.666667

(Visual C++ is fine:
cl dpi.c user32.lib gdi32.lib
.\dpi
)

I wonder if lying in this code:

Searching for 'GetDeviceCaps'...
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScreenType.m3(25): VAR res := NEW(T); n_colors := GetDeviceCaps (WinGDI.NUMCOLORS);
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScreenType.m3(32): res.depth := GetDeviceCaps(WinGDI.BITSPIXEL); (* John Karnak 8/3/98 *)
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScreenType.m3(68): mm_hor = GetDeviceCaps (WinGDI.HORZSIZE),
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScreenType.m3(69): mm_ver = GetDeviceCaps (WinGDI.VERTSIZE) DO

Searching for 'res.res[Axis.T.'...
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScreenType.m3(71): res.res[Axis.T.Hor] := FLOAT(pix_hor) / FLOAT(mm_hor);
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScreenType.m3(72): res.res[Axis.T.Ver] := FLOAT(pix_ver) / FLOAT(mm_ver);


D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScreenType.m3(89):PROCEDURE GetDeviceCaps (cap: Ctypes.int): INTEGER =
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScreenType.m3(93): res := WinGDI.GetDeviceCaps (hdc, cap);
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScreenType.m3(98): END GetDeviceCaps;
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScrnColorMap.m3(268): cnt := WinGDI.GetDeviceCaps (hdc, WinGDI.NUMCOLORS);
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScrnFont.m3(316): LogicalPixelsPerVertInch := WinGDI.GetDeviceCaps(er.hdc, WinGDI.LOGPIXELSY);
9 occurrence(s) have been found.


Searching for '1000.0'...
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScrnPixmap.m3(463): bmih.biXPelsPerMeter := ROUND (st.res[Axis.T.Hor] * 1000.0);
D:\dev2\cm3.2\m3-ui\ui\src\winvbt\WinScrnPixmap.m3(464): bmih.biYPelsPerMeter := ROUND (st.res[Axis.T.Ver] * 1000.0);


and just claiming 96dpi is the way to go.
Can you try that??


Specifically try setting res.res[Axis.T.Hor] and .Ver to 3.779527559055118.
Or heck to 3.3333 like my laptop has.


Or maybe claiming ignorance and setting biXPelsPerMeter and biYPelsPerMeter to 0???
Claiming ignorance feels better than lying of course. :)

Hm, so throw in also:

printf("LogicalPixelsX %u\n", GetDeviceCaps(hdc, LOGPIXELSX));
printf("LogicalPixelsY %u\n", GetDeviceCaps(hdc, LOGPIXELSY));

I get the magic number 96.

- Jay





More information about the M3devel mailing list