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
451IMPLEMENT_DYNAMIC_CLASS(ocpnBitmap, wxBitmap /*wxGDIObject*/)
452
453// class wxBitmapRefData;
454
455// ============================================================================
456// implementation
457// ============================================================================
458
459ocpnBitmap::ocpnBitmap() {}
460
461// ocpnBitmap::~ocpnBitmap()
462//{
463//}
464
466#ifdef __WXGTK__
467#ifdef opcnUSE_GTK_OPTIMIZE
468
469// ----------------------------------------------------------------------------
470// Create from Data
471// ----------------------------------------------------------------------------
472
473bool 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/*
568bool 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
611bool 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
642bool 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// ----------------------------------------------------------------------------
704bool 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
825bool 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
1066IMPLEMENT_DYNAMIC_CLASS(ocpnMemDC, wxMemoryDC)
1067
1068ocpnMemDC::ocpnMemDC() {}
1069
1070#ifdef ocpnUSE_DIBSECTION
1071void 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
1125static 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
1133static 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
1141static 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
1147wxImage 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}