OpenCPN Partial API docs
Loading...
Searching...
No Matches
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 */
106static int MITErrorFlag = 0;
107static int HandleXError(Display *dpy, XErrorEvent *event) {
108 MITErrorFlag = 1;
109 return 0;
110}
111#endif
112
113//---------------------------------------------------------------------------------------------------------
114// Private Memory Management
115//---------------------------------------------------------------------------------------------------------
116
117static 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
166ocpnXImage::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
237after_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
254ocpnXImage::~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
268bool 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// ============================================================================
294PixelCache::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
351PixelCache::~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
372size_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
380void PixelCache::Update(void) {
381#ifdef __PIX_CACHE_WXIMAGE__
382 delete m_pbm; // kill the old one
383 m_pbm = NULL;
384#endif
385}
386
387void 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
420unsigned 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// class wxBitmapRefData;
452
453// ============================================================================
454// implementation
455// ============================================================================
456
457ocpnBitmap::ocpnBitmap() {}
458
459// ocpnBitmap::~ocpnBitmap()
460//{
461//}
462
464#ifdef __WXGTK__
465#ifdef opcnUSE_GTK_OPTIMIZE
466
467// ----------------------------------------------------------------------------
468// Create from Data
469// ----------------------------------------------------------------------------
470
471bool ocpnBitmap::CreateFromData(void *pPix, int width, int height, int depth) {
472 /*
473 XImage *img = NULL;
474
475// Do some basic setup in the parent wxBitmap class
476 Create(width, height, -1);
477
478 Display *xdisplay = (Display *)GetDisplay();
479
480 ocpnXImage *pXI = new ocpnXImage(width, height);
481 img = pXI->m_img;
482
483// Faster render from a 24 or 32 bit pixel buffer
484
485 if((pPix != NULL ) && (NULL != img))
486 {
487 unsigned char* data = (unsigned char *)pPix;
488 if(depth == 32) // special fast case
489 {
490 for (int y = 0; y < height; y++)
491 {
492 char *pd = img->data + (y * img->bytes_per_line);
493 unsigned char *ps = data + (y * width * 4);
494 memcpy(pd, ps, width*4);
495 }
496 }
497
498 else
499 {
500 int *pi = (int *)img->data;
501 int index = 0;
502 for (int y = 0; y < height; y++)
503 {
504 for (int x = 0; x < width; x++)
505 {
506 int ri = *(int *)(&data[index]);
507 index++;
508 index++;
509 index++;
510
511 *pi = ri;
512 pi++;
513
514 }
515 }
516 }
517 }
518
519 // Blit picture
520
521 Pixmap mypixmap = ((Pixmap )GetPixmap());
522 GC gc = XCreateGC( xdisplay, mypixmap, 0, NULL );
523
524 pXI->PutImage(mypixmap, gc);
525
526 delete pXI;
527
528 XFreeGC( xdisplay, gc );
529 */
530
532 Create(width, height, 32);
533
534 // GdkPixbuf* pixbuf = wx_static_cast(wxBitmapRefData*,
535 // m_refData))->m_pixbuf;
536 // GdkPixbuf* pixbuf = M_BMPDATA->m_pixbuf;
537
538 // Copy the data:
539 if (NULL != pPix) {
540 GdkPixbuf *pixbuf = GetPixbuf();
541
542 if (!pixbuf) return false;
543
544 unsigned char *in = (unsigned char *)pPix;
545 unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
546
547 int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
548
549 for (int y = 0; y < height; y++, out += rowpad) {
553
554 for (int x = 0; x < width; x++, out += 4, in += 4) {
555 out[0] = in[0];
556 out[1] = in[1];
557 out[2] = in[2];
558 out[3] = in[3];
559 }
560 }
561 }
562 return true;
563}
564
565/*
566bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
567{
568 wxASSERT(image.HasAlpha());
569
570 int width = image.GetWidth();
571 int height = image.GetHeight();
572
573 Create(width, height, 32);
574 GdkPixbuf* pixbuf = M_BMPDATA->m_pixbuf;
575 if (!pixbuf)
576 return false;
577
578 // Copy the data:
579 const unsigned char* in = image.GetData();
580 unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
581 unsigned char *alpha = image.GetAlpha();
582
583 int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
584
585 for (int y = 0; y < height; y++, out += rowpad)
586 {
587 for (int x = 0; x < width; x++, alpha++, out += 4, in += 3)
588 {
589 out[0] = in[0];
590 out[1] = in[1];
591 out[2] = in[2];
592 out[3] = *alpha;
593 }
594 }
595
596 return true;
597}
598*/
599#endif // opcnUSE_GTK_OPTIMIZE
600#endif
601
602#ifdef __WXX11__
603// This is the X11 Version
604
605// ----------------------------------------------------------------------------
606// Create from ocpnXImage
607// ----------------------------------------------------------------------------
608
609bool ocpnBitmap::CreateFromocpnXImage(ocpnXImage *poXI, int width, int height,
610 int depth) {
611 // Do some basic setup in the parent wxBitmap class
612 Create(width, height, -1);
613
614 Display *xdisplay = (Display *)GetDisplay();
615
616 XImage *data_image = poXI->m_img;
617 bool bShared = poXI->buse_mit;
618
619 // Blit picture
620
621 Pixmap mypixmap = ((Pixmap)GetPixmap());
622
623 GC gc = XCreateGC(xdisplay, mypixmap, 0, NULL);
624
625 if (bShared)
626 XShmPutImage(xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height,
627 False);
628 else
629 XPutImage(xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height);
630
631 XFreeGC(xdisplay, gc);
632
633 return true;
634}
635
636// ----------------------------------------------------------------------------
637// Create from Data
638// ----------------------------------------------------------------------------
639
640bool ocpnBitmap::CreateFromData(void *pPix, int width, int height, int depth) {
641 XImage *img = NULL;
642
643 // Do some basic setup in the parent wxBitmap class
644 Create(width, height, -1);
645
646 Display *xdisplay = (Display *)GetDisplay();
647
648 ocpnXImage *pXI = new ocpnXImage(width, height);
649 img = pXI->m_img;
650
651 // Faster render from a 24 or 32 bit pixel buffer
652
653 if ((pPix != NULL) && (NULL != img)) {
654 unsigned char *data = (unsigned char *)pPix;
655 if (depth == 32) // special fast case
656 {
657 for (int y = 0; y < height; y++) {
658 char *pd = img->data + (y * img->bytes_per_line);
659 unsigned char *ps = data + (y * width * 4);
660 memcpy(pd, ps, width * 4);
661 }
662 }
663
664 else {
665 int *pi = (int *)img->data;
666 int index = 0;
667 for (int y = 0; y < height; y++) {
668 for (int x = 0; x < width; x++) {
669 int ri = *(int *)(&data[index]);
670 index++;
671 index++;
672 index++;
673
674 *pi = ri;
675 pi++;
676 }
677 }
678 }
679 }
680
681 // Blit picture
682
683 Pixmap mypixmap = ((Pixmap)GetPixmap());
684 GC gc = XCreateGC(xdisplay, mypixmap, 0, NULL);
685
686 pXI->PutImage(mypixmap, gc);
687
688 delete pXI;
689
690 XFreeGC(xdisplay, gc);
691
692 return TRUE;
693}
694
695#endif //__WXX11__
696
697#ifdef __WXMSW__
698
699// ----------------------------------------------------------------------------
700// Create from Data
701// ----------------------------------------------------------------------------
702bool ocpnBitmap::CreateFromData(void *pPix, int width, int height, int depth) {
703 m_refData = CreateData(); // found in wxBitmap
704 // int width = image.GetWidth();
705 // int height0 = image.GetHeight();
706
707 int height0 = height;
708 int sizeLimit = 1280 * 1024 * 3;
709
710 int bmpHeight = height0;
711 // int height = bmpHeight;
712
713 // calc the number of bytes per scanline and padding
714 int bytePerLine = width * 3;
715 int sizeDWORD = sizeof(DWORD);
716 int lineBoundary = bytePerLine % sizeDWORD;
717 int padding = 0;
718 if (lineBoundary > 0) {
719 padding = sizeDWORD - lineBoundary;
720 bytePerLine += padding;
721 }
722
723 // set bitmap parameters
724 SetWidth(width);
725 SetHeight(bmpHeight);
726 if (depth == -1) depth = wxDisplayDepth();
727 SetDepth(depth);
728
729 // create a DIB header
730 int headersize = sizeof(BITMAPINFOHEADER);
731 BITMAPINFO *lpDIBh = (BITMAPINFO *)malloc(headersize);
732 wxCHECK_MSG(lpDIBh, FALSE, wxT("could not allocate memory for DIB header"));
733 // Fill in the DIB header
734 lpDIBh->bmiHeader.biSize = headersize;
735 lpDIBh->bmiHeader.biWidth = (DWORD)width;
736 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
737 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
738 // the general formula for biSizeImage:
739 // ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
740 lpDIBh->bmiHeader.biPlanes = 1;
741 lpDIBh->bmiHeader.biBitCount = 24;
742 lpDIBh->bmiHeader.biCompression = BI_RGB;
743 lpDIBh->bmiHeader.biClrUsed = 0;
744 // These seem not really needed for our purpose here.
745 lpDIBh->bmiHeader.biClrImportant = 0;
746 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
747 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
748 // memory for DIB data
749 unsigned char *lpBits;
750 lpBits = (unsigned char *)malloc(lpDIBh->bmiHeader.biSizeImage);
751 if (!lpBits) {
752 wxFAIL_MSG(wxT("could not allocate memory for DIB"));
753 free(lpDIBh);
754 return FALSE;
755 }
756
757 // create and set the device-dependent bitmap
758 HDC hdc = ::GetDC(NULL);
759 HDC memdc = ::CreateCompatibleDC(hdc);
760 HBITMAP hbitmap;
761 // hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );
762
763 // if(hbitmap == NULL)
764 // int cop =0;
765
766 // ::SelectObject( memdc, hbitmap);
767
768 // copy image data into DIB data
769 unsigned char *data = (unsigned char *)pPix;
770 int i, j;
771 unsigned char *ptdata = data;
772 unsigned char *ptbits;
773
774 ptbits = lpBits;
775
776 if (pPix) {
777 for (j = 0; j < height; j++) {
778 memcpy(ptbits, ptdata, width * 3);
779 ptbits += width * 3;
780 ptdata += width * 3;
781
782 for (i = 0; i < padding; i++) *(ptbits++) = 0;
783 }
784 }
785
786 else {
787 for (j = 0; j < height; j++) {
788 memset(ptbits, 0, width * 3);
789 ptbits += width * 3;
790
791 for (i = 0; i < padding; i++) *(ptbits++) = 0;
792 }
793 }
794
795 hbitmap = CreateDIBitmap(hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh,
796 DIB_RGB_COLORS);
797 // The above line is equivalent to the following two lines.
798 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
799 // ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
800 // or the following lines
801 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
802 // HDC memdc = ::CreateCompatibleDC( hdc );
803 // ::SelectObject( memdc, hbitmap);
804 // ::SetDIBitsToDevice( memdc, 0, 0, width, height,
805 // 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
806 // ::SelectObject( memdc, 0 );
807 // ::DeleteDC( memdc );
808 SetHBITMAP((WXHBITMAP)hbitmap);
809
810 // free allocated resources
811 ::DeleteDC(memdc);
812 ::ReleaseDC(NULL, hdc);
813 free(lpDIBh);
814 free(lpBits);
815
816 return TRUE;
817}
818
819// ----------------------------------------------------------------------------
820// wxImage from conversion
821// ----------------------------------------------------------------------------
822
823bool ocpnBitmap::CreateFromImage(const wxImage &image, int depth) {
824 // wxCHECK_MSG( image.Ok(), FALSE, wxT("invalid image") )
825
826 m_refData = CreateData(); // found in wxBitmap
827
828 // sizeLimit is the MS upper limit for the DIB size
829 int sizeLimit = 1280 * 1024 * 3;
830
831 // width and height of the device-dependent bitmap
832 int width = image.GetWidth();
833 int bmpHeight = image.GetHeight();
834
835 // calc the number of bytes per scanline and padding
836 int bytePerLine = width * 3;
837 int sizeDWORD = sizeof(DWORD);
838 int lineBoundary = bytePerLine % sizeDWORD;
839 int padding = 0;
840 if (lineBoundary > 0) {
841 padding = sizeDWORD - lineBoundary;
842 bytePerLine += padding;
843 }
844 // calc the number of DIBs and heights of DIBs
845 int numDIB = 1;
846 int hRemain = 0;
847 int height = sizeLimit / bytePerLine;
848 if (height >= bmpHeight)
849 height = bmpHeight;
850 else {
851 numDIB = bmpHeight / height;
852 hRemain = bmpHeight % height;
853 if (hRemain > 0) numDIB++;
854 }
855
856 // set bitmap parameters
857 wxCHECK_MSG(image.Ok(), FALSE, wxT("invalid image"));
858 SetWidth(width);
859 SetHeight(bmpHeight);
860 if (depth == -1) depth = wxDisplayDepth();
861 SetDepth(depth);
862
863#if wxUSE_PALETTE
864 // Copy the palette from the source image
865 SetPalette(image.GetPalette());
866#endif // wxUSE_PALETTE
867
868 // create a DIB header
869 int headersize = sizeof(BITMAPINFOHEADER);
870 BITMAPINFO *lpDIBh = (BITMAPINFO *)malloc(headersize);
871 wxCHECK_MSG(lpDIBh, FALSE, wxT("could not allocate memory for DIB header"));
872 // Fill in the DIB header
873 lpDIBh->bmiHeader.biSize = headersize;
874 lpDIBh->bmiHeader.biWidth = (DWORD)width;
875 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
876 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
877 // the general formula for biSizeImage:
878 // ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
879 lpDIBh->bmiHeader.biPlanes = 1;
880 lpDIBh->bmiHeader.biBitCount = 24;
881 lpDIBh->bmiHeader.biCompression = BI_RGB;
882 lpDIBh->bmiHeader.biClrUsed = 0;
883 // These seem not really needed for our purpose here.
884 lpDIBh->bmiHeader.biClrImportant = 0;
885 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
886 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
887 // memory for DIB data
888 unsigned char *lpBits;
889 lpBits = (unsigned char *)malloc(lpDIBh->bmiHeader.biSizeImage);
890 if (!lpBits) {
891 wxFAIL_MSG(wxT("could not allocate memory for DIB"));
892 free(lpDIBh);
893 return FALSE;
894 }
895
896 // create and set the device-dependent bitmap
897 HDC hdc = ::GetDC(NULL);
898 HDC memdc = ::CreateCompatibleDC(hdc);
899 HBITMAP hbitmap;
900 // hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );
901
902 // if(hbitmap == NULL)
903 // int cop =0;
904
905 // ::SelectObject( memdc, hbitmap);
906
907#if wxUSE_PALETTE
908 HPALETTE hOldPalette = 0;
909 if (image.GetPalette().Ok()) {
910 hOldPalette = ::SelectPalette(
911 memdc, (HPALETTE)image.GetPalette().GetHPALETTE(), FALSE);
912 ::RealizePalette(memdc);
913 }
914#endif // wxUSE_PALETTE
915
916 // copy image data into DIB data and then into DDB (in a loop)
917 unsigned char *data = image.GetData();
918 int i, j, n;
919 int origin = 0;
920 unsigned char *ptdata = data;
921 unsigned char *ptbits;
922
923 for (n = 0; n < numDIB; n++) {
924 if (numDIB > 1 && n == numDIB - 1 && hRemain > 0) {
925 // redefine height and size of the (possibly) last smaller DIB
926 // memory is not reallocated
927 height = hRemain;
928 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
929 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
930 }
931 ptbits = lpBits;
932
933 for (j = 0; j < height; j++) {
934 memcpy(ptbits, ptdata, width * 3);
935 ptbits += width * 3;
936 ptdata += width * 3;
937
938 /*
939 for( i=0; i<width; i++ )
940 {
941 *(ptbits++) = *(ptdata+2);
942 *(ptbits++) = *(ptdata+1);
943 *(ptbits++) = *(ptdata );
944 ptdata += 3;
945 }
946 */
947 for (i = 0; i < padding; i++) *(ptbits++) = 0;
948 }
949 // ::StretchDIBits( memdc, 0, origin, width, height,
950 // 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
951 origin += height;
952 // if numDIB = 1, lines below can also be used
953 hbitmap = CreateDIBitmap(hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits,
954 lpDIBh, DIB_RGB_COLORS);
955 // if(hbitmap == NULL)
956 // int cop =0;
957
958 // The above line is equivalent to the following two lines.
959 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
960 // ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
961 // or the following lines
962 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
963 // HDC memdc = ::CreateCompatibleDC( hdc );
964 // ::SelectObject( memdc, hbitmap);
965 // ::SetDIBitsToDevice( memdc, 0, 0, width, height,
966 // 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
967 // ::SelectObject( memdc, 0 );
968 // ::DeleteDC( memdc );
969 }
970 SetHBITMAP((WXHBITMAP)hbitmap);
971
972 // if(!this->Ok())
973 // int cop = this->Ok();
974
975#if wxUSE_PALETTE
976 if (hOldPalette) SelectPalette(memdc, hOldPalette, FALSE);
977#endif // wxUSE_PALETTE
978
979 // similarly, created an mono-bitmap for the possible mask
980 if (image.HasMask()) {
981 hbitmap = ::CreateBitmap((WORD)width, (WORD)bmpHeight, 1, 1, NULL);
982 HGDIOBJ hbmpOld = ::SelectObject(memdc, hbitmap);
983 if (numDIB == 1)
984 height = bmpHeight;
985 else
986 height = sizeLimit / bytePerLine;
987 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
988 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
989 origin = 0;
990 unsigned char r = image.GetMaskRed();
991 unsigned char g = image.GetMaskGreen();
992 unsigned char b = image.GetMaskBlue();
993 unsigned char zero = 0, one = 255;
994 ptdata = data;
995 for (n = 0; n < numDIB; n++) {
996 if (numDIB > 1 && n == numDIB - 1 && hRemain > 0) {
997 // redefine height and size of the (possibly) last smaller DIB
998 // memory is not reallocated
999 height = hRemain;
1000 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
1001 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
1002 }
1003 ptbits = lpBits;
1004 for (int j = 0; j < height; j++) {
1005 for (i = 0; i < width; i++) {
1006 // was causing a code gen bug in cw : if( ( cr !=r) || (cg!=g) ||
1007 // (cb!=b) )
1008 unsigned char cr = (*(ptdata++));
1009 unsigned char cg = (*(ptdata++));
1010 unsigned char cb = (*(ptdata++));
1011
1012 if ((cr != r) || (cg != g) || (cb != b)) {
1013 *(ptbits++) = one;
1014 *(ptbits++) = one;
1015 *(ptbits++) = one;
1016 } else {
1017 *(ptbits++) = zero;
1018 *(ptbits++) = zero;
1019 *(ptbits++) = zero;
1020 }
1021 }
1022 for (i = 0; i < padding; i++) *(ptbits++) = zero;
1023 }
1024 ::StretchDIBits(memdc, 0, origin, width, height, 0, 0, width, height,
1025 lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
1026 origin += height;
1027 }
1028 // create a wxMask object
1029 wxMask *mask = new wxMask();
1030 mask->SetMaskBitmap((WXHBITMAP)hbitmap);
1031 SetMask(mask);
1032 // It will be deleted when the wxBitmap object is deleted (as of 01/1999)
1033 /* The following can also be used but is slow to run
1034 wxColour colour( GetMaskRed(), GetMaskGreen(), GetMaskBlue());
1035 wxMask *mask = new wxMask( *this, colour );
1036 SetMask( mask );
1037 */
1038
1039 ::SelectObject(memdc, hbmpOld);
1040 }
1041
1042 // free allocated resources
1043 ::DeleteDC(memdc);
1044 ::ReleaseDC(NULL, hdc);
1045 free(lpDIBh);
1046 free(lpBits);
1047
1048#if WXWIN_COMPATIBILITY_2
1049 // check the wxBitmap object
1050 GetBitmapData()->SetOk();
1051#endif // WXWIN_COMPATIBILITY_2
1052
1053 return TRUE;
1054}
1055
1056#endif // __WXMSW__
1057
1058#endif // ocpnUSE_ocpnBitmap
1059
1060// ============================================================================
1061// ocpnMemDC implementation
1062// ============================================================================
1063
1064ocpnMemDC::ocpnMemDC() {}
1065
1066#ifdef ocpnUSE_DIBSECTION
1067void ocpnMemDC::SelectObject(wxDIB &dib) {
1068 // select old bitmap out of the device context
1069 if (m_oldBitmap) {
1070 ::SelectObject(GetHdc(), (HBITMAP)m_oldBitmap);
1071 if (m_selectedBitmap.Ok()) {
1072#ifdef __WXDEBUG__
1073 // m_selectedBitmap.SetSelectedInto(NULL);
1074#endif
1075 m_selectedBitmap = wxNullBitmap;
1076 }
1077 }
1078
1079 // check for whether the bitmap is already selected into a device context
1080 // wxASSERT_MSG( !bitmap.GetSelectedInto() ||
1081 // (bitmap.GetSelectedInto() == this),
1082 // wxT("Bitmap is selected in another wxMemoryDC, delete the
1083 // first wxMemoryDC or use SelectObject(NULL)") );
1084
1085 /*
1086 m_selectedBitmap = bitmap;
1087 WXHBITMAP hBmp = m_selectedBitmap.GetHBITMAP();
1088 if ( !hBmp )
1089 return; // already selected
1090 */
1091 m_pselectedDIB = &dib;
1092 HBITMAP hDIB = m_pselectedDIB->GetHandle();
1093 if (!hDIB) return; // already selected
1094
1095#ifdef __WXDEBUG__
1096// m_selectedBitmap.SetSelectedInto(this);
1097#endif
1098
1099 // hBmp = (WXHBITMAP)::SelectObject(GetHdc(), (HBITMAP)hBmp);
1100
1101 hDIB = (HBITMAP)::SelectObject(GetHdc(), hDIB);
1102
1103 if (!hDIB) {
1104 wxLogLastError(wxT("SelectObject(ocpnMemDC, DIB)"));
1105
1106 wxFAIL_MSG(wxT("Couldn't select a DIB into ocpnMemDC"));
1107 }
1108
1109 else if (!m_oldBitmap) {
1110 m_oldBitmap = hDIB;
1111 }
1112}
1113
1114#endif // ocpnUSE_DIBSECTION
1115
1116/*
1117 * Rotation code by Carlos Moreno
1118 * Adapted to static and modified for improved performance by dsr
1119 */
1120
1121static const double wxROTATE_EPSILON = 1e-10;
1122
1123// Auxiliary function to rotate a point (x,y) with respect to point p0
1124// make it inline and use a straight return to facilitate optimization
1125// also, the function receives the sine and cosine of the angle to avoid
1126// repeating the time-consuming calls to these functions -- sin/cos can
1127// be computed and stored in the calling function.
1128
1129static inline wxRealPoint wxRotatePoint(const wxRealPoint &p, double cos_angle,
1130 double sin_angle,
1131 const wxRealPoint &p0) {
1132 return wxRealPoint(
1133 p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
1134 p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
1135}
1136
1137static inline wxRealPoint wxRotatePoint(double x, double y, double cos_angle,
1138 double sin_angle,
1139 const wxRealPoint &p0) {
1140 return wxRotatePoint(wxRealPoint(x, y), cos_angle, sin_angle, p0);
1141}
1142
1143wxImage Image_Rotate(wxImage &base_image, double angle,
1144 const wxPoint &centre_of_rotation, bool interpolating,
1145 wxPoint *offset_after_rotation) {
1146 int i;
1147 angle =
1148 -angle; // screen coordinates are a mirror image of "real" coordinates
1149
1150 bool has_alpha = base_image.HasAlpha();
1151
1152 const int w = base_image.GetWidth(), h = base_image.GetHeight();
1153
1154 // Create pointer-based array to accelerate access to wxImage's data
1155 unsigned char **data = new unsigned char *[h];
1156 data[0] = base_image.GetData();
1157 for (i = 1; i < h; i++) data[i] = data[i - 1] + (3 * w);
1158
1159 // Same for alpha channel
1160 unsigned char **alpha = NULL;
1161 if (has_alpha) {
1162 alpha = new unsigned char *[h];
1163 alpha[0] = base_image.GetAlpha();
1164 for (i = 1; i < h; i++) alpha[i] = alpha[i - 1] + w;
1165 }
1166
1167 // precompute coefficients for rotation formula
1168 // (sine and cosine of the angle)
1169 const double cos_angle = cos(angle);
1170 const double sin_angle = sin(angle);
1171
1172 // Create new Image to store the result
1173 // First, find rectangle that covers the rotated image; to do that,
1174 // rotate the four corners
1175
1176 const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
1177
1178 wxRealPoint p1 = wxRotatePoint(0, 0, cos_angle, sin_angle, p0);
1179 wxRealPoint p2 = wxRotatePoint(0, h, cos_angle, sin_angle, p0);
1180 wxRealPoint p3 = wxRotatePoint(w, 0, cos_angle, sin_angle, p0);
1181 wxRealPoint p4 = wxRotatePoint(w, h, cos_angle, sin_angle, p0);
1182
1183 int x1a = (int)floor(wxMin(wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
1184 int y1a = (int)floor(wxMin(wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
1185 int x2a = (int)ceil(wxMax(wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
1186 int y2a = (int)ceil(wxMax(wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));
1187
1188 // Create rotated image
1189 wxImage rotated(x2a - x1a + 1, y2a - y1a + 1, false);
1190 // With alpha channel
1191 if (has_alpha) rotated.SetAlpha();
1192
1193 if (offset_after_rotation != NULL) {
1194 *offset_after_rotation = wxPoint(x1a, y1a);
1195 }
1196
1197 // GRG: The rotated (destination) image is always accessed
1198 // sequentially, so there is no need for a pointer-based
1199 // array here (and in fact it would be slower).
1200 //
1201 unsigned char *dst = rotated.GetData();
1202
1203 unsigned char *alpha_dst = NULL;
1204 if (has_alpha) alpha_dst = rotated.GetAlpha();
1205
1206 // GRG: if the original image has a mask, use its RGB values
1207 // as the blank pixel, else, fall back to default (black).
1208 //
1209 unsigned char blank_r = 0;
1210 unsigned char blank_g = 0;
1211 unsigned char blank_b = 0;
1212
1213 if (base_image.HasMask()) {
1214 blank_r = base_image.GetMaskRed();
1215 blank_g = base_image.GetMaskGreen();
1216 blank_b = base_image.GetMaskBlue();
1217 rotated.SetMaskColour(blank_r, blank_g, blank_b);
1218 }
1219
1220 // Now, for each point of the rotated image, find where it came from, by
1221 // performing an inverse rotation (a rotation of -angle) and getting the
1222 // pixel at those coordinates
1223
1224 const int rH = rotated.GetHeight();
1225 const int rW = rotated.GetWidth();
1226
1227 // GRG: I've taken the (interpolating) test out of the loops, so that
1228 // it is done only once, instead of repeating it for each pixel.
1229
1230 if (interpolating) {
1231 for (int y = 0; y < rH; y++) {
1232 for (int x = 0; x < rW; x++) {
1233 wxRealPoint src =
1234 wxRotatePoint(x + x1a, y + y1a, cos_angle, -sin_angle, p0);
1235
1236 if (-0.25 < src.x && src.x < w - 0.75 && -0.25 < src.y &&
1237 src.y < h - 0.75) {
1238 // interpolate using the 4 enclosing grid-points. Those
1239 // points can be obtained using floor and ceiling of the
1240 // exact coordinates of the point
1241 int x1, y1, x2, y2;
1242
1243 if (0 < src.x && src.x < w - 1) {
1244 x1 = wxRound(floor(src.x));
1245 x2 = wxRound(ceil(src.x));
1246 } else // else means that x is near one of the borders (0 or width-1)
1247 {
1248 x1 = x2 = wxRound(src.x);
1249 }
1250
1251 if (0 < src.y && src.y < h - 1) {
1252 y1 = wxRound(floor(src.y));
1253 y2 = wxRound(ceil(src.y));
1254 } else {
1255 y1 = y2 = wxRound(src.y);
1256 }
1257
1258 // get four points and the distances (square of the distance,
1259 // for efficiency reasons) for the interpolation formula
1260
1261 // GRG: Do not calculate the points until they are
1262 // really needed -- this way we can calculate
1263 // just one, instead of four, if d1, d2, d3
1264 // or d4 are < wxROTATE_EPSILON
1265
1266 const double d1 =
1267 (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
1268 const double d2 =
1269 (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
1270 const double d3 =
1271 (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
1272 const double d4 =
1273 (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
1274
1275 // Now interpolate as a weighted average of the four surrounding
1276 // points, where the weights are the distances to each of those points
1277
1278 // If the point is exactly at one point of the grid of the source
1279 // image, then don't interpolate -- just assign the pixel
1280
1281 // d1,d2,d3,d4 are positive -- no need for abs()
1282 if (d1 < wxROTATE_EPSILON) {
1283 unsigned char *p = data[y1] + (3 * x1);
1284 *(dst++) = *(p++);
1285 *(dst++) = *(p++);
1286 *(dst++) = *p;
1287
1288 if (has_alpha) *(alpha_dst++) = *(alpha[y1] + x1);
1289 } else if (d2 < wxROTATE_EPSILON) {
1290 unsigned char *p = data[y1] + (3 * x2);
1291 *(dst++) = *(p++);
1292 *(dst++) = *(p++);
1293 *(dst++) = *p;
1294
1295 if (has_alpha) *(alpha_dst++) = *(alpha[y1] + x2);
1296 } else if (d3 < wxROTATE_EPSILON) {
1297 unsigned char *p = data[y2] + (3 * x2);
1298 *(dst++) = *(p++);
1299 *(dst++) = *(p++);
1300 *(dst++) = *p;
1301
1302 if (has_alpha) *(alpha_dst++) = *(alpha[y2] + x2);
1303 } else if (d4 < wxROTATE_EPSILON) {
1304 unsigned char *p = data[y2] + (3 * x1);
1305 *(dst++) = *(p++);
1306 *(dst++) = *(p++);
1307 *(dst++) = *p;
1308
1309 if (has_alpha) *(alpha_dst++) = *(alpha[y2] + x1);
1310 } else {
1311 // weights for the weighted average are proportional to the inverse
1312 // of the distance
1313 unsigned char *v1 = data[y1] + (3 * x1);
1314 unsigned char *v2 = data[y1] + (3 * x2);
1315 unsigned char *v3 = data[y2] + (3 * x2);
1316 unsigned char *v4 = data[y2] + (3 * x1);
1317
1318 const double w1 = 1 / d1, w2 = 1 / d2, w3 = 1 / d3, w4 = 1 / d4;
1319
1320 // GRG: Unrolled.
1321
1322 *(dst++) = (unsigned char)((w1 * *(v1++) + w2 * *(v2++) +
1323 w3 * *(v3++) + w4 * *(v4++)) /
1324 (w1 + w2 + w3 + w4));
1325 *(dst++) = (unsigned char)((w1 * *(v1++) + w2 * *(v2++) +
1326 w3 * *(v3++) + w4 * *(v4++)) /
1327 (w1 + w2 + w3 + w4));
1328 *(dst++) =
1329 (unsigned char)((w1 * *v1 + w2 * *v2 + w3 * *v3 + w4 * *v4) /
1330 (w1 + w2 + w3 + w4));
1331
1332 if (has_alpha) {
1333 v1 = alpha[y1] + (x1);
1334 v2 = alpha[y1] + (x2);
1335 v3 = alpha[y2] + (x2);
1336 v4 = alpha[y2] + (x1);
1337
1338 *(alpha_dst++) =
1339 (unsigned char)((w1 * *v1 + w2 * *v2 + w3 * *v3 + w4 * *v4) /
1340 (w1 + w2 + w3 + w4));
1341 }
1342 }
1343 } else {
1344 *(dst++) = blank_r;
1345 *(dst++) = blank_g;
1346 *(dst++) = blank_b;
1347
1348 if (has_alpha) *(alpha_dst++) = 0;
1349 }
1350 }
1351 }
1352 } else // not interpolating
1353 {
1354 double x0 = p0.x;
1355 double y0 = p0.y;
1356 double x1b = x1a - p0.x;
1357 double y1b = y1a - p0.y;
1358 double msa = -sin_angle;
1359
1360 for (int y = 0; y < rH; y++) {
1361 for (int x = 0; x < rW; x++) {
1362 // wxRealPoint src =
1363 // wxRotatePoint (x + x1a,
1364 // y + y1a, cos_angle,
1365 // -sin_angle, p0);
1366
1367 // double sx = p0.x + (x +
1368 // x1a - p0.x) * cos_angle
1369 // - (y + y1a - p0.y) *
1370 // -sin_angle; double sy=
1371 // p0.y + (y + y1a - p0.y)
1372 // * cos_angle + (x + x1a
1373 // - p0.x) * -sin_angle;
1374
1375 double sx = x0 + (x + x1b) * cos_angle - (y + y1b) * msa;
1376 double sy = y0 + (y + y1b) * cos_angle + (x + x1b) * msa;
1377
1378 const int xs = (int)sx;
1379 const int ys = (int)sy;
1380
1381 // return wxRealPoint(p0.x
1382 // + (p.x - p0.x) *
1383 // cos_angle - (p.y -
1384 // p0.y) * sin_angle,
1385 // p0.y + (p.y
1386 // - p0.y) *
1387 // cos_angle +
1388 // (p.x -
1389 // p0.x) *
1390 // sin_angle);
1391
1392 // const int xs =
1393 // /*wxRound*/ (src.x); //
1394 // wxRound rounds to the
1395 // const int ys =
1396 // /*wxRound*/ (src.y); //
1397 // closest integer
1398
1399 if (0 <= xs && xs < w && 0 <= ys && ys < h) {
1400 unsigned char *p = data[ys] + (3 * xs);
1401 *(dst++) = *(p++);
1402 *(dst++) = *(p++);
1403 *(dst++) = *p;
1404
1405 if (has_alpha) *(alpha_dst++) = *(alpha[ys] + (xs));
1406 } else {
1407 *(dst++) = blank_r;
1408 *(dst++) = blank_g;
1409 *(dst++) = blank_b;
1410
1411 if (has_alpha) *(alpha_dst++) = 255;
1412 }
1413 }
1414 }
1415 }
1416
1417 delete[] data;
1418
1419 if (has_alpha) delete[] alpha;
1420
1421 return rotated;
1422}