1 // wxshowmath.cpp                       Copyright (C) 2016-2020, A C Norman
2 
3 /**************************************************************************
4  * Copyright (C) 2020, Codemist.                         A C Norman       *
5  *                                                                        *
6  * Redistribution and use in source and binary forms, with or without     *
7  * modification, are permitted provided that the following conditions are *
8  * met:                                                                   *
9  *                                                                        *
10  *     * Redistributions of source code must retain the relevant          *
11  *       copyright notice, this list of conditions and the following      *
12  *       disclaimer.                                                      *
13  *     * Redistributions in binary form must reproduce the above          *
14  *       copyright notice, this list of conditions and the following      *
15  *       disclaimer in the documentation and/or other materials provided  *
16  *       with the distribution.                                           *
17  *                                                                        *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    *
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      *
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS      *
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE         *
22  * COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,   *
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,   *
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  *
25  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR  *
27  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF     *
28  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH   *
29  * DAMAGE.                                                                *
30  *************************************************************************/
31 
32 // $Id: wxshowmath.cpp 5555 2020-12-30 22:11:56Z arthurcnorman $
33 
34 
35 // This is a fresh implementation of this code and will avoid use of
36 // custom fonts used with wxGraphicsContext - this is bacause in order
37 // to use custom fontw in a wxGraphicsContext I needed a private extension
38 // to wxWidgets. The code here should work using a standard copy of wxWidgets.
39 // To achieve accurate character placement I will use a memory buffer
40 // rather bigger than the screen and first draw onto that, and then do
41 // scale reduction to a second buffer sized as per the real window I am
42 // painting in. That real window will never be bigger than the screen size.
43 
44 
45 #ifndef __STDC_FORMAT_MACROS
46 #define __STDC_FORMAT_MACROS 1
47 #endif
48 
49 #ifndef __STDC_CONSTANT_MACROS
50 #define __STDC_CONSTANT_MACROS 1
51 #endif
52 
53 #include "wx/wxprec.h"
54 
55 #ifndef WX_PRECOMP
56 #include"wx/wx.h"
57 #endif
58 
59 #include <wx/display.h>
60 #include <wx/graphics.h>
61 #include <wx/filename.h>
62 #include <wx/rawbmp.h>
63 
64 #include"config.h"
65 
66 #include "wxfwin.h"
67 
68 #ifdef WIN32
69 #include <windows.h>
70 #endif  // WIN32
71 
72 // I may be old fashioned, but I will be happier using C rather than C++
73 // libraries here.
74 
75 #include <cstring>
76 #include <cstdlib>
77 #include <cstdio>
78 #include <cstdarg>
79 #include <cctype>
80 #include <ctime>
81 #include <csignal>
82 #include <cstdint>
83 #include <cinttypes>
84 
85 #if HAVE_UNISTD_H
86 #include <unistd.h>
87 #else
88 extern char *getcwd(char *s, size_t n);
89 #endif // HAVE_UNISTD_H
90 
91 #include <sys/stat.h>
92 #include <sys/types.h>
93 #include <errno.h>
94 
95 #ifdef HAVE_DIRENT_H
96 #include <dirent.h>
97 #else
98 #ifndef WIN32
99 #include <sys/dir.h>
100 #else
101 #include <direct.h>
102 #endif
103 #endif // HAVE_DIRENT_H
104 
105 #if !defined __WXMSW__ && !defined __WXPM__
106 #include"fwin.xpm" // Icon to use in non-Windows cases
107 #endif
108 
109 // I have a generated file that contains the widths of all the fonts
110 // that I am willing to use here.
111 
112 #include "uninames.h"
113 #include "charmetrics.h"
114 
115 static wxStopWatch sw;
116 
117 static int options = 5;  // default to using my own scaling
118 
119 static std::FILE *logFile = nullptr;
120 
printlog(const char * fmt,...)121 static int printlog(const char *fmt, ...)
122 {   std::va_list a;
123     int r = 0;
124     if (logFile == nullptr) logFile = std::fopen("wxshowmath.log", "w");
125     if (logFile != nullptr)
126     {   va_start(a, fmt);
127         r = std::vfprintf(logFile, fmt, a);
128         std::fflush(logFile);
129         va_end(a);
130     }
131 #ifndef MACINTOSH
132 // On systems other than the Mac I expect I can (sometimes!) have a console
133 // attached to my program, and in that case it will be convenient to sent the
134 // trace output there as well as to a file.
135     va_start(a, fmt);
136     r = std::vprintf(fmt, a);
137     va_end(a);
138     std::fflush(stdout);
139 #endif
140     return r;
141 }
142 
143 class showmathApp : public wxApp
144 {
145 public:
146     virtual bool OnInit();
147 };
148 
149 #define MAX_FONTS 16
150 wxFont *Font[MAX_FONTS];
151 int Baseline[MAX_FONTS];
152 bool FontValid[MAX_FONTS];
153 
154 
155 class showmathPanel : public wxPanel
156 {
157 public:
158     showmathPanel(class showmathFrame *parent,
159                   const char *showmathFilename);
160 
161     void OnPaint(wxPaintEvent &event);
162 
163 // The event handling is not really needed for this application, but I
164 // am putting some in so I can experiment with it!
165     void OnChar(wxKeyEvent &event);
166     void OnKeyDown(wxKeyEvent &event);
167     void OnKeyUp(wxKeyEvent &event);
168     void OnMouse(wxMouseEvent &event);
169 
170     wxFont FixedPitch;
171     int FixedPitchBaseline;
172 
173     char *showmathData;  // the .showmath file's contents are stored here
174 
175     DECLARE_EVENT_TABLE()
176 };
177 
178 BEGIN_EVENT_TABLE(showmathPanel, wxPanel)
179     EVT_PAINT(           showmathPanel::OnPaint)
180     EVT_CHAR(            showmathPanel::OnChar)
181 //  EVT_KEY_DOWN(        showmathPanel::OnKeyDown)
182 //  EVT_KEY_UP(          showmathPanel::OnKeyUp)
183     EVT_LEFT_UP(         showmathPanel::OnMouse)
184 END_EVENT_TABLE()
185 
186 // The size (in pixels, or at least in the units that wxWidgets uses)
187 // of my screen. No window can ever be bigger than this.
188 int screenWidth, screenHeight;
189 
190 wxBitmap *bigBitmap;
191 wxMemoryDC *bigDC;
192 // I have bigBitmap which is bigWidth*bigHeight, which is 4 times the
193 // size of my screen (well my screen as reported and worked with by
194 // wxWidgets). Eg for a HD monitor that means it will be 7680x4320 which
195 // is 31 megapixels. It turns out to be VITALLY IMPORTANT for performance
196 // that I make this bitmnap have the same depth as my screen. It ends up
197 // using quite a lot of memory!
198 
199 int bigWidth, bigHeight;
200 
201 // I will limit the area of my window to the size of my screen, and so once
202 // borders (and perhaps scroll bars and other decorations) have been allowed
203 // for the client area will be strictly smaller. The size of it is recorded
204 // here.
205 
206 int clientWidth, clientHeight;
207 
208 // The client area will be mapped by a region in the top left hand corner of
209 // bigBitmap. This will be larger by some factor that is in the range 3.5
210 // to 4. usedScale records this scale factor and usedWidth and usedHeight
211 // the size of this area. Note that the constraints I have documented ensure
212 // that this fits within bigBitmap. I am going to arrange that usedWidth is
213 // either a multiple of 80 or only a few pixels larger. That is so that I can
214 // fit a row of 80 fixed-pitch characters across my window neatly.
215 
216 double usedScale;
217 int usedWidth, usedHeight;
218 
219 // To help me redraw the client area of my screen reasonably rapidly I
220 // will work with tiles, and I will require usedScale to be a rational
221 // number bigTileSize/smallTileSize. Then I will have usedWidth as
222 // ceiling(usedScale*clientWidth) and usedHeight = celing(usedScale*
223 // clientHeight). If I do this I can align my tiles precisely on both
224 // client and big bitmaps. Vertical measurements are in charOffset and
225 // charLinespacing, and pointSize records the point size passed
226 // to wxWidgets to create the font. To help me choose a suitable point size
227 // I record charWidth_1000 which is the width of a 1000-point character.
228 
229 wxBitmap *bigTile = nullptr;
230 wxBitmap *smallTile = nullptr;
231 wxMemoryDC *tileDC;
232 int smallTileSize, bigTileSize;
233 
234 // bigFont is a fixed pitch font sized for use on bigBitmap, such that
235 // each character there has width charWidth, and usedWidth is just over
236 // 80*charWidth. The latter is so that the line neatly fills my window.
237 
238 wxFont *bigFont;
239 
240 // A maths font...
241 wxFont *mathFont;
242 
243 int pointSize;   // size of the font.
244 int charWidth_1000;  // width of characters when font is 1000 points.
245 int charWidth;       // for fixed pitch font, as on bigBitmap.
246 int charOffset;      // so that I print relative to index point.
247 int charLinespace;   // how much to separate lines by.
248 
249 // Once I have done all that I intend to set a wxWidgets UserScale so that
250 // the user works using integer coordinates such that the apparent point size
251 // of the fixed point font is 10000 -- I intend to think of this as working
252 // in millipoints, and it amounts to pretending that the fixed pitch
253 // font I am working with is at 10 points (even though on screen it will
254 // typically be larger).
255 
256 // The scale factor between bigBitmap and the client area means that I
257 // will initially position and render characters between 3.5 and 4 times as
258 // precisely as single-pixel addressing would have allowed. Copying down from
259 // bigBitmap to the screen will do anti-aliasing and support sub-pixel
260 // character placement. I may need to experiment to decide if this reduction
261 // factor is about right, but at present my guess is that it will give
262 // usefully better visual effects than schemes that force all characters to
263 // snap to pixels, and that more oversampling would bot gain anything
264 // worth while. And the cost of what I am doing now is perhaps already
265 // fairly bad.
266 
267 
268 class showmathFrame : public wxFrame
269 {
270 public:
271     showmathFrame(const char *showmathFilename);
272 
273     void RepaintBuffer();
274 
275     void OnClose(wxCloseEvent &event);
276     void OnExit(wxCommandEvent &event);
277     void OnAbout(wxCommandEvent &event);
278     void OnSize(wxSizeEvent &event);
279 
280 private:
281     showmathPanel *panel;
282     DECLARE_EVENT_TABLE()
283 };
284 
BEGIN_EVENT_TABLE(showmathFrame,wxFrame)285 BEGIN_EVENT_TABLE(showmathFrame, wxFrame)
286     EVT_CLOSE(           showmathFrame::OnClose)
287     EVT_MENU(wxID_EXIT,  showmathFrame::OnExit)
288     EVT_MENU(wxID_ABOUT, showmathFrame::OnAbout)
289     EVT_SIZE(            showmathFrame::OnSize)
290 END_EVENT_TABLE()
291 
292 #ifndef LONGEST_LEGAL_FILENAME
293 #define LONGEST_LEGAL_FILENAME 1024
294 #endif
295 
296 int main(int argc, char *argv[])
297 {   int usegui = 1;
298 // Find where I am invoked from before doing anything else
299     find_program_directory(argv[0]);
300 // The next fragment is not very useful in THIS program but stands in for
301 // behaviour I want in more complicated cases.
302     for (int i=1; i<argc; i++)
303     {   if (std::strncmp(argv[i], "-w", 2) == 0) usegui = 0;
304         if (std::strncmp(argv[i], "-x",
305                          2) == 0) options = std::atoi(argv[i]+2);
306     }
307 #if !defined WIN32 && !defined MACINTOSH
308 // Under X11 I will demote to being a console mode application if DISPLAY
309 // is not set. This is not a perfect test but it will spot the simple
310 // cases. Eg I could look at stdin & stdout and check if it looks as if
311 // they are pipes of they have been redirected...
312     {   const char *s = std::getenv("DISPLAY");
313         if (s==nullptr || *s == 0) usegui = 0;
314     }
315 #endif
316     if (usegui)
317     {
318 #ifdef MACINTOSH
319 // If I will be wanting to use a GUI and if I have just loaded an
320 // executable that is not within an application bundle then I will
321 // use "open" to launch the corresponding application bundle. Doing this
322 // makes resources (eg fonts) that are within the bundle available and
323 // it also seems to cause things to terminate more neatly.
324         char xname[LONGEST_LEGAL_FILENAME];
325         std::sprintf(xname,"%s.app", programName);
326         if (std::strstr(fullProgramName, xname) == nullptr)
327         {
328 // Here the binary I launched was not located as
329 //      ...foo.app../.../foo
330 // so I will view it is NOT being from an application bundle. I will
331 // re-launch it so it is! This may be a bit of a hacky way to decide!
332             struct stat buf;
333             std::sprintf(xname,"%s.app", fullProgramName);
334             if (stat(xname, &buf) == 0 &&
335                 (buf.st_mode & S_IFDIR) != 0)
336             {
337 // Well foo.app exists and is a directory, so I will try to use it
338                 const char **nargs =
339                     new (std::nothrow) char *[argc+3];
340                 if (nargs == nullptr) my_abort();
341                 nargs[0] ="/usr/bin/open";
342                 nargs[1] = xname;
343                 nargs[2] ="--args";
344                 for (int i=1; i<argc; i++)
345                     nargs[i+2] = argv[i];
346                 nargs[argc+2] = nullptr;
347 // /usr/bin/open foo.app --args [any original arguments]
348                 return execv("/usr/bin/open", const_cast<char * const *>(nargs));
349             }
350         }
351 #endif
352 // I have my own "main" function and call wxEntry from it. So I need
353 // to call the following so that if I compiled with NDEBUG set a range of
354 // assertions etc will not be checked...
355         wxDISABLE_DEBUG_SUPPORT();
356         add_custom_fonts();
357 #if DEBUG
358 //      display_font_information();
359 #endif
360         return wxEntry(argc, (char **)argv);
361     }
362 //
363 // The following is a bit silly but is here to prove that I can launch this
364 // code in console mode if I wish to. In this case it is not very useful!
365 //
366     std::printf("This program has been launched asking for use in a console\n");
367     std::printf("type a line of text please\n");
368     int c;
369     while ((c = std::getchar()) != '\n' && c != EOF) std::putchar(c);
370     std::putchar('\n');
371     std::printf("Exiting from demonstration of console mode use!\n");
372     return 0;
373 }
374 
IMPLEMENT_APP_NO_MAIN(showmathApp)375 IMPLEMENT_APP_NO_MAIN(showmathApp)
376 
377 //=========================================================================
378 //
379 // Now that start of my code in a proper sense!
380 //
381 //
382 
383 bool showmathApp::OnInit()
384 {
385 // The program can take an argument - grab that here and pass it down to
386 // showmathFrame - or nullptr if there was none.
387     char **myargv = (char **)argv;
388     const char *showmathFilename = nullptr;
389     for (int i=1; i<argc; i++)
390         if (myargv[i][0] != '-') showmathFilename = myargv[i];
391 #ifdef WIN32
392     static char tidyFilename[LONGEST_LEGAL_FILENAME];
393     if (showmathFilename != nullptr &&
394         std::strncmp(showmathFilename, "/cygdrive/", 10) == 0 &&
395         showmathFilename[11] == '/')
396     {   std::sprintf(tidyFilename, "%c:%s",
397                      showmathFilename[10], showmathFilename+11);
398         showmathFilename = tidyFilename;
399     }
400 #endif
401     sw.Start(0);  // start the StopWatch
402     showmathFrame *frame = new showmathFrame(showmathFilename);
403     frame->Show(true);
404     return true;
405 }
406 
showmathFrame(const char * showmathFilename)407 showmathFrame::showmathFrame(const char *showmathFilename)
408     : wxFrame(nullptr, wxID_ANY, "wxshowmath")
409 {   SetIcon(wxICON(fwin));
410     panel = new showmathPanel(this, showmathFilename);
411     int numDisplays = wxDisplay::GetCount(); // how many displays?
412 // It is not clear to me what I should do if there are several displays,
413 // and if there are none I am probably in a mess!
414     if (numDisplays != 1)
415     {   printlog("There seem to be %d displays\n", numDisplays);
416     }
417     wxDisplay d0(0);                         // just look at display 0
418     wxRect screenArea(d0.GetClientArea());   // omitting task bar
419     screenWidth = screenArea.GetWidth();
420     screenHeight = screenArea.GetHeight();
421 
422     bigBitmap = new wxBitmap(bigWidth=4*screenWidth,
423                              bigHeight=4*screenHeight,
424                              24); // wxBITMAP_SCREEN_DEPTH);
425     bigDC = new wxMemoryDC();
426     bigTile = nullptr;
427     smallTile = nullptr;
428     tileDC = new wxMemoryDC();
429     bigDC->SelectObject(*bigBitmap);
430 //
431 // I will now create a font suitable for writing into the big
432 // bitmap. Well here I will just make one using "CMU Typewriter Text", a
433 // fixed pitch font. I start by creating a font that is 1000 points. This
434 // is of course ridiculously big, but maybe it lets me measure widths
435 // accurately. Actually the size I use here is not that important and
436 // I will (repeatedly) change it later on.
437     wxFontInfo bigfi(1000);
438     bigfi.FaceName("CMU Typewriter Text");
439     bigFont = new wxFont(bigfi);
440     mathFont = new wxFont(wxFontInfo(10).FaceName(wxT("cslSTIXMath")));
441     bigDC->SetFont(*bigFont);
442     int w, h, d, xl;
443     bigDC->GetTextExtent("X", &w, &h, &d, &xl);
444     charWidth_1000 = w;
445     bigDC->SelectObject(wxNullBitmap);
446 // I am now going to pick a default initial size for the client area
447 // of my main window. The choice here is perhaps somewhat arbitrary and
448 // may only be good for MY screen.
449     clientWidth = 640;
450     clientHeight = 880;
451 // If the default size would fill over 90% of screen width or height I
452 // clip it down to make it fit better. The consequence of this is that on
453 // computers in general the window size may not end up a neat multiple
454 // of anything in particular.
455     if (10*clientWidth > 9*screenWidth) clientWidth = 90*screenWidth/100;
456     if (10*clientHeight > 9*screenHeight) clientHeight =
457             90*screenHeight/100;
458     SetClientSize(clientWidth, clientHeight);
459 // I put a minimum on the client size...
460     SetMinClientSize(wxSize(400, 100));
461 // ... but a maximum on the total size including borders and decorations.
462     SetMaxSize(wxSize(screenWidth, screenHeight));
463     RepaintBuffer(); // I must get an image ready before I first try to
464     // display the window.
465     Centre();
466     Refresh();          // Force full re-draw so it appears on-screen
467 }
468 
469 int bestP, bestQ, bestErr;
470 
471 // Check all rational numbers in a given range so I can see which
472 // one will work best for me. Look up "Farey Sequence" for more insight.
473 
farey(int p1,int q1,int p2,int q2,int maxQ)474 void farey(int p1, int q1, int p2, int q2, int maxQ)
475 {   int uw = (p2 * clientWidth)/q2;
476     int err = uw % 80;
477     if (err <= bestErr)
478     {   if (err < bestErr || q2 < bestQ)
479         {   bestP = p2;
480             bestQ = q2;
481         }
482         bestErr = err;
483     }
484     int p = p1 + p2;
485     int q = q1 + q2;
486     if (q > maxQ) return;
487     farey(p1, q1, p, q, maxQ);
488     farey(p, q, p2, q2, maxQ);
489 }
490 
491 #if 0
492 // When I was developing this code I ran this fragment to help me judge how
493 // large denominators would be useful. I record the following table of
494 // which shows how large a denominator must be accepted in order to
495 // limit the gap at the right hand end of the line to a given number of
496 // pixels for all possible client widths in the range 400..2000.
497 //        gap  necessary denominator
498 //         10          13
499 //          8          16
500 //          6          18
501 //          5          19
502 //          4          22
503 //          3          25  <<<< my choice
504 //          2          31
505 //          1          40
506 //          0          53
507 // Based on this table I pick 25 as my cut-off.
508 for (int m = 2; m<80; m++)
509 {   int cw = clientWidth;
510     int maxQ = m;
511     int worstErr = 0;
512     int Q = 0, WW = 0;
513     for (clientWidth = 400; clientWidth <=2000; clientWidth++)
514     {   bestP = 7;
515         bestQ = 2;
516         int uw = (bestP * clientWidth)/bestQ;
517         bestErr = uw % 80;
518         farey(7, 2, 4, 1, maxQ);
519         if (bestErr > worstErr)
520         {   worstErr = bestErr;
521             WW = clientWidth;
522             Q = bestQ;
523         }
524     }
525     printlog("at maxQ = %d see err = %d at cw=%d, Q=%d\n",
526              m, worstErr, WW, Q);
527     clientWidth = cw;
528 }
529 #endif
530 
531 // The map of tiles here can cover 64*64 tiles in all.  The small tiles
532 // can be from 20x20 to 120*120 so that this can deal with a raw display
533 // area up to 7680x7680, which is ridiculously large by the standards of
534 // the year in which this code is being written.
535 
536 static uint64_t tileMap[64];
537 
538 #define maxSmallTileSize 120
539 #define maxBigTileSize (4*maxSmallTileSize)
540 
convert_font_name(char * dest,char * src)541 static int32_t convert_font_name(char *dest, char *src)
542 {
543 // The font name passed should be one of
544 //    cmuntt
545 //    odokai
546 //    math
547 //    <anything else>
548 //    <anything else>-Bold
549 //    <anything else>-Italic
550 //    <anything else>-BoldItalic
551 //
552     int32_t r = wxFONTFLAG_DEFAULT;
553     if (std::strcmp(src, "cmuntt") == 0) std::strcpy(dest,
554                 "CMU Typewriter Text");
555     else if (std::strcmp(src, "odokai") == 0) std::strcpy(dest,
556                 "AR PL New Kai");
557     else if (std::strcmp(src, "Math") == 0) std::strcpy(dest,
558                 "cslSTIXMath");
559     else std::sprintf(dest, "cslSTIX");
560 // Here if the font name is suffixed as "-Bold" or "-Italic" or "-BoldItalic"
561     if (std::strcmp(dest,
562                     "CMU Typewriter Text") == 0) r |= (F_cmuntt<<16);
563     else if (std::strcmp(dest, "AR PL New Kai") == 0) r |= (F_odokai<<16);
564     else if (std::strcmp(dest, "cslSTIXMath") == 0) r |= (F_Math<<16);
565 // I have not thought through and implemented support for bold and italic
566 // options here...
567 #ifdef PENDING_BOLD_AND_ITALIC
568     else if ((r & (wxFONTFLAG_BOLD + wxFONTFLAG_ITALIC)) ==
569              (wxFONTFLAG_BOLD + wxFONTFLAG_ITALIC)) r |= (F_BoldItalic<<16);
570     else if ((r & wxFONTFLAG_BOLD) ==
571              wxFONTFLAG_BOLD) r |= (F_Bold<<16);
572     else if ((r & wxFONTFLAG_ITALIC) ==
573              wxFONTFLAG_ITALIC) r |= (F_Italic<<16);
574     else
575 #endif
576         r |= (F_Regular<<16);
577     printlog("Gives %s with flags %x\n", dest, r); std::fflush(stdout);
578     return r;
579 }
580 
581 // I want to use the whole range of Unicode, and in particular I wish to
582 // use characters beyond U+FFFF. If I am using a computer where wchar_t
583 // is 16-bits I will need to do that by transmitting the code using
584 // UTF-16. This applies on Windows. If I am on a machine where
585 // wchar_t is a 32-bit type I can use a single unit to represent any
586 // codepoint at all. This function adjusts...
587 
allow_for_utf16(wchar_t * ccc,int cp)588 static void allow_for_utf16(wchar_t *ccc, int cp)
589 {   if (sizeof(wchar_t) == 4 ||
590         cp <= 0xffff)
591     {   ccc[0] = cp;
592         ccc[1] = 0;
593     }
594     else
595     {   cp = (cp - 0x10000) & 0xfffff;
596         ccc[0] = 0xd800 + (cp >> 10);
597         ccc[1] = 0xdc00 + (cp & 0x3ff);
598         printlog("Char mapped to %x %x\n", ccc[0], ccc[1]);
599         ccc[2] = 0;
600     }
601 }
602 
RepaintBuffer()603 void showmathFrame::RepaintBuffer()
604 {   printlog("RepaintBuffer called\n");
605 // Now that I know how big by client window will be I can work out how
606 // much of the big bitmap I will use.
607     bestP = 7;
608     bestQ = 2;
609     int uw = (bestP * clientWidth)/bestQ;
610     bestErr = uw % 80;
611     farey(7, 2, 4, 1, 25);
612 // My small tiles will have an ideal size such that there are about 64 of
613 // them across the window. If the window is narrow I will keep the size
614 // at least 20 pixels. So each tile is slightly larger than a single
615 // character. I will also limit things so that the window is no more than
616 // 64 tiles high.
617     {   int nw = (clientWidth + 63)/64;  // ideal width
618         int nh = (clientHeight + 63)/64; // ideal height
619         int n = nh > nw ? nh : nw;       // use the larger
620         if (n < 20) n = 20;
621         int r = (n + bestQ - 1)/bestQ;
622         bigTileSize = r*bestP;
623         smallTileSize = r*bestQ;
624     }
625     printlog("Small tiles are %d square, with %d*%d covering window\n",
626              smallTileSize,
627              (clientWidth+smallTileSize-1)/smallTileSize,
628              (clientHeight+smallTileSize-1)/smallTileSize);
629     if (bigTile != nullptr) delete bigTile;
630     bigTile =
631         new wxBitmap(bigTileSize, bigTileSize, 24); // wxBITMAP_SCREEN_DEPTH);
632     if (smallTile != nullptr) delete smallTile;
633     smallTile =
634         new wxBitmap(smallTileSize, smallTileSize,
635                      24); // wxBITMAP_SCREEN_DEPTH);
636 // The used region of the big bitmap will be bestP/bestQ times the size of
637 // the client area. Well that might not be exactly an integer, so I will
638 // round up here, and what I will end up with will be a width that is 0, 1,
639 // 2, 3 or 4 modulo 80. When scaled down to the client area that means that
640 // I can expect 80 columns of text to come within a pixel of the width. That
641 // seems like pretty good fitting to me.
642     usedWidth = (bestP*clientWidth + bestQ - 1)/bestQ;
643     usedHeight = (bestP*clientHeight + bestQ - 1)/bestQ;
644     bigDC->SelectObject(
645         *bigBitmap); // Writing to the DC writes into bitmap
646 // Predict a point size to use...
647     pointSize = (usedWidth*1000 + 40*charWidth_1000)/(80*charWidth_1000);
648 // There us a potential danger that xWidgets create a font here that was
649 // not exactly what I wanted - and in particular that it might have created
650 // one (slightly) bigger than I wanted, leading to 80 characters not fitting
651 // neatly on the line. If that is the case I will decrease the requested
652 // point size by 1 and try again. Equally I could at least imagine the
653 // font ending up too small, in which case I will make it larger.
654     int width, height, descent, xleading;
655     for (;;)
656     {   bigFont->SetPointSize(pointSize);
657         mathFont->SetPointSize(pointSize);
658         bigDC->SetFont(*bigFont);
659         bigDC->GetTextExtent("X", &width, &height, &descent, &xleading);
660         if (80*width > usedWidth) pointSize--;
661         if (80*width <= usedWidth-80) pointSize++;
662         charWidth = width;
663         charOffset = height - descent;
664         charLinespace = height + xleading;
665         break;
666     }
667     printlog("Now 80 chars should have with %d in bigBitmap (cf %d)\n",
668              80*charWidth, usedWidth);
669 
670 
671 // Now I should find how all my fonts will be arranged in terms of the
672 // distance from the index point used by wxWidgets to the font base-line
673 // as relevent in .afm metrics. Horribly this is something that seems
674 // to be platform dependent, but rather than just testing what system I am
675 // building on I will run some code to measure and make my choices on that
676 // basis. The cases I check are ones that I believe reveal the differences!
677 // This seems hideously hacky.
678     for (;;)
679     {   int depth, leading;
680         wxFont tf1(wxFontInfo(10000).FaceName(
681                        wxT("cslSTIX")).Bold().Italic());
682         bigDC->SetFont(tf1);
683         bigDC->GetTextExtent(wxString((wchar_t)'X'),
684                              &width, &height, &depth, &leading);
685         printlog("%d %d [%d]\n", height, depth, height-depth);
686         if ((height - depth + 5)/10 == chardepth_WIN32[F_BoldItalic])
687         {   chardepth = chardepth_WIN32;
688             break;
689         }
690         tf1.SetStyle(wxFONTSTYLE_NORMAL);
691         tf1.SetFaceName(wxT("AR PL New Kai"));
692         bigDC->SetFont(tf1);
693         bigDC->GetTextExtent(wxString((wchar_t)'X'),
694                              &width, &height, &depth, &leading);
695         printlog("%d %d [%d]\n", height, depth, height-depth);
696         if ((height - depth + 5)/10 == chardepth_X11[F_odokai])
697         {   chardepth = chardepth_X11;
698             break;
699         }
700         tf1.SetFaceName(wxT("cslSTIXMath"));
701         bigDC->SetFont(tf1);
702         bigDC->GetTextExtent(wxString((wchar_t)unicode_INTEGRAL),
703                              &width, &height, &depth, &leading);
704         printlog("%d %d [%d]\n", height, depth, height-depth);
705         if ((height - depth + 5)/10 == chardepth_OSX[F_Math])
706         {   chardepth = chardepth_OSX;
707             break;
708         }
709         printlog("\n+++ Character positioning not recognized\n");
710         chardepth = chardepth_X11;
711         break;
712     }
713     bigDC->SetFont(*bigFont);
714 
715 //
716 // Now so that I can see what is going in a bit I will draw a row of
717 // characters across the top of bigBitmap...
718     bigDC->SetBackground(*wxWHITE_BRUSH);
719     bigDC->Clear();
720     for (int i=0; i<80; i++)
721     {   bigDC->DrawText(wxString((wchar_t)(i+0x21)),
722                         i*charWidth,
723                         charLinespace - charOffset);
724     }
725 #if 1
726 // Draw blocks every 1000 pixels in bigBitmap
727     bigDC->SetBrush(*wxYELLOW_BRUSH);
728     for (int i=0; i<usedWidth; i+=1000)
729         bigDC->DrawRectangle(i, 2*charLinespace, 500, charLinespace);
730 #endif
731 
732 // Next I move to draw some stuff that is a bit more "mathsy"
733     wxFont regular(wxFontInfo(pointSize).FaceName(wxT("cslSTIX")));
734     bigDC->SetFont(regular);
735     bigDC->GetTextExtent(wxString((wchar_t)'x'), &width, &height,
736                          &descent, &xleading);
737     printlog("%d %d %d %d regular\n", width, height, descent, xleading);
738     int regularBaseline = height - descent;
739     printlog("regular baseline = %d\n", regularBaseline);
740 
741     bigDC->SetFont(*mathFont);
742     bigDC->GetTextExtent(wxString((wchar_t)'x'), &width, &height,
743                          &descent, &xleading);
744     printlog("%d %d %d %d math\n", width, height, descent, xleading);
745     int mathBaseline = height - descent;
746     printlog("math baseline = %d\n", mathBaseline);
747 
748 // cslSTIXMath puts the glyphs used to build up big delimiters in a
749 // private use area. It expects the code to use maths tables to discover
750 // where they are rather than mere unicode names. But for this test and
751 // demonstration I will just use absolute code-points.
752 
753 #define stix_LEFT_CURLY_BRACKET_UPPER_HOOK    0x10821c
754 #define stix_LEFT_CURLY_BRACKET_MIDDLE_PIECE  0x10821d
755 #define stix_CURLY_BRACKET_EXTENSION          0x10821f
756 #define stix_LEFT_CURLY_BRACKET_LOWER_HOOK    0x10821e
757 
758     lookupchar(F_Math, stix_LEFT_CURLY_BRACKET_UPPER_HOOK);
759     printlog("upper hook   %d %d\n", c_lly, c_ury);
760     lookupchar(F_Math, stix_LEFT_CURLY_BRACKET_MIDDLE_PIECE);
761     printlog("middle piece %d %d\n", c_lly, c_ury);
762     lookupchar(F_Math, stix_LEFT_CURLY_BRACKET_LOWER_HOOK);
763     printlog("lower hook   %d %d\n", c_lly, c_ury);
764     lookupchar(F_Math, stix_CURLY_BRACKET_EXTENSION);
765     printlog("extension    %d %d\n", c_lly, c_ury);
766 
767     double s = static_cast<double>(pointSize)/10.0;
768 #define H (10.0)
769 #define XX 120.0
770 #define YY 100.0
771     {   wchar_t ccc[4];
772         allow_for_utf16(ccc, stix_LEFT_CURLY_BRACKET_UPPER_HOOK);
773         printlog("Character %#x %#x\n", ccc[0], ccc[1]);
774         bigDC->DrawText(wxString(ccc), s*XX, s*(YY-H)-mathBaseline);
775         allow_for_utf16(ccc, stix_LEFT_CURLY_BRACKET_MIDDLE_PIECE);
776         bigDC->DrawText(wxString(ccc), s*XX, s*YY-mathBaseline);
777         allow_for_utf16(ccc, stix_CURLY_BRACKET_EXTENSION);
778         bigDC->DrawText(wxString(ccc), s*XX, s*(YY+H)-mathBaseline);
779         allow_for_utf16(ccc, stix_LEFT_CURLY_BRACKET_LOWER_HOOK);
780         bigDC->DrawText(wxString(ccc), s*XX, s*(YY+2.0*H)-mathBaseline);
781     }
782 // The next two have codepoints in the Basic Multilingual Plane so
783 // I do not need to mess around with encoding even on platforms like
784 // Windows that use UTF16 internally.
785     bigDC->SetFont(regular);
786     bigDC->DrawText(wxString((wchar_t)unicode_GREEK_SMALL_LETTER_OMEGA),
787                     s*XX, s*(YY+100.0)-regularBaseline);
788     bigDC->DrawText(wxString((wchar_t)
789                              unicode_RIGHT_ANGLE_WITH_DOWNWARDS_ZIGZAG_ARROW),
790                     s*(XX+100.0), s*(YY+100.0)-regularBaseline);
791 // Now I will try a row of text.Or some material provided on an input file.
792     const char *in = panel->showmathData;
793     printlog("About to process data:\n\"%.70s\"... ...\n\n", in);
794     do
795     {   int x, y, n, cp, size;
796         char name[100], name1[64];
797         while (std::isspace(*in)) in++;
798         if (*in == 0) break;
799         if (*in == '#' || *in == '%')
800 // # ...     comments extend to the end of the line
801 // % ...
802         {   n = 0;
803             while (*in != 0 && *in != '\n')
804                 if (n < 99) name[n++] = *in++;
805                 else in++;
806             name[n] = 0;
807             printlog("%s\n", name);
808             if (*in == '\n') in++;
809             continue;
810         }
811         else if (std::sscanf(in, "deffont %d %60s %d;", &n, name,
812                              &size) == 3 &&
813 // deffont number name size;   define font with given number
814                  0 <= n &&
815                  n < MAX_FONTS)
816         {   int flags = convert_font_name(name1, name);
817             int col;
818             printlog("font[%d] = \"%s\" size %d\n", n, name1, size);
819             Font[n] =
820                 new wxFont(wxFontInfo((pointSize*size*5)/144).FaceName(name1));
821             bigDC->SetFont(*Font[n]);
822             bigDC->GetTextExtent(wxString((wchar_t)'('), &width, &height,
823                                  &descent, &xleading);
824             printlog("( %s/%d: %d %d [%d]\n",
825                      name1, size, height, descent, height-descent);
826             col = printlog("    %d,",
827                            static_cast<int>((height - descent)/10.0 + 0.5));
828             while (col++ < 20) printlog(" ");
829             printlog("// %s\n", name);
830             Baseline[n] =   // This still needs review!
831                 size * chardepth[(flags >> 16) & 0x1f] / 1000.0;
832             printlog("from table baseline offset = %.6g\n", Baseline[n]);
833         }
834         else if (std::sscanf(in, "put %d %d %d 0x%x;", &n, &x, &y,
835                              &cp) == 4 ||
836                  std::sscanf(in, "put %d %d %d %d;", &n, &x, &y, &cp) == 4)
837         {
838 // put fontnum xpos ypos codepoint;  dump character onto screen
839 // note x & y in units of 1/1000 point.
840 //          printlog("Font %d (%d,%d) char %d = %#x\n", n, x, y, cp, cp);
841             bigDC->SetFont(*Font[n]);
842             wchar_t ccc[4];
843 // For the benefit of Windows I need to represent code points in other
844 // then the basic multilingual pane as surrogate pairs. Well that will
845 // probably apply anywhere where sizeof(wchar_t) < 4.
846             allow_for_utf16(ccc, cp);
847             bigDC->DrawText(wxString(ccc),
848                             (s*x)/2400, s*150 + (400-y)/2400); //-graphicsBaseline[n]);
849         }
850         else printlog("\nLine <%.32s> unrecognised\n", in);
851         in = std::strchr(in, ';');
852         if (in != nullptr) in++;
853     }
854     while (in != nullptr);
855 
856 // I will mark all the fonts I might have created as invalid now
857 // that the context they were set up for is being left.
858     for (int i=0; i<MAX_FONTS; i++) FontValid[i] = false;
859     bigDC->SelectObject(wxNullBitmap);
860     return;
861 }
862 
863 static char default_data[4096] =
864     "deffont 1 Regular 24;"
865     "put 1 -408 0 84;"
866     "put 1 14256 0 104;"
867     "put 1 26256 0 101;"
868     "put 1 36912 0 32;"
869     "put 1 42912 0 98;"
870     "put 1 54912 0 111;"
871     "put 1 66384 0 121;"
872     "put 1 78384 0 32;"
873     "put 1 84384 0 115;"
874     "put 1 93528 0 116;"
875     "put 1 100152 0 111;"
876     "put 1 112152 0 111;"
877     "put 1 124152 0 100;"
878     "put 1 136152 0 32;"
879     "put 1 142152 0 111;"
880     "put 1 154152 0 110;"
881     "put 1 166152 0 32;"
882     "put 1 172152 0 116;"
883     "put 1 179016 0 104;"
884     "put 1 191016 0 101;"
885     "put 1 201672 0 32;"
886     "put 1 207672 0 98;"
887     "put 1 219672 0 117;"
888     "put 1 231672 0 114;"
889     "put 1 240168 0 110;"
890     "put 1 252168 0 105;"
891     "put 1 258840 0 110;"
892     "put 1 270840 0 103;"
893     "put 1 282840 0 32;"
894     "put 1 288840 0 100;"
895     "put 1 300840 0 101;"
896     "put 1 311496 0 99;"
897     "put 1 321624 0 107;"
898     "put 1 333744 0 44;"
899     "put 1 339744 0 32;"
900     "put 1 345744 0 119;"
901     "put 1 363072 0 104;"
902     "put 1 375072 0 101;"
903     "put 1 385728 0 110;"
904     "put 1 397728 0 99;"
905     "put 1 408384 0 101;"
906     "put 1 419040 0 32;"
907     "put 1 425040 0 97;"
908     "put 1 435696 0 108;"
909     "put 1 442368 0 108;"
910     "put 1 449040 0 32;"
911     "put 1 455040 0 98;"
912     "put 1 467040 0 117;"
913     "put 1 479040 0 116;"
914     "put 1 485712 0 32;"
915     "put 1 491712 0 104;"
916     "put 1 503712 0 101;"
917     "put 1 514368 0 32;"
918     "put 1 520368 0 104;"
919     "put 1 532368 0 97;"
920     "put 1 543024 0 100;"
921     "put 1 555024 0 32;"
922     "put 1 561024 0 102;"
923     "put 1 570072 0 108;"
924     "put 1 576744 0 101;"
925     "put 1 587400 0 100;"
926     "put 1 599400 0 46;";
927 
928 // When I construct this I must avoid the wxTAB_TRAVERSAL style since that
929 // tends to get characters passed to child windows not this one. Avoiding
930 // that is the reason behind providing so many arguments to the parent
931 // constructor
932 
showmathPanel(showmathFrame * parent,const char * showmathFilename)933 showmathPanel::showmathPanel(showmathFrame *parent,
934                              const char *showmathFilename)
935     : wxPanel(parent, wxID_ANY, wxDefaultPosition,
936               wxDefaultSize, 0L,"showmathPanel")
937 {
938 // I will read in any data once here and put it in a character buffer.
939     std::FILE *f = nullptr;
940     if (showmathFilename == nullptr) showmathData = default_data;
941     else
942     {   int i;
943         f = std::fopen(showmathFilename,"r");
944         if (f == nullptr)
945         {   printlog("File \"%s\" not found\n", showmathFilename);
946             std::exit(1);
947         }
948         std::fseek(f, (off_t)0, SEEK_END);
949         off_t len = std::ftell(f);
950         showmathData = new char[4+(size_t)len];
951         std::fseek(f, (off_t)0, SEEK_SET);
952         for (i=0; i<len; i++) showmathData[i] = std::getc(f);
953         showmathData[i] = 0;
954         std::fclose(f);
955     }
956     for (int i=0; i<MAX_FONTS; i++)
957     {   FontValid[i] = false;
958         Baseline[i] = 0;
959     }
960 }
961 
962 
OnClose(wxCloseEvent & WXUNUSED (event))963 void showmathFrame::OnClose(wxCloseEvent &WXUNUSED(event))
964 {   Destroy();
965 #ifdef WIN32
966 // Otherwise under XP bad things happen for me. Like the application
967 // re-launching. I do not think I understand, but the extreme action of
968 // utterly killing the process does what I need!
969     TerminateProcess(GetCurrentProcess(), 1);
970 #else
971     std::exit(0);    // I want the whole application to terminate here!
972 #endif
973 }
974 
OnExit(wxCommandEvent & WXUNUSED (event))975 void showmathFrame::OnExit(wxCommandEvent &WXUNUSED(event))
976 {   Destroy();
977 #ifdef WIN32
978     TerminateProcess(GetCurrentProcess(), 1);
979 #else
980     std::exit(0);    // I want the whole application to terminate here!
981 #endif
982 }
983 
OnAbout(wxCommandEvent & WXUNUSED (event))984 void showmathFrame::OnAbout(wxCommandEvent &WXUNUSED(event))
985 {
986 // At present this never gets activated!
987     wxMessageBox(
988         wxString::Format(
989             "wxshowmath (A C Norman 2016)\n"
990             "wxWidgets version: %s\n"
991             "Operating system: %s",
992             wxVERSION_STRING,
993             wxGetOsDescription()),
994         "About wxshowmath",
995         wxOK | wxICON_INFORMATION,
996         this);
997 }
998 
OnSize(wxSizeEvent & WXUNUSED (event))999 void showmathFrame::OnSize(wxSizeEvent &WXUNUSED(event))
1000 {   wxSize clientSize(GetClientSize());
1001     int w = clientSize.GetWidth(), h = clientSize.GetHeight();
1002     if (clientWidth == w) printlog("Width has not changed\n");
1003     if (clientHeight == h) printlog("Height has not changed\n");
1004 //  if (clientWidth == w && clientHeight == h) return;
1005     clientWidth = w;
1006     clientHeight = h;
1007     printlog("New window size %d*%d\n", clientWidth, clientHeight);
1008     panel->SetSize(clientWidth, clientHeight);
1009     printlog("About to repaint\n");
1010     RepaintBuffer();
1011     printlog("Completed repaint\n");
1012     Refresh();
1013 }
1014 
OnChar(wxKeyEvent & event)1015 void showmathPanel::OnChar(wxKeyEvent &event)
1016 {
1017 // This merely records the character in the log file.
1018     const char *msg ="OnChar", *raw ="";
1019     int c = event.GetUnicodeKey();
1020     if (c == WXK_NONE) c = event.GetKeyCode(), raw ="Raw";
1021     if (0x20 < c && c < 0x7f) printlog("%s%s %x (%c)\n", msg, raw, c, c);
1022     else printlog("%s%s %x\n", msg, raw, c);
1023 }
1024 
OnKeyDown(wxKeyEvent & event)1025 void showmathPanel::OnKeyDown(wxKeyEvent &event)
1026 {
1027 // Again merely log the event.
1028     const char *msg ="OnKeyDown", *raw ="";
1029     int c = event.GetUnicodeKey();
1030     if (c == WXK_NONE) c = event.GetKeyCode(), raw ="Raw";
1031     if (0x20 < c && c < 0x7f) printlog("%s%s %x (%c)\n", msg, raw, c, c);
1032     else printlog("%s%s %x\n", msg, raw, c);
1033     event.Skip();
1034 }
1035 
OnKeyUp(wxKeyEvent & event)1036 void showmathPanel::OnKeyUp(wxKeyEvent &event)
1037 {
1038 // Merely log the event.
1039     const char *msg ="OnKeyUp", *raw ="";
1040     int c = event.GetUnicodeKey();
1041     if (c == WXK_NONE) c = event.GetKeyCode(), raw ="Raw";
1042     if (0x20 < c && c < 0x7f) printlog("%s%s %x (%c)\n", msg, raw, c, c);
1043     else printlog("%s%s %x\n", msg, raw, c);
1044     event.Skip();
1045 }
1046 
OnMouse(wxMouseEvent & event)1047 void showmathPanel::OnMouse(wxMouseEvent &event)
1048 {
1049 // Log but take no action.
1050     printlog("Mouse event\n");
1051     event.Skip();
1052 // Here I use a mouse event to force a re-draw.
1053     Refresh();     // forces redraw of everything
1054 }
1055 
OnPaint(wxPaintEvent & event)1056 void showmathPanel::OnPaint(wxPaintEvent &event)
1057 {   printlog("OnPaint called\n");
1058     wxPaintDC mydc(this);
1059 //    mydc.SetBackground(*wxWHITE_BRUSH);
1060 //@ The fillowing few lines would make sense if the window I was working
1061 //@ with here was a scrolled one... which in the fullness of time it will be!
1062 //@    int vbX, vbY;
1063 //@    GetViewStart(&vbX, &vbY);
1064 //@    printlog("top left of client is at %d %d\n", vbX, vbY);
1065     wxRegionIterator upd(GetUpdateRegion());
1066     if (!upd) return; // no update regiions reported!
1067 // Rather than re-painting the whole screen I should cover just the
1068 // regions I have been told to refresh. I will use tileMap to control
1069 // the actual painting.
1070     for (int i=0; i<64; i++) tileMap[i] = 0;
1071     while (upd)
1072     {   int vX = upd.GetX();
1073         int vY = upd.GetY();
1074         int vW = vX + upd.GetW();
1075         int vH = vY + upd.GetH();
1076 // I set a bit in tileMap for each tile that will actually need painting.
1077         printlog("Need to repaint %d %d %d %d\n", vX, vY, vW, vH);
1078         for (int y=vY/smallTileSize;
1079              y<=(vH+smallTileSize-1)/smallTileSize;
1080              y++)
1081             for (int x=vX/smallTileSize;
1082                  x<=(vW+smallTileSize-1)/smallTileSize;
1083                  x++)
1084             {
1085 #ifdef SHOW_WHICH_TILES_WILL_BE_REDRAWN
1086                 printlog("tile at %d %d to be redrawn\n",
1087                          x*smallTileSize, y*smallTileSize);
1088 #endif
1089                 tileMap[y] |= ((uint64_t)1)<<x;
1090             }
1091         upd++;
1092     }
1093 #ifdef DISPLAY_TILE_BITMAP
1094     for (int y=0; y<64; y++)
1095     {   uint64_t v = tileMap[y];
1096         for (int x=0; x<64; x++)
1097         {   std::putchar('0' + static_cast<int>(v & 1));
1098             v = v >> 1;
1099         }
1100         std::putchar('\n');
1101     }
1102 #endif
1103     sw.Start(0);
1104     int tileCount = 0;
1105     typedef wxPixelData<wxBitmap, wxNativePixelFormat> PixelData;
1106     PixelData bigData(*bigBitmap);
1107     if (!bigData) printlog("Creation of bigData failed\n");
1108     PixelData smallData(*smallTile);
1109     if (!smallData) printlog("Creation of smallData failed\n");
1110     PixelData::Iterator pBig(bigData);
1111     PixelData::Iterator pSmall(smallData);
1112     for (int tileY=0; tileY<clientHeight; tileY+=smallTileSize)
1113         for (int tileX=0; tileX<clientWidth;  tileX+=smallTileSize)
1114         {   if ((tileMap[tileY/smallTileSize] &
1115                  (((uint64_t)1)<<(tileX/smallTileSize))) == 0) continue;
1116             tileCount++;
1117             pBig.MoveTo(bigData, (bigTileSize*tileX)/smallTileSize,
1118                         (bigTileSize*tileY)/smallTileSize);
1119             pSmall.MoveTo(smallData, 0, 0);
1120             int srcY=0, srcY1=smallTileSize, destY=0, destY1=bigTileSize;
1121             int srcPixYR[maxSmallTileSize],
1122                 srcPixYG[maxSmallTileSize],
1123                 srcPixYB[maxSmallTileSize],
1124                 destPixYR[maxBigTileSize],
1125                 destPixYG[maxBigTileSize],
1126                 destPixYB[maxBigTileSize];
1127             int scale = bigTileSize*bigTileSize;
1128             for (int i=0; i<smallTileSize; i++)
1129             {   srcPixYR[i] = 0;  srcPixYG[i] = 0;  srcPixYB[i] = 0;
1130                 destPixYR[i] = 0; destPixYG[i] = 0; destPixYB[i] = 0;
1131             }
1132             while (destY < smallTileSize)
1133             {   PixelData::Iterator rowStartSmall = pSmall;
1134                 PixelData::Iterator rowStartBig = pBig;
1135                 int srcX=0, srcX1=smallTileSize, destX=0, destX1=bigTileSize;
1136                 int srcPixXR=0, srcPixXG=0, srcPixXB=0,
1137                     destPixXR=0, destPixXG=0, destPixXB=0,
1138                     w;
1139                 while (destX < smallTileSize)
1140                 {
1141 // The next few lines are the only ones where I access the source bitmap...
1142                     srcPixXR = pBig.Red();
1143                     srcPixXG = pBig.Green();
1144                     srcPixXB = pBig.Blue();
1145                     ++pBig;
1146                     srcX++;
1147                     if (srcX1 < destX1)
1148                     {   destPixXR += smallTileSize*srcPixXR;
1149                         destPixXG += smallTileSize*srcPixXG;
1150                         destPixXB += smallTileSize*srcPixXB;
1151                     }
1152                     else
1153                     {   w = srcX1 - destX1;
1154                         srcPixYR[destX] = (srcPixXR*(smallTileSize-w) + destPixXR);
1155                         srcPixYG[destX] = (srcPixXG*(smallTileSize-w) + destPixXG);
1156                         srcPixYB[destX] = (srcPixXB*(smallTileSize-w) + destPixXB);
1157                         destPixXR = srcPixXR*w;
1158                         destPixXG = srcPixXG*w;
1159                         destPixXB = srcPixXB*w;
1160                         destX++;
1161                         destX1 += bigTileSize;
1162                     }
1163                     srcX1 += smallTileSize;
1164                 }
1165                 pBig = rowStartBig;
1166                 pBig.OffsetY(bigData, 1);
1167                 srcY++;
1168                 if (srcY1 < destY1)
1169                 {   for (int i=0; i<smallTileSize; i++)
1170                     {   destPixYR[i] += smallTileSize*srcPixYR[i];
1171                         destPixYG[i] += smallTileSize*srcPixYG[i];
1172                         destPixYB[i] += smallTileSize*srcPixYB[i];
1173                     }
1174                 }
1175                 else
1176                 {   w = srcY1 - destY1;
1177                     for (int i=0; i<smallTileSize; i++)
1178                     {
1179 // The next few lines are the only ones where I access the destination bitmap...
1180                         pSmall.Red() =
1181                             (srcPixYR[i]*(smallTileSize-w) + destPixYR[i])/scale;
1182                         pSmall.Green() =
1183                             (srcPixYG[i]*(smallTileSize-w) + destPixYG[i])/scale;
1184                         pSmall.Blue() =
1185                             (srcPixYB[i]*(smallTileSize-w) + destPixYB[i])/scale;
1186                         ++pSmall;
1187                         destPixYR[i] = srcPixYR[i]*w;
1188                         destPixYG[i] = srcPixYG[i]*w;
1189                         destPixYB[i] = srcPixYB[i]*w;
1190                     }
1191                     pSmall = rowStartSmall;
1192                     pSmall.OffsetY(smallData, 1);
1193                     destY++;
1194                     destY1 += bigTileSize;
1195                 }
1196                 srcY1 += smallTileSize;
1197             }
1198             mydc.DrawBitmap(*smallTile, tileX, tileY);
1199         }
1200     printlog("Scale %d tiles from bitmap to screen in %" PRId64 "\n",
1201              tileCount, (int64_t)sw.Time());
1202 
1203 
1204 }
1205 
1206 // A dummy definition that is needed because of wxfwin.cpp
1207 
windowed_worker(int argc,const char * argv[],fwin_entrypoint * fwin_main)1208 int windowed_worker(int argc, const char *argv[],
1209                     fwin_entrypoint *fwin_main)
1210 {   return 0;
1211 }
1212 
1213 // end of wxshowmath.cpp
1214