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