[M3devel] pixmap problem (! big success !)

Randy Coleburn rcoleburn at scires.com
Sat Aug 9 07:48:15 CEST 2008


Jay:
 
Thanks so much for your work on this issue!!!  In my sleep-deprived state I didn't think of switching these fields to be "accessor-based."
 
I've tested your changes and they seem to work fine for me.  I've rebuilt all of cm3 just to make sure there were no compile-time errors.
 
I also had to adjust my scroll bar implementation on Windows to scale the scroll bar slider.  (The slider was not a pixmap and was being drawn based on a default size in millimeters.  Since the arrow buttons at the top and bottom of the scroll bar are pixmaps, I had to ensure the slider and margins didn't exceed the width of these pixmaps.)  Your procedures in ImageInit made this easy for me.  The revised code is now checked into the repository.
 
I probably need to make the same fix to the slider in the Posix implementation, but since we aren't yet dealing with resolution differences on Posix, maybe it doesn't matter for now.  Also, there are no arrow buttons on the Posix scroll bar, so it will probably be okay to just let the slider be the default width in millimeters.  The reason I had trouble on Windows was that the slider was wider than the arrow pixmaps when rendered on the hi-res display.
 
After reflecting on your comments and discussion, I will volunteer with your kind permission to adjust your Image.i3/Image.m3 implementation to be "more in the spirit of Modula-3".  Namely, I do think it makes sense to make these fields truly opaque so that access must be governed by the methods.  I feel pretty good about the fact that your change didn't break any of the rest of the code in the cm3 tree, so I think there will be few, if any, code breaks in other client code.
 
Also, I did notice that in ImageInit.m3 you declare a global var "init" but never assign the default value of "FALSE" to this variable before it is tested in the two procedures.  Shouldn't this be set to FALSE, or are you relying on something of which I'm unaware?  
 
Also, in ImageInit.m3 you call WinNT.MemoryBarrier().  I looked in the WinNT.i3 file, but there is no comment describing this function.  I'm unfamiliar with this function, so I would welcome any explanation you can offer as to what it does.
 
As for pickles, I don't see how any of your changes would break pickles.  The pickler should still be able to pickle these types.  Of course, anyone who is using pickles will need to recompile to see the new format, but there should not be any source code changes required.
 
I also need to thank Daniel for cluing us into the pixmap scaling problem in the first place by pointing out the xres/yres constants of 75.0.
 
At some point, I do think it makes sense to make a similar set of fixes on Posix because I'm sure that the 75 dpi is not going to be accurate for most modern displays.  Of course, we will need to find a way to come up with the right value in a platform-independent way.
 
Regards,
Randy

>>> Jay <jayk123 at hotmail.com> 8/8/2008 10:05 AM >>>

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



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://m3lists.elegosoft.com/pipermail/m3devel/attachments/20080809/00e22eca/attachment-0002.html>


More information about the M3devel mailing list