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