OpenCPN Partial API docs
ocpn_pixel.cpp
1 /******************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: Optimized wxBitmap Object
5  * Author: David Register
6  *
7  ***************************************************************************
8  * Copyright (C) 2010 by David S. Register *
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  * This program is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18  * GNU General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU General Public License *
21  * along with this program; if not, write to the *
22  * Free Software Foundation, Inc., *
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24  ***************************************************************************
25  *
26  */
27 
28 // Original comment header for xshm test code
29 // imported from xshm.c
30 /* xshm.c */
31 
32 /*
33  * Example of how to use the X Shared Memory extension: MIT_SHM.
34  * This code was lifted from my Mesa library. It hasn't been tested
35  * in this form but should be close enough for you to get it working.
36  * Beware that this extension isn't available on all systems. Your
37  * application code should use #ifdef's around this code so it can be
38  * omitted on systems that don't have it, then fallback to using a regular
39  * XImage.
40  *
41  * Brian Paul Sep, 20, 1995 brianp@ssec.wisc.edu
42  */
43 
44 // ============================================================================
45 // declarations
46 // ============================================================================
47 // ----------------------------------------------------------------------------
48 // headers
49 // ----------------------------------------------------------------------------
50 
51 // For compilers that support precompilation, includes "wx.h".
52 #include <wx/wxprec.h>
53 #ifndef WX_PRECOMP
54 #include <wx/wx.h>
55 #endif
56 
57 #include "dychart.h"
58 #include "ocpn_pixel.h"
59 
60 #ifndef WX_PRECOMP
61 #include <stdio.h>
62 
63 #include <wx/list.h>
64 #include <wx/utils.h>
65 #include <wx/app.h>
66 #include <wx/palette.h>
67 #include <wx/dcmemory.h>
68 #include <wx/bitmap.h>
69 #include <wx/icon.h>
70 #endif
71 
72 #ifdef __WXMSW__
73 #include <wx/msw/private.h>
74 #include <wx/log.h>
75 #include <wx/msw/dib.h>
76 #endif
77 
78 #include <wx/bitmap.h>
79 #include <wx/icon.h>
80 #include <wx/log.h>
81 #include <wx/image.h>
82 #include <wx/app.h>
83 #include <wx/math.h>
84 #include <wx/gdicmn.h>
85 #include <wx/palette.h>
86 
87 // missing from mingw32 header
88 #ifndef CLR_INVALID
89 #define CLR_INVALID ((COLORREF)-1)
90 #endif // no CLR_INVALID
91 
92 #ifdef ocpnUSE_ocpnBitmap
93 #ifdef __WXX11__
94 #include <sys/ipc.h>
95 #include <sys/shm.h>
96 #include <X11/extensions/XShm.h>
97 #endif
98 #endif
99 
100 #ifdef __WXX11__
101 
102 #ifdef ocpUSE_MITSHM
103 /*
104  * MIT-SHM Test Error handling.
105  */
106 static int MITErrorFlag = 0;
107 static int HandleXError(Display *dpy, XErrorEvent *event) {
108  MITErrorFlag = 1;
109  return 0;
110 }
111 #endif
112 
113 //---------------------------------------------------------------------------------------------------------
114 // Private Memory Management
115 //---------------------------------------------------------------------------------------------------------
116 
117 static void *x_malloc(size_t t) {
118  void *pr = malloc(t);
119 
120  // malloc fails
121  if (NULL == pr) {
122  wxLogMessage(_T("x_malloc...malloc fails with request of %d bytes."), t);
123 
124  // Cat the /proc/meminfo file
125 
126  char *p;
127  char buf[2000];
128  int len;
129 
130  int fd = open("/proc/meminfo", O_RDONLY);
131 
132  if (fd == -1) exit(1);
133 
134  len = read(fd, buf, sizeof(buf) - 1);
135  if (len <= 0) {
136  close(fd);
137  exit(1);
138  }
139  close(fd);
140  buf[len] = 0;
141 
142  p = buf;
143  while (*p) {
144  // printf("%c", *p++);
145  }
146 
147  exit(0);
148  return NULL; // for MSVC
149  }
150 
151  else {
152  if (t > malloc_max) {
153  malloc_max = t;
154  // wxLogMessage(_T("New malloc_max: %d",
155  // malloc_max));
156  }
157 
158  return pr; // good return
159  }
160 }
161 
162 //----------------------------------------------------------------------
163 // ocpnXImage Implementation
164 //----------------------------------------------------------------------
165 
166 ocpnXImage::ocpnXImage(int width, int height) {
167  m_width = width;
168  m_height = height;
169  buse_mit = false;
170  m_img = NULL;
171 
172  xdisplay = (Display *)wxGlobalDisplay();
173  xscreen = DefaultScreen(xdisplay);
174  xvisual = DefaultVisual(xdisplay, xscreen);
175  int bpp = wxTheApp->GetVisualInfo(xdisplay)->m_visualDepth;
176 
177 #ifdef ocpUSE_MITSHM
178 
179  // Check to see if the basic extension is supported
180  int ignore;
181  bool bMIT_SHM =
182  XQueryExtension(xdisplay, "MIT-SHM", &ignore, &ignore, &ignore);
183 
184  if (bMIT_SHM) {
185  m_img = XShmCreateImage(xdisplay, xvisual, bpp, ZPixmap, NULL, &shminfo,
186  width, height);
187  if (m_img == NULL) {
188  wxLogError(_T("XShmCreateImage failed!"));
189  goto after_check;
190  }
191 
192  // Identify and allocate the shared memory buffer
193  shminfo.shmid = shmget(IPC_PRIVATE, m_img->bytes_per_line * m_img->height,
194  IPC_CREAT | 0777);
195  if (shminfo.shmid < 0) {
196  XDestroyImage(m_img);
197  m_img = NULL;
198  wxLogMessage(
199  _T("alloc_back_buffer: Shared memory error (shmget), disabling."));
200  goto after_check;
201  }
202 
203  shminfo.shmaddr = m_img->data = (char *)shmat(shminfo.shmid, 0, 0);
204 
205  if (shminfo.shmaddr == (char *)-1) {
206  XDestroyImage(m_img);
207  m_img = NULL;
208  wxLogMessage(_T("shmat failed"));
209  goto after_check;
210  }
211 
212  // Make some further checks
213  shminfo.readOnly = False;
214  MITErrorFlag = 0;
215 
216  XSetErrorHandler(HandleXError);
217  // This may trigger the X protocol error we're ready to catch:
218  XShmAttach(xdisplay, &shminfo);
219  XSync(xdisplay, False);
220 
221  if (MITErrorFlag) {
222  // we are on a remote display, this error is normal, don't print it
223  XFlush(xdisplay);
224  MITErrorFlag = 0;
225  XDestroyImage(m_img);
226  m_img = NULL;
227  shmdt(shminfo.shmaddr);
228  shmctl(shminfo.shmid, IPC_RMID, 0);
229  goto after_check;
230  }
231 
232  shmctl(shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
233 
234  buse_mit = true; // passed all tests
235  }
236 
237 after_check:
238  // if bMIT_SHM
239 #endif
240 
241  if (NULL == m_img) {
242  m_img = XCreateImage(xdisplay, xvisual, bpp, ZPixmap, 0, 0, width, height,
243  32, 0);
244  m_img->data = (char *)x_malloc(m_img->bytes_per_line * m_img->height);
245 
246  if (m_img->data == NULL) {
247  XDestroyImage(m_img);
248  m_img = NULL;
249  wxLogError(wxT("ocpn_Bitmap:Cannot malloc for data image."));
250  }
251  }
252 }
253 
254 ocpnXImage::~ocpnXImage() {
255 #ifdef ocpUSE_MITSHM
256  if (buse_mit) {
257  XShmDetach(xdisplay, &shminfo);
258  XDestroyImage(m_img);
259  shmdt(shminfo.shmaddr);
260  } else {
261  XDestroyImage(m_img);
262  }
263 #else
264  XDestroyImage(m_img);
265 #endif
266 }
267 
268 bool ocpnXImage::PutImage(Pixmap pixmap, GC gc) {
269 #ifdef ocpUSE_MITSHM
270  if (buse_mit)
271  XShmPutImage(xdisplay, pixmap, gc, m_img, 0, 0, 0, 0, m_width, m_height,
272  False);
273  else
274  XPutImage(xdisplay, pixmap, gc, m_img, 0, 0, 0, 0, m_width, m_height);
275 
276 #else
277  XPutImage(xdisplay, pixmap, gc, m_img, 0, 0, 0, 0, m_width, m_height);
278 #endif
279 
280  return true;
281 }
282 
283 #endif // __WXX11__
284 
285 /* Class : PixelCache
286  Why a specific class for what is, in effect, a simple unsigned char[] ?
287  Answer: Allow performance optimization for specific platforms,
288  such as MSW dibSections, and X11 Pixmaps
289 */
290 
291 // ============================================================================
292 // PixelCache Implementation
293 // ============================================================================
294 PixelCache::PixelCache(int width, int height, int depth) {
295  m_width = width;
296  m_height = height;
297  m_depth = depth;
298  m_pbm = NULL;
299  m_rgbo = RGB; // default value;
300  pData = NULL;
301 
302  bytes_per_pixel = BPP / 8;
303  line_pitch_bytes = bytes_per_pixel * width;
304 
305 #ifdef ocpnUSE_ocpnBitmap
306  m_rgbo = BGR;
307 #endif
308 
309 #ifdef __PIX_CACHE_PIXBUF__
310  m_rgbo = RGB;
311 #endif
312 
313 #ifdef __PIX_CACHE_DIBSECTION__
314  m_pDS = new wxDIB(width, -height, BPP);
315  pData = m_pDS->GetData();
316  // For DIBsections, each scan line is DWORD aligned, padded on the
317  // right
318  line_pitch_bytes = (((m_width * 24) + 31) & ~31) >> 3;
319 
320 #endif
321 
322 #ifdef __PIX_CACHE_WXIMAGE__
323  m_pimage = new wxImage(m_width, m_height, (bool)FALSE);
324  pData = m_pimage->GetData();
325 #endif
326 
327 #ifdef __PIX_CACHE_X11IMAGE__
328  m_pocpnXI = new ocpnXImage(width, height);
329  pData = (unsigned char *)m_pocpnXI->m_img->data;
330 #endif //__PIX_CACHE_X11IMAGE__
331 
332 #ifdef __PIX_CACHE_PIXBUF__
333  // m_pbm = new ocpnBitmap((unsigned char *)NULL, m_width, m_height,
334  // m_depth);
335 
340 
344 
345  pData = (unsigned char *)malloc(m_width * m_height * 4);
348 #endif
349 }
350 
351 PixelCache::~PixelCache() {
352 #ifdef __PIX_CACHE_WXIMAGE__
353  delete m_pimage;
354  delete m_pbm;
355 #endif
356 
357 #ifdef __PIX_CACHE_DIBSECTION__
358  delete m_pDS;
359 #endif
360 
361 #ifdef __PIX_CACHE_X11IMAGE__
362  delete m_pbm;
363  delete m_pocpnXI;
364 #endif
365 
366 #ifdef __PIX_CACHE_PIXBUF__
367  free(pData);
368  delete m_pbm;
369 #endif
370 }
371 
372 size_t PixelCache::GetLength(void) {
373 #ifdef __PIX_CACHE_WXIMAGE__
374  return m_width * m_height * 3;
375 #else
376  return 0;
377 #endif
378 }
379 
380 void PixelCache::Update(void) {
381 #ifdef __PIX_CACHE_WXIMAGE__
382  delete m_pbm; // kill the old one
383  m_pbm = NULL;
384 #endif
385 }
386 
387 void PixelCache::SelectIntoDC(wxMemoryDC &dc) {
388 #ifdef __PIX_CACHE_DIBSECTION__
389  ocpnMemDC *pmdc = dynamic_cast<ocpnMemDC *>(&dc);
390  pmdc->SelectObject(*m_pDS);
391 
392 #endif //__PIX_CACHE_DIBSECTION__
393 
394 #ifdef __PIX_CACHE_WXIMAGE__
395  // delete m_pbm; // kill the old one
396 
397  // Convert image to bitmap
398 #ifdef ocpnUSE_ocpnBitmap
399  if (!m_pbm) m_pbm = new ocpnBitmap(*m_pimage, m_depth);
400 #else
401  if (!m_pbm) m_pbm = new wxBitmap(*m_pimage, -1);
402 #endif
403 
404  if (m_pbm) dc.SelectObject(*m_pbm);
405 #endif // __PIX_CACHE_WXIMAGE__
406 
407 #ifdef __PIX_CACHE_X11IMAGE__
408  if (!m_pbm) m_pbm = new ocpnBitmap(m_pocpnXI, m_width, m_height, m_depth);
409  dc.SelectObject(*m_pbm);
410 #endif //__PIX_CACHE_X11IMAGE__
411 
412 #ifdef __PIX_CACHE_PIXBUF__
413  if (!m_pbm) m_pbm = new ocpnBitmap(pData, m_width, m_height, m_depth);
414  if (m_pbm) {
415  dc.SelectObject(*m_pbm);
416  }
417 #endif //__PIX_CACHE_PIXBUF__
418 }
419 
420 unsigned char *PixelCache::GetpData(void) const { return pData; }
421 
422 #ifdef ocpnUSE_ocpnBitmap
423 //-----------------------------------------------------------------------------
424 /*
425  Class: ocpnBitmap
426 
427  Derived from wxBitmap
428 
429  Why?....
430  wxWidgets does a very correct, but sometimes slow job of bitmap creation
431  and copying. ocpnBitmap is an optimization of wxBitmap for specific
432  platforms and color formats.
433 
434  ocpn_Bitmap is optimized specifically for Windows and X11 Linux/Unix
435  systems, taking advantage of some known underlying data structures and
436  formats.
437 
438  There is (currently) no optimization for for other platforms,
439  such as GTK or MAC
440 
441  The included methods are very different for MSW and X11
442  See the Code
443 
444  */
445 
446 // ----------------------------------------------------------------------------
447 // macros
448 // ----------------------------------------------------------------------------
449 #define M_BMPDATA wx_static_cast(wxBitmapRefData *, m_refData)
450 
451 IMPLEMENT_DYNAMIC_CLASS(ocpnBitmap, wxBitmap /*wxGDIObject*/)
452 
453 // class wxBitmapRefData;
454 
455 // ============================================================================
456 // implementation
457 // ============================================================================
458 
459 ocpnBitmap::ocpnBitmap() {}
460 
461 // ocpnBitmap::~ocpnBitmap()
462 //{
463 //}
464 
466 #ifdef __WXGTK__
467 #ifdef opcnUSE_GTK_OPTIMIZE
468 
469 // ----------------------------------------------------------------------------
470 // Create from Data
471 // ----------------------------------------------------------------------------
472 
473 bool ocpnBitmap::CreateFromData(void *pPix, int width, int height, int depth) {
474  /*
475  XImage *img = NULL;
476 
477 // Do some basic setup in the parent wxBitmap class
478  Create(width, height, -1);
479 
480  Display *xdisplay = (Display *)GetDisplay();
481 
482  ocpnXImage *pXI = new ocpnXImage(width, height);
483  img = pXI->m_img;
484 
485 // Faster render from a 24 or 32 bit pixel buffer
486 
487  if((pPix != NULL ) && (NULL != img))
488  {
489  unsigned char* data = (unsigned char *)pPix;
490  if(depth == 32) // special fast case
491  {
492  for (int y = 0; y < height; y++)
493  {
494  char *pd = img->data + (y * img->bytes_per_line);
495  unsigned char *ps = data + (y * width * 4);
496  memcpy(pd, ps, width*4);
497  }
498  }
499 
500  else
501  {
502  int *pi = (int *)img->data;
503  int index = 0;
504  for (int y = 0; y < height; y++)
505  {
506  for (int x = 0; x < width; x++)
507  {
508  int ri = *(int *)(&data[index]);
509  index++;
510  index++;
511  index++;
512 
513  *pi = ri;
514  pi++;
515 
516  }
517  }
518  }
519  }
520 
521  // Blit picture
522 
523  Pixmap mypixmap = ((Pixmap )GetPixmap());
524  GC gc = XCreateGC( xdisplay, mypixmap, 0, NULL );
525 
526  pXI->PutImage(mypixmap, gc);
527 
528  delete pXI;
529 
530  XFreeGC( xdisplay, gc );
531  */
532 
534  Create(width, height, 32);
535 
536  // GdkPixbuf* pixbuf = wx_static_cast(wxBitmapRefData*,
537  // m_refData))->m_pixbuf;
538  // GdkPixbuf* pixbuf = M_BMPDATA->m_pixbuf;
539 
540  // Copy the data:
541  if (NULL != pPix) {
542  GdkPixbuf *pixbuf = GetPixbuf();
543 
544  if (!pixbuf) return false;
545 
546  unsigned char *in = (unsigned char *)pPix;
547  unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
548 
549  int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
550 
551  for (int y = 0; y < height; y++, out += rowpad) {
555 
556  for (int x = 0; x < width; x++, out += 4, in += 4) {
557  out[0] = in[0];
558  out[1] = in[1];
559  out[2] = in[2];
560  out[3] = in[3];
561  }
562  }
563  }
564  return true;
565 }
566 
567 /*
568 bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
569 {
570  wxASSERT(image.HasAlpha());
571 
572  int width = image.GetWidth();
573  int height = image.GetHeight();
574 
575  Create(width, height, 32);
576  GdkPixbuf* pixbuf = M_BMPDATA->m_pixbuf;
577  if (!pixbuf)
578  return false;
579 
580  // Copy the data:
581  const unsigned char* in = image.GetData();
582  unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
583  unsigned char *alpha = image.GetAlpha();
584 
585  int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
586 
587  for (int y = 0; y < height; y++, out += rowpad)
588  {
589  for (int x = 0; x < width; x++, alpha++, out += 4, in += 3)
590  {
591  out[0] = in[0];
592  out[1] = in[1];
593  out[2] = in[2];
594  out[3] = *alpha;
595  }
596  }
597 
598  return true;
599 }
600 */
601 #endif // opcnUSE_GTK_OPTIMIZE
602 #endif
603 
604 #ifdef __WXX11__
605 // This is the X11 Version
606 
607 // ----------------------------------------------------------------------------
608 // Create from ocpnXImage
609 // ----------------------------------------------------------------------------
610 
611 bool ocpnBitmap::CreateFromocpnXImage(ocpnXImage *poXI, int width, int height,
612  int depth) {
613  // Do some basic setup in the parent wxBitmap class
614  Create(width, height, -1);
615 
616  Display *xdisplay = (Display *)GetDisplay();
617 
618  XImage *data_image = poXI->m_img;
619  bool bShared = poXI->buse_mit;
620 
621  // Blit picture
622 
623  Pixmap mypixmap = ((Pixmap)GetPixmap());
624 
625  GC gc = XCreateGC(xdisplay, mypixmap, 0, NULL);
626 
627  if (bShared)
628  XShmPutImage(xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height,
629  False);
630  else
631  XPutImage(xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height);
632 
633  XFreeGC(xdisplay, gc);
634 
635  return true;
636 }
637 
638 // ----------------------------------------------------------------------------
639 // Create from Data
640 // ----------------------------------------------------------------------------
641 
642 bool ocpnBitmap::CreateFromData(void *pPix, int width, int height, int depth) {
643  XImage *img = NULL;
644 
645  // Do some basic setup in the parent wxBitmap class
646  Create(width, height, -1);
647 
648  Display *xdisplay = (Display *)GetDisplay();
649 
650  ocpnXImage *pXI = new ocpnXImage(width, height);
651  img = pXI->m_img;
652 
653  // Faster render from a 24 or 32 bit pixel buffer
654 
655  if ((pPix != NULL) && (NULL != img)) {
656  unsigned char *data = (unsigned char *)pPix;
657  if (depth == 32) // special fast case
658  {
659  for (int y = 0; y < height; y++) {
660  char *pd = img->data + (y * img->bytes_per_line);
661  unsigned char *ps = data + (y * width * 4);
662  memcpy(pd, ps, width * 4);
663  }
664  }
665 
666  else {
667  int *pi = (int *)img->data;
668  int index = 0;
669  for (int y = 0; y < height; y++) {
670  for (int x = 0; x < width; x++) {
671  int ri = *(int *)(&data[index]);
672  index++;
673  index++;
674  index++;
675 
676  *pi = ri;
677  pi++;
678  }
679  }
680  }
681  }
682 
683  // Blit picture
684 
685  Pixmap mypixmap = ((Pixmap)GetPixmap());
686  GC gc = XCreateGC(xdisplay, mypixmap, 0, NULL);
687 
688  pXI->PutImage(mypixmap, gc);
689 
690  delete pXI;
691 
692  XFreeGC(xdisplay, gc);
693 
694  return TRUE;
695 }
696 
697 #endif //__WXX11__
698 
699 #ifdef __WXMSW__
700 
701 // ----------------------------------------------------------------------------
702 // Create from Data
703 // ----------------------------------------------------------------------------
704 bool ocpnBitmap::CreateFromData(void *pPix, int width, int height, int depth) {
705  m_refData = CreateData(); // found in wxBitmap
706  // int width = image.GetWidth();
707  // int height0 = image.GetHeight();
708 
709  int height0 = height;
710  int sizeLimit = 1280 * 1024 * 3;
711 
712  int bmpHeight = height0;
713  // int height = bmpHeight;
714 
715  // calc the number of bytes per scanline and padding
716  int bytePerLine = width * 3;
717  int sizeDWORD = sizeof(DWORD);
718  int lineBoundary = bytePerLine % sizeDWORD;
719  int padding = 0;
720  if (lineBoundary > 0) {
721  padding = sizeDWORD - lineBoundary;
722  bytePerLine += padding;
723  }
724 
725  // set bitmap parameters
726  SetWidth(width);
727  SetHeight(bmpHeight);
728  if (depth == -1) depth = wxDisplayDepth();
729  SetDepth(depth);
730 
731  // create a DIB header
732  int headersize = sizeof(BITMAPINFOHEADER);
733  BITMAPINFO *lpDIBh = (BITMAPINFO *)malloc(headersize);
734  wxCHECK_MSG(lpDIBh, FALSE, wxT("could not allocate memory for DIB header"));
735  // Fill in the DIB header
736  lpDIBh->bmiHeader.biSize = headersize;
737  lpDIBh->bmiHeader.biWidth = (DWORD)width;
738  lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
739  lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
740  // the general formula for biSizeImage:
741  // ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
742  lpDIBh->bmiHeader.biPlanes = 1;
743  lpDIBh->bmiHeader.biBitCount = 24;
744  lpDIBh->bmiHeader.biCompression = BI_RGB;
745  lpDIBh->bmiHeader.biClrUsed = 0;
746  // These seem not really needed for our purpose here.
747  lpDIBh->bmiHeader.biClrImportant = 0;
748  lpDIBh->bmiHeader.biXPelsPerMeter = 0;
749  lpDIBh->bmiHeader.biYPelsPerMeter = 0;
750  // memory for DIB data
751  unsigned char *lpBits;
752  lpBits = (unsigned char *)malloc(lpDIBh->bmiHeader.biSizeImage);
753  if (!lpBits) {
754  wxFAIL_MSG(wxT("could not allocate memory for DIB"));
755  free(lpDIBh);
756  return FALSE;
757  }
758 
759  // create and set the device-dependent bitmap
760  HDC hdc = ::GetDC(NULL);
761  HDC memdc = ::CreateCompatibleDC(hdc);
762  HBITMAP hbitmap;
763  // hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );
764 
765  // if(hbitmap == NULL)
766  // int cop =0;
767 
768  // ::SelectObject( memdc, hbitmap);
769 
770  // copy image data into DIB data
771  unsigned char *data = (unsigned char *)pPix;
772  int i, j;
773  unsigned char *ptdata = data;
774  unsigned char *ptbits;
775 
776  ptbits = lpBits;
777 
778  if (pPix) {
779  for (j = 0; j < height; j++) {
780  memcpy(ptbits, ptdata, width * 3);
781  ptbits += width * 3;
782  ptdata += width * 3;
783 
784  for (i = 0; i < padding; i++) *(ptbits++) = 0;
785  }
786  }
787 
788  else {
789  for (j = 0; j < height; j++) {
790  memset(ptbits, 0, width * 3);
791  ptbits += width * 3;
792 
793  for (i = 0; i < padding; i++) *(ptbits++) = 0;
794  }
795  }
796 
797  hbitmap = CreateDIBitmap(hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh,
798  DIB_RGB_COLORS);
799  // The above line is equivalent to the following two lines.
800  // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
801  // ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
802  // or the following lines
803  // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
804  // HDC memdc = ::CreateCompatibleDC( hdc );
805  // ::SelectObject( memdc, hbitmap);
806  // ::SetDIBitsToDevice( memdc, 0, 0, width, height,
807  // 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
808  // ::SelectObject( memdc, 0 );
809  // ::DeleteDC( memdc );
810  SetHBITMAP((WXHBITMAP)hbitmap);
811 
812  // free allocated resources
813  ::DeleteDC(memdc);
814  ::ReleaseDC(NULL, hdc);
815  free(lpDIBh);
816  free(lpBits);
817 
818  return TRUE;
819 }
820 
821 // ----------------------------------------------------------------------------
822 // wxImage from conversion
823 // ----------------------------------------------------------------------------
824 
825 bool ocpnBitmap::CreateFromImage(const wxImage &image, int depth) {
826  // wxCHECK_MSG( image.Ok(), FALSE, wxT("invalid image") )
827 
828  m_refData = CreateData(); // found in wxBitmap
829 
830  // sizeLimit is the MS upper limit for the DIB size
831  int sizeLimit = 1280 * 1024 * 3;
832 
833  // width and height of the device-dependent bitmap
834  int width = image.GetWidth();
835  int bmpHeight = image.GetHeight();
836 
837  // calc the number of bytes per scanline and padding
838  int bytePerLine = width * 3;
839  int sizeDWORD = sizeof(DWORD);
840  int lineBoundary = bytePerLine % sizeDWORD;
841  int padding = 0;
842  if (lineBoundary > 0) {
843  padding = sizeDWORD - lineBoundary;
844  bytePerLine += padding;
845  }
846  // calc the number of DIBs and heights of DIBs
847  int numDIB = 1;
848  int hRemain = 0;
849  int height = sizeLimit / bytePerLine;
850  if (height >= bmpHeight)
851  height = bmpHeight;
852  else {
853  numDIB = bmpHeight / height;
854  hRemain = bmpHeight % height;
855  if (hRemain > 0) numDIB++;
856  }
857 
858  // set bitmap parameters
859  wxCHECK_MSG(image.Ok(), FALSE, wxT("invalid image"));
860  SetWidth(width);
861  SetHeight(bmpHeight);
862  if (depth == -1) depth = wxDisplayDepth();
863  SetDepth(depth);
864 
865 #if wxUSE_PALETTE
866  // Copy the palette from the source image
867  SetPalette(image.GetPalette());
868 #endif // wxUSE_PALETTE
869 
870  // create a DIB header
871  int headersize = sizeof(BITMAPINFOHEADER);
872  BITMAPINFO *lpDIBh = (BITMAPINFO *)malloc(headersize);
873  wxCHECK_MSG(lpDIBh, FALSE, wxT("could not allocate memory for DIB header"));
874  // Fill in the DIB header
875  lpDIBh->bmiHeader.biSize = headersize;
876  lpDIBh->bmiHeader.biWidth = (DWORD)width;
877  lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
878  lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
879  // the general formula for biSizeImage:
880  // ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
881  lpDIBh->bmiHeader.biPlanes = 1;
882  lpDIBh->bmiHeader.biBitCount = 24;
883  lpDIBh->bmiHeader.biCompression = BI_RGB;
884  lpDIBh->bmiHeader.biClrUsed = 0;
885  // These seem not really needed for our purpose here.
886  lpDIBh->bmiHeader.biClrImportant = 0;
887  lpDIBh->bmiHeader.biXPelsPerMeter = 0;
888  lpDIBh->bmiHeader.biYPelsPerMeter = 0;
889  // memory for DIB data
890  unsigned char *lpBits;
891  lpBits = (unsigned char *)malloc(lpDIBh->bmiHeader.biSizeImage);
892  if (!lpBits) {
893  wxFAIL_MSG(wxT("could not allocate memory for DIB"));
894  free(lpDIBh);
895  return FALSE;
896  }
897 
898  // create and set the device-dependent bitmap
899  HDC hdc = ::GetDC(NULL);
900  HDC memdc = ::CreateCompatibleDC(hdc);
901  HBITMAP hbitmap;
902  // hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );
903 
904  // if(hbitmap == NULL)
905  // int cop =0;
906 
907  // ::SelectObject( memdc, hbitmap);
908 
909 #if wxUSE_PALETTE
910  HPALETTE hOldPalette = 0;
911  if (image.GetPalette().Ok()) {
912  hOldPalette = ::SelectPalette(
913  memdc, (HPALETTE)image.GetPalette().GetHPALETTE(), FALSE);
914  ::RealizePalette(memdc);
915  }
916 #endif // wxUSE_PALETTE
917 
918  // copy image data into DIB data and then into DDB (in a loop)
919  unsigned char *data = image.GetData();
920  int i, j, n;
921  int origin = 0;
922  unsigned char *ptdata = data;
923  unsigned char *ptbits;
924 
925  for (n = 0; n < numDIB; n++) {
926  if (numDIB > 1 && n == numDIB - 1 && hRemain > 0) {
927  // redefine height and size of the (possibly) last smaller DIB
928  // memory is not reallocated
929  height = hRemain;
930  lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
931  lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
932  }
933  ptbits = lpBits;
934 
935  for (j = 0; j < height; j++) {
936  memcpy(ptbits, ptdata, width * 3);
937  ptbits += width * 3;
938  ptdata += width * 3;
939 
940  /*
941  for( i=0; i<width; i++ )
942  {
943  *(ptbits++) = *(ptdata+2);
944  *(ptbits++) = *(ptdata+1);
945  *(ptbits++) = *(ptdata );
946  ptdata += 3;
947  }
948  */
949  for (i = 0; i < padding; i++) *(ptbits++) = 0;
950  }
951  // ::StretchDIBits( memdc, 0, origin, width, height,
952  // 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
953  origin += height;
954  // if numDIB = 1, lines below can also be used
955  hbitmap = CreateDIBitmap(hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits,
956  lpDIBh, DIB_RGB_COLORS);
957  // if(hbitmap == NULL)
958  // int cop =0;
959 
960  // The above line is equivalent to the following two lines.
961  // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
962  // ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
963  // or the following lines
964  // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
965  // HDC memdc = ::CreateCompatibleDC( hdc );
966  // ::SelectObject( memdc, hbitmap);
967  // ::SetDIBitsToDevice( memdc, 0, 0, width, height,
968  // 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
969  // ::SelectObject( memdc, 0 );
970  // ::DeleteDC( memdc );
971  }
972  SetHBITMAP((WXHBITMAP)hbitmap);
973 
974  // if(!this->Ok())
975  // int cop = this->Ok();
976 
977 #if wxUSE_PALETTE
978  if (hOldPalette) SelectPalette(memdc, hOldPalette, FALSE);
979 #endif // wxUSE_PALETTE
980 
981  // similarly, created an mono-bitmap for the possible mask
982  if (image.HasMask()) {
983  hbitmap = ::CreateBitmap((WORD)width, (WORD)bmpHeight, 1, 1, NULL);
984  HGDIOBJ hbmpOld = ::SelectObject(memdc, hbitmap);
985  if (numDIB == 1)
986  height = bmpHeight;
987  else
988  height = sizeLimit / bytePerLine;
989  lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
990  lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
991  origin = 0;
992  unsigned char r = image.GetMaskRed();
993  unsigned char g = image.GetMaskGreen();
994  unsigned char b = image.GetMaskBlue();
995  unsigned char zero = 0, one = 255;
996  ptdata = data;
997  for (n = 0; n < numDIB; n++) {
998  if (numDIB > 1 && n == numDIB - 1 && hRemain > 0) {
999  // redefine height and size of the (possibly) last smaller DIB
1000  // memory is not reallocated
1001  height = hRemain;
1002  lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
1003  lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
1004  }
1005  ptbits = lpBits;
1006  for (int j = 0; j < height; j++) {
1007  for (i = 0; i < width; i++) {
1008  // was causing a code gen bug in cw : if( ( cr !=r) || (cg!=g) ||
1009  // (cb!=b) )
1010  unsigned char cr = (*(ptdata++));
1011  unsigned char cg = (*(ptdata++));
1012  unsigned char cb = (*(ptdata++));
1013 
1014  if ((cr != r) || (cg != g) || (cb != b)) {
1015  *(ptbits++) = one;
1016  *(ptbits++) = one;
1017  *(ptbits++) = one;
1018  } else {
1019  *(ptbits++) = zero;
1020  *(ptbits++) = zero;
1021  *(ptbits++) = zero;
1022  }
1023  }
1024  for (i = 0; i < padding; i++) *(ptbits++) = zero;
1025  }
1026  ::StretchDIBits(memdc, 0, origin, width, height, 0, 0, width, height,
1027  lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
1028  origin += height;
1029  }
1030  // create a wxMask object
1031  wxMask *mask = new wxMask();
1032  mask->SetMaskBitmap((WXHBITMAP)hbitmap);
1033  SetMask(mask);
1034  // It will be deleted when the wxBitmap object is deleted (as of 01/1999)
1035  /* The following can also be used but is slow to run
1036  wxColour colour( GetMaskRed(), GetMaskGreen(), GetMaskBlue());
1037  wxMask *mask = new wxMask( *this, colour );
1038  SetMask( mask );
1039  */
1040 
1041  ::SelectObject(memdc, hbmpOld);
1042  }
1043 
1044  // free allocated resources
1045  ::DeleteDC(memdc);
1046  ::ReleaseDC(NULL, hdc);
1047  free(lpDIBh);
1048  free(lpBits);
1049 
1050 #if WXWIN_COMPATIBILITY_2
1051  // check the wxBitmap object
1052  GetBitmapData()->SetOk();
1053 #endif // WXWIN_COMPATIBILITY_2
1054 
1055  return TRUE;
1056 }
1057 
1058 #endif // __WXMSW__
1059 
1060 #endif // ocpnUSE_ocpnBitmap
1061 
1062 // ============================================================================
1063 // ocpnMemDC implementation
1064 // ============================================================================
1065 
1066 IMPLEMENT_DYNAMIC_CLASS(ocpnMemDC, wxMemoryDC)
1067 
1068 ocpnMemDC::ocpnMemDC() {}
1069 
1070 #ifdef ocpnUSE_DIBSECTION
1071 void ocpnMemDC::SelectObject(wxDIB &dib) {
1072  // select old bitmap out of the device context
1073  if (m_oldBitmap) {
1074  ::SelectObject(GetHdc(), (HBITMAP)m_oldBitmap);
1075  if (m_selectedBitmap.Ok()) {
1076 #ifdef __WXDEBUG__
1077  // m_selectedBitmap.SetSelectedInto(NULL);
1078 #endif
1079  m_selectedBitmap = wxNullBitmap;
1080  }
1081  }
1082 
1083  // check for whether the bitmap is already selected into a device context
1084  // wxASSERT_MSG( !bitmap.GetSelectedInto() ||
1085  // (bitmap.GetSelectedInto() == this),
1086  // wxT("Bitmap is selected in another wxMemoryDC, delete the
1087  // first wxMemoryDC or use SelectObject(NULL)") );
1088 
1089  /*
1090  m_selectedBitmap = bitmap;
1091  WXHBITMAP hBmp = m_selectedBitmap.GetHBITMAP();
1092  if ( !hBmp )
1093  return; // already selected
1094  */
1095  m_pselectedDIB = &dib;
1096  HBITMAP hDIB = m_pselectedDIB->GetHandle();
1097  if (!hDIB) return; // already selected
1098 
1099 #ifdef __WXDEBUG__
1100 // m_selectedBitmap.SetSelectedInto(this);
1101 #endif
1102 
1103  // hBmp = (WXHBITMAP)::SelectObject(GetHdc(), (HBITMAP)hBmp);
1104 
1105  hDIB = (HBITMAP)::SelectObject(GetHdc(), hDIB);
1106 
1107  if (!hDIB) {
1108  wxLogLastError(wxT("SelectObject(ocpnMemDC, DIB)"));
1109 
1110  wxFAIL_MSG(wxT("Couldn't select a DIB into ocpnMemDC"));
1111  }
1112 
1113  else if (!m_oldBitmap) {
1114  m_oldBitmap = hDIB;
1115  }
1116 }
1117 
1118 #endif // ocpnUSE_DIBSECTION
1119 
1120 /*
1121  * Rotation code by Carlos Moreno
1122  * Adapted to static and modified for improved performance by dsr
1123  */
1124 
1125 static const double wxROTATE_EPSILON = 1e-10;
1126 
1127 // Auxiliary function to rotate a point (x,y) with respect to point p0
1128 // make it inline and use a straight return to facilitate optimization
1129 // also, the function receives the sine and cosine of the angle to avoid
1130 // repeating the time-consuming calls to these functions -- sin/cos can
1131 // be computed and stored in the calling function.
1132 
1133 static inline wxRealPoint wxRotatePoint(const wxRealPoint &p, double cos_angle,
1134  double sin_angle,
1135  const wxRealPoint &p0) {
1136  return wxRealPoint(
1137  p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
1138  p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
1139 }
1140 
1141 static inline wxRealPoint wxRotatePoint(double x, double y, double cos_angle,
1142  double sin_angle,
1143  const wxRealPoint &p0) {
1144  return wxRotatePoint(wxRealPoint(x, y), cos_angle, sin_angle, p0);
1145 }
1146 
1147 wxImage Image_Rotate(wxImage &base_image, double angle,
1148  const wxPoint &centre_of_rotation, bool interpolating,
1149  wxPoint *offset_after_rotation) {
1150  int i;
1151  angle =
1152  -angle; // screen coordinates are a mirror image of "real" coordinates
1153 
1154  bool has_alpha = base_image.HasAlpha();
1155 
1156  const int w = base_image.GetWidth(), h = base_image.GetHeight();
1157 
1158  // Create pointer-based array to accelerate access to wxImage's data
1159  unsigned char **data = new unsigned char *[h];
1160  data[0] = base_image.GetData();
1161  for (i = 1; i < h; i++) data[i] = data[i - 1] + (3 * w);
1162 
1163  // Same for alpha channel
1164  unsigned char **alpha = NULL;
1165  if (has_alpha) {
1166  alpha = new unsigned char *[h];
1167  alpha[0] = base_image.GetAlpha();
1168  for (i = 1; i < h; i++) alpha[i] = alpha[i - 1] + w;
1169  }
1170 
1171  // precompute coefficients for rotation formula
1172  // (sine and cosine of the angle)
1173  const double cos_angle = cos(angle);
1174  const double sin_angle = sin(angle);
1175 
1176  // Create new Image to store the result
1177  // First, find rectangle that covers the rotated image; to do that,
1178  // rotate the four corners
1179 
1180  const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
1181 
1182  wxRealPoint p1 = wxRotatePoint(0, 0, cos_angle, sin_angle, p0);
1183  wxRealPoint p2 = wxRotatePoint(0, h, cos_angle, sin_angle, p0);
1184  wxRealPoint p3 = wxRotatePoint(w, 0, cos_angle, sin_angle, p0);
1185  wxRealPoint p4 = wxRotatePoint(w, h, cos_angle, sin_angle, p0);
1186 
1187  int x1a = (int)floor(wxMin(wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
1188  int y1a = (int)floor(wxMin(wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
1189  int x2a = (int)ceil(wxMax(wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
1190  int y2a = (int)ceil(wxMax(wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));
1191 
1192  // Create rotated image
1193  wxImage rotated(x2a - x1a + 1, y2a - y1a + 1, false);
1194  // With alpha channel
1195  if (has_alpha) rotated.SetAlpha();
1196 
1197  if (offset_after_rotation != NULL) {
1198  *offset_after_rotation = wxPoint(x1a, y1a);
1199  }
1200 
1201  // GRG: The rotated (destination) image is always accessed
1202  // sequentially, so there is no need for a pointer-based
1203  // array here (and in fact it would be slower).
1204  //
1205  unsigned char *dst = rotated.GetData();
1206 
1207  unsigned char *alpha_dst = NULL;
1208  if (has_alpha) alpha_dst = rotated.GetAlpha();
1209 
1210  // GRG: if the original image has a mask, use its RGB values
1211  // as the blank pixel, else, fall back to default (black).
1212  //
1213  unsigned char blank_r = 0;
1214  unsigned char blank_g = 0;
1215  unsigned char blank_b = 0;
1216 
1217  if (base_image.HasMask()) {
1218  blank_r = base_image.GetMaskRed();
1219  blank_g = base_image.GetMaskGreen();
1220  blank_b = base_image.GetMaskBlue();
1221  rotated.SetMaskColour(blank_r, blank_g, blank_b);
1222  }
1223 
1224  // Now, for each point of the rotated image, find where it came from, by
1225  // performing an inverse rotation (a rotation of -angle) and getting the
1226  // pixel at those coordinates
1227 
1228  const int rH = rotated.GetHeight();
1229  const int rW = rotated.GetWidth();
1230 
1231  // GRG: I've taken the (interpolating) test out of the loops, so that
1232  // it is done only once, instead of repeating it for each pixel.
1233 
1234  if (interpolating) {
1235  for (int y = 0; y < rH; y++) {
1236  for (int x = 0; x < rW; x++) {
1237  wxRealPoint src =
1238  wxRotatePoint(x + x1a, y + y1a, cos_angle, -sin_angle, p0);
1239 
1240  if (-0.25 < src.x && src.x < w - 0.75 && -0.25 < src.y &&
1241  src.y < h - 0.75) {
1242  // interpolate using the 4 enclosing grid-points. Those
1243  // points can be obtained using floor and ceiling of the
1244  // exact coordinates of the point
1245  int x1, y1, x2, y2;
1246 
1247  if (0 < src.x && src.x < w - 1) {
1248  x1 = wxRound(floor(src.x));
1249  x2 = wxRound(ceil(src.x));
1250  } else // else means that x is near one of the borders (0 or width-1)
1251  {
1252  x1 = x2 = wxRound(src.x);
1253  }
1254 
1255  if (0 < src.y && src.y < h - 1) {
1256  y1 = wxRound(floor(src.y));
1257  y2 = wxRound(ceil(src.y));
1258  } else {
1259  y1 = y2 = wxRound(src.y);
1260  }
1261 
1262  // get four points and the distances (square of the distance,
1263  // for efficiency reasons) for the interpolation formula
1264 
1265  // GRG: Do not calculate the points until they are
1266  // really needed -- this way we can calculate
1267  // just one, instead of four, if d1, d2, d3
1268  // or d4 are < wxROTATE_EPSILON
1269 
1270  const double d1 =
1271  (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
1272  const double d2 =
1273  (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
1274  const double d3 =
1275  (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
1276  const double d4 =
1277  (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
1278 
1279  // Now interpolate as a weighted average of the four surrounding
1280  // points, where the weights are the distances to each of those points
1281 
1282  // If the point is exactly at one point of the grid of the source
1283  // image, then don't interpolate -- just assign the pixel
1284 
1285  // d1,d2,d3,d4 are positive -- no need for abs()
1286  if (d1 < wxROTATE_EPSILON) {
1287  unsigned char *p = data[y1] + (3 * x1);
1288  *(dst++) = *(p++);
1289  *(dst++) = *(p++);
1290  *(dst++) = *p;
1291 
1292  if (has_alpha) *(alpha_dst++) = *(alpha[y1] + x1);
1293  } else if (d2 < wxROTATE_EPSILON) {
1294  unsigned char *p = data[y1] + (3 * x2);
1295  *(dst++) = *(p++);
1296  *(dst++) = *(p++);
1297  *(dst++) = *p;
1298 
1299  if (has_alpha) *(alpha_dst++) = *(alpha[y1] + x2);
1300  } else if (d3 < wxROTATE_EPSILON) {
1301  unsigned char *p = data[y2] + (3 * x2);
1302  *(dst++) = *(p++);
1303  *(dst++) = *(p++);
1304  *(dst++) = *p;
1305 
1306  if (has_alpha) *(alpha_dst++) = *(alpha[y2] + x2);
1307  } else if (d4 < wxROTATE_EPSILON) {
1308  unsigned char *p = data[y2] + (3 * x1);
1309  *(dst++) = *(p++);
1310  *(dst++) = *(p++);
1311  *(dst++) = *p;
1312 
1313  if (has_alpha) *(alpha_dst++) = *(alpha[y2] + x1);
1314  } else {
1315  // weights for the weighted average are proportional to the inverse
1316  // of the distance
1317  unsigned char *v1 = data[y1] + (3 * x1);
1318  unsigned char *v2 = data[y1] + (3 * x2);
1319  unsigned char *v3 = data[y2] + (3 * x2);
1320  unsigned char *v4 = data[y2] + (3 * x1);
1321 
1322  const double w1 = 1 / d1, w2 = 1 / d2, w3 = 1 / d3, w4 = 1 / d4;
1323 
1324  // GRG: Unrolled.
1325 
1326  *(dst++) = (unsigned char)((w1 * *(v1++) + w2 * *(v2++) +
1327  w3 * *(v3++) + w4 * *(v4++)) /
1328  (w1 + w2 + w3 + w4));
1329  *(dst++) = (unsigned char)((w1 * *(v1++) + w2 * *(v2++) +
1330  w3 * *(v3++) + w4 * *(v4++)) /
1331  (w1 + w2 + w3 + w4));
1332  *(dst++) =
1333  (unsigned char)((w1 * *v1 + w2 * *v2 + w3 * *v3 + w4 * *v4) /
1334  (w1 + w2 + w3 + w4));
1335 
1336  if (has_alpha) {
1337  v1 = alpha[y1] + (x1);
1338  v2 = alpha[y1] + (x2);
1339  v3 = alpha[y2] + (x2);
1340  v4 = alpha[y2] + (x1);
1341 
1342  *(alpha_dst++) =
1343  (unsigned char)((w1 * *v1 + w2 * *v2 + w3 * *v3 + w4 * *v4) /
1344  (w1 + w2 + w3 + w4));
1345  }
1346  }
1347  } else {
1348  *(dst++) = blank_r;
1349  *(dst++) = blank_g;
1350  *(dst++) = blank_b;
1351 
1352  if (has_alpha) *(alpha_dst++) = 0;
1353  }
1354  }
1355  }
1356  } else // not interpolating
1357  {
1358  double x0 = p0.x;
1359  double y0 = p0.y;
1360  double x1b = x1a - p0.x;
1361  double y1b = y1a - p0.y;
1362  double msa = -sin_angle;
1363 
1364  for (int y = 0; y < rH; y++) {
1365  for (int x = 0; x < rW; x++) {
1366  // wxRealPoint src =
1367  // wxRotatePoint (x + x1a,
1368  // y + y1a, cos_angle,
1369  // -sin_angle, p0);
1370 
1371  // double sx = p0.x + (x +
1372  // x1a - p0.x) * cos_angle
1373  // - (y + y1a - p0.y) *
1374  // -sin_angle; double sy=
1375  // p0.y + (y + y1a - p0.y)
1376  // * cos_angle + (x + x1a
1377  // - p0.x) * -sin_angle;
1378 
1379  double sx = x0 + (x + x1b) * cos_angle - (y + y1b) * msa;
1380  double sy = y0 + (y + y1b) * cos_angle + (x + x1b) * msa;
1381 
1382  const int xs = (int)sx;
1383  const int ys = (int)sy;
1384 
1385  // return wxRealPoint(p0.x
1386  // + (p.x - p0.x) *
1387  // cos_angle - (p.y -
1388  // p0.y) * sin_angle,
1389  // p0.y + (p.y
1390  // - p0.y) *
1391  // cos_angle +
1392  // (p.x -
1393  // p0.x) *
1394  // sin_angle);
1395 
1396  // const int xs =
1397  // /*wxRound*/ (src.x); //
1398  // wxRound rounds to the
1399  // const int ys =
1400  // /*wxRound*/ (src.y); //
1401  // closest integer
1402 
1403  if (0 <= xs && xs < w && 0 <= ys && ys < h) {
1404  unsigned char *p = data[ys] + (3 * xs);
1405  *(dst++) = *(p++);
1406  *(dst++) = *(p++);
1407  *(dst++) = *p;
1408 
1409  if (has_alpha) *(alpha_dst++) = *(alpha[ys] + (xs));
1410  } else {
1411  *(dst++) = blank_r;
1412  *(dst++) = blank_g;
1413  *(dst++) = blank_b;
1414 
1415  if (has_alpha) *(alpha_dst++) = 255;
1416  }
1417  }
1418  }
1419  }
1420 
1421  delete[] data;
1422 
1423  if (has_alpha) delete[] alpha;
1424 
1425  return rotated;
1426 }