58#include "ocpn_pixel.h"
66#include <wx/palette.h>
67#include <wx/dcmemory.h>
73#include <wx/msw/private.h>
75#include <wx/msw/dib.h>
85#include <wx/palette.h>
89#define CLR_INVALID ((COLORREF)-1)
92#ifdef ocpnUSE_ocpnBitmap
96#include <X11/extensions/XShm.h>
106static int MITErrorFlag = 0;
107static int HandleXError(Display *dpy, XErrorEvent *event) {
117static void *x_malloc(
size_t t) {
118 void *pr = malloc(t);
122 wxLogMessage(_T(
"x_malloc...malloc fails with request of %d bytes."), t);
130 int fd = open(
"/proc/meminfo", O_RDONLY);
132 if (fd == -1) exit(1);
134 len = read(fd, buf,
sizeof(buf) - 1);
152 if (t > malloc_max) {
166ocpnXImage::ocpnXImage(
int width,
int height) {
172 xdisplay = (Display *)wxGlobalDisplay();
173 xscreen = DefaultScreen(xdisplay);
174 xvisual = DefaultVisual(xdisplay, xscreen);
175 int bpp = wxTheApp->GetVisualInfo(xdisplay)->m_visualDepth;
182 XQueryExtension(xdisplay,
"MIT-SHM", &ignore, &ignore, &ignore);
185 m_img = XShmCreateImage(xdisplay, xvisual, bpp, ZPixmap, NULL, &shminfo,
188 wxLogError(_T(
"XShmCreateImage failed!"));
193 shminfo.shmid = shmget(IPC_PRIVATE, m_img->bytes_per_line * m_img->height,
195 if (shminfo.shmid < 0) {
196 XDestroyImage(m_img);
199 _T(
"alloc_back_buffer: Shared memory error (shmget), disabling."));
203 shminfo.shmaddr = m_img->data = (
char *)shmat(shminfo.shmid, 0, 0);
205 if (shminfo.shmaddr == (
char *)-1) {
206 XDestroyImage(m_img);
208 wxLogMessage(_T(
"shmat failed"));
213 shminfo.readOnly = False;
216 XSetErrorHandler(HandleXError);
218 XShmAttach(xdisplay, &shminfo);
219 XSync(xdisplay, False);
225 XDestroyImage(m_img);
227 shmdt(shminfo.shmaddr);
228 shmctl(shminfo.shmid, IPC_RMID, 0);
232 shmctl(shminfo.shmid, IPC_RMID, 0);
242 m_img = XCreateImage(xdisplay, xvisual, bpp, ZPixmap, 0, 0, width, height,
244 m_img->data = (
char *)x_malloc(m_img->bytes_per_line * m_img->height);
246 if (m_img->data == NULL) {
247 XDestroyImage(m_img);
249 wxLogError(wxT(
"ocpn_Bitmap:Cannot malloc for data image."));
254ocpnXImage::~ocpnXImage() {
257 XShmDetach(xdisplay, &shminfo);
258 XDestroyImage(m_img);
259 shmdt(shminfo.shmaddr);
261 XDestroyImage(m_img);
264 XDestroyImage(m_img);
268bool ocpnXImage::PutImage(Pixmap pixmap, GC gc) {
271 XShmPutImage(xdisplay, pixmap, gc, m_img, 0, 0, 0, 0, m_width, m_height,
274 XPutImage(xdisplay, pixmap, gc, m_img, 0, 0, 0, 0, m_width, m_height);
277 XPutImage(xdisplay, pixmap, gc, m_img, 0, 0, 0, 0, m_width, m_height);
294PixelCache::PixelCache(
int width,
int height,
int depth) {
302 bytes_per_pixel = BPP / 8;
303 line_pitch_bytes = bytes_per_pixel * width;
305#ifdef ocpnUSE_ocpnBitmap
309#ifdef __PIX_CACHE_PIXBUF__
313#ifdef __PIX_CACHE_DIBSECTION__
314 m_pDS =
new wxDIB(width, -height, BPP);
315 pData = m_pDS->GetData();
318 line_pitch_bytes = (((m_width * 24) + 31) & ~31) >> 3;
322#ifdef __PIX_CACHE_WXIMAGE__
323 m_pimage =
new wxImage(m_width, m_height, (
bool)FALSE);
324 pData = m_pimage->GetData();
327#ifdef __PIX_CACHE_X11IMAGE__
328 m_pocpnXI =
new ocpnXImage(width, height);
329 pData = (
unsigned char *)m_pocpnXI->m_img->data;
332#ifdef __PIX_CACHE_PIXBUF__
345 pData = (
unsigned char *)malloc(m_width * m_height * 4);
351PixelCache::~PixelCache() {
352#ifdef __PIX_CACHE_WXIMAGE__
357#ifdef __PIX_CACHE_DIBSECTION__
361#ifdef __PIX_CACHE_X11IMAGE__
366#ifdef __PIX_CACHE_PIXBUF__
372size_t PixelCache::GetLength(
void) {
373#ifdef __PIX_CACHE_WXIMAGE__
374 return m_width * m_height * 3;
380void PixelCache::Update(
void) {
381#ifdef __PIX_CACHE_WXIMAGE__
387void PixelCache::SelectIntoDC(wxMemoryDC &dc) {
388#ifdef __PIX_CACHE_DIBSECTION__
390 pmdc->SelectObject(*m_pDS);
394#ifdef __PIX_CACHE_WXIMAGE__
398#ifdef ocpnUSE_ocpnBitmap
399 if (!m_pbm) m_pbm =
new ocpnBitmap(*m_pimage, m_depth);
401 if (!m_pbm) m_pbm =
new wxBitmap(*m_pimage, -1);
404 if (m_pbm) dc.SelectObject(*m_pbm);
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);
412#ifdef __PIX_CACHE_PIXBUF__
413 if (!m_pbm) m_pbm =
new ocpnBitmap(pData, m_width, m_height, m_depth);
415 dc.SelectObject(*m_pbm);
420unsigned char *PixelCache::GetpData(
void)
const {
return pData; }
422#ifdef ocpnUSE_ocpnBitmap
449#define M_BMPDATA wx_static_cast(wxBitmapRefData *, m_refData)
451IMPLEMENT_DYNAMIC_CLASS(ocpnBitmap, wxBitmap )
459ocpnBitmap::ocpnBitmap() {}
467#ifdef opcnUSE_GTK_OPTIMIZE
473bool ocpnBitmap::CreateFromData(
void *pPix,
int width,
int height,
int depth) {
534 Create(width, height, 32);
542 GdkPixbuf *pixbuf = GetPixbuf();
544 if (!pixbuf)
return false;
546 unsigned char *in = (
unsigned char *)pPix;
547 unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
549 int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
551 for (
int y = 0; y < height; y++, out += rowpad) {
556 for (
int x = 0; x < width; x++, out += 4, in += 4) {
611bool ocpnBitmap::CreateFromocpnXImage(ocpnXImage *poXI,
int width,
int height,
614 Create(width, height, -1);
616 Display *xdisplay = (Display *)GetDisplay();
618 XImage *data_image = poXI->m_img;
619 bool bShared = poXI->buse_mit;
623 Pixmap mypixmap = ((Pixmap)GetPixmap());
625 GC gc = XCreateGC(xdisplay, mypixmap, 0, NULL);
628 XShmPutImage(xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height,
631 XPutImage(xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height);
633 XFreeGC(xdisplay, gc);
642bool ocpnBitmap::CreateFromData(
void *pPix,
int width,
int height,
int depth) {
646 Create(width, height, -1);
648 Display *xdisplay = (Display *)GetDisplay();
650 ocpnXImage *pXI =
new ocpnXImage(width, height);
655 if ((pPix != NULL) && (NULL != img)) {
656 unsigned char *data = (
unsigned char *)pPix;
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);
667 int *pi = (
int *)img->data;
669 for (
int y = 0; y < height; y++) {
670 for (
int x = 0; x < width; x++) {
671 int ri = *(
int *)(&data[index]);
685 Pixmap mypixmap = ((Pixmap)GetPixmap());
686 GC gc = XCreateGC(xdisplay, mypixmap, 0, NULL);
688 pXI->PutImage(mypixmap, gc);
692 XFreeGC(xdisplay, gc);
704bool ocpnBitmap::CreateFromData(
void *pPix,
int width,
int height,
int depth) {
705 m_refData = CreateData();
709 int height0 = height;
710 int sizeLimit = 1280 * 1024 * 3;
712 int bmpHeight = height0;
716 int bytePerLine = width * 3;
717 int sizeDWORD =
sizeof(DWORD);
718 int lineBoundary = bytePerLine % sizeDWORD;
720 if (lineBoundary > 0) {
721 padding = sizeDWORD - lineBoundary;
722 bytePerLine += padding;
727 SetHeight(bmpHeight);
728 if (depth == -1) depth = wxDisplayDepth();
732 int headersize =
sizeof(BITMAPINFOHEADER);
733 BITMAPINFO *lpDIBh = (BITMAPINFO *)malloc(headersize);
734 wxCHECK_MSG(lpDIBh, FALSE, wxT(
"could not allocate memory for 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;
742 lpDIBh->bmiHeader.biPlanes = 1;
743 lpDIBh->bmiHeader.biBitCount = 24;
744 lpDIBh->bmiHeader.biCompression = BI_RGB;
745 lpDIBh->bmiHeader.biClrUsed = 0;
747 lpDIBh->bmiHeader.biClrImportant = 0;
748 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
749 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
751 unsigned char *lpBits;
752 lpBits = (
unsigned char *)malloc(lpDIBh->bmiHeader.biSizeImage);
754 wxFAIL_MSG(wxT(
"could not allocate memory for DIB"));
760 HDC hdc = ::GetDC(NULL);
761 HDC memdc = ::CreateCompatibleDC(hdc);
771 unsigned char *data = (
unsigned char *)pPix;
773 unsigned char *ptdata = data;
774 unsigned char *ptbits;
779 for (j = 0; j < height; j++) {
780 memcpy(ptbits, ptdata, width * 3);
784 for (i = 0; i < padding; i++) *(ptbits++) = 0;
789 for (j = 0; j < height; j++) {
790 memset(ptbits, 0, width * 3);
793 for (i = 0; i < padding; i++) *(ptbits++) = 0;
797 hbitmap = CreateDIBitmap(hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh,
810 SetHBITMAP((WXHBITMAP)hbitmap);
814 ::ReleaseDC(NULL, hdc);
825bool ocpnBitmap::CreateFromImage(
const wxImage &image,
int depth) {
828 m_refData = CreateData();
831 int sizeLimit = 1280 * 1024 * 3;
834 int width = image.GetWidth();
835 int bmpHeight = image.GetHeight();
838 int bytePerLine = width * 3;
839 int sizeDWORD =
sizeof(DWORD);
840 int lineBoundary = bytePerLine % sizeDWORD;
842 if (lineBoundary > 0) {
843 padding = sizeDWORD - lineBoundary;
844 bytePerLine += padding;
849 int height = sizeLimit / bytePerLine;
850 if (height >= bmpHeight)
853 numDIB = bmpHeight / height;
854 hRemain = bmpHeight % height;
855 if (hRemain > 0) numDIB++;
859 wxCHECK_MSG(image.Ok(), FALSE, wxT(
"invalid image"));
861 SetHeight(bmpHeight);
862 if (depth == -1) depth = wxDisplayDepth();
867 SetPalette(image.GetPalette());
871 int headersize =
sizeof(BITMAPINFOHEADER);
872 BITMAPINFO *lpDIBh = (BITMAPINFO *)malloc(headersize);
873 wxCHECK_MSG(lpDIBh, FALSE, wxT(
"could not allocate memory for 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;
881 lpDIBh->bmiHeader.biPlanes = 1;
882 lpDIBh->bmiHeader.biBitCount = 24;
883 lpDIBh->bmiHeader.biCompression = BI_RGB;
884 lpDIBh->bmiHeader.biClrUsed = 0;
886 lpDIBh->bmiHeader.biClrImportant = 0;
887 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
888 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
890 unsigned char *lpBits;
891 lpBits = (
unsigned char *)malloc(lpDIBh->bmiHeader.biSizeImage);
893 wxFAIL_MSG(wxT(
"could not allocate memory for DIB"));
899 HDC hdc = ::GetDC(NULL);
900 HDC memdc = ::CreateCompatibleDC(hdc);
910 HPALETTE hOldPalette = 0;
911 if (image.GetPalette().Ok()) {
912 hOldPalette = ::SelectPalette(
913 memdc, (HPALETTE)image.GetPalette().GetHPALETTE(), FALSE);
914 ::RealizePalette(memdc);
919 unsigned char *data = image.GetData();
922 unsigned char *ptdata = data;
923 unsigned char *ptbits;
925 for (n = 0; n < numDIB; n++) {
926 if (numDIB > 1 && n == numDIB - 1 && hRemain > 0) {
930 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
931 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
935 for (j = 0; j < height; j++) {
936 memcpy(ptbits, ptdata, width * 3);
949 for (i = 0; i < padding; i++) *(ptbits++) = 0;
955 hbitmap = CreateDIBitmap(hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits,
956 lpDIBh, DIB_RGB_COLORS);
972 SetHBITMAP((WXHBITMAP)hbitmap);
978 if (hOldPalette) SelectPalette(memdc, hOldPalette, FALSE);
982 if (image.HasMask()) {
983 hbitmap = ::CreateBitmap((WORD)width, (WORD)bmpHeight, 1, 1, NULL);
984 HGDIOBJ hbmpOld = ::SelectObject(memdc, hbitmap);
988 height = sizeLimit / bytePerLine;
989 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
990 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
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;
997 for (n = 0; n < numDIB; n++) {
998 if (numDIB > 1 && n == numDIB - 1 && hRemain > 0) {
1002 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
1003 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
1006 for (
int j = 0; j < height; j++) {
1007 for (i = 0; i < width; i++) {
1010 unsigned char cr = (*(ptdata++));
1011 unsigned char cg = (*(ptdata++));
1012 unsigned char cb = (*(ptdata++));
1014 if ((cr != r) || (cg != g) || (cb != b)) {
1024 for (i = 0; i < padding; i++) *(ptbits++) = zero;
1026 ::StretchDIBits(memdc, 0, origin, width, height, 0, 0, width, height,
1027 lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
1031 wxMask *mask =
new wxMask();
1032 mask->SetMaskBitmap((WXHBITMAP)hbitmap);
1041 ::SelectObject(memdc, hbmpOld);
1046 ::ReleaseDC(NULL, hdc);
1050#if WXWIN_COMPATIBILITY_2
1052 GetBitmapData()->SetOk();
1066IMPLEMENT_DYNAMIC_CLASS(
ocpnMemDC, wxMemoryDC)
1068ocpnMemDC::ocpnMemDC() {}
1070#ifdef ocpnUSE_DIBSECTION
1071void ocpnMemDC::SelectObject(wxDIB &dib) {
1074 ::SelectObject(GetHdc(), (HBITMAP)m_oldBitmap);
1075 if (m_selectedBitmap.Ok()) {
1079 m_selectedBitmap = wxNullBitmap;
1095 m_pselectedDIB = &dib;
1096 HBITMAP hDIB = m_pselectedDIB->GetHandle();
1105 hDIB = (HBITMAP)::SelectObject(GetHdc(), hDIB);
1108 wxLogLastError(wxT(
"SelectObject(ocpnMemDC, DIB)"));
1110 wxFAIL_MSG(wxT(
"Couldn't select a DIB into ocpnMemDC"));
1113 else if (!m_oldBitmap) {
1125static const double wxROTATE_EPSILON = 1e-10;
1133static inline wxRealPoint wxRotatePoint(
const wxRealPoint &p,
double cos_angle,
1135 const wxRealPoint &p0) {
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);
1141static inline wxRealPoint wxRotatePoint(
double x,
double y,
double cos_angle,
1143 const wxRealPoint &p0) {
1144 return wxRotatePoint(wxRealPoint(x, y), cos_angle, sin_angle, p0);
1147wxImage Image_Rotate(wxImage &base_image,
double angle,
1148 const wxPoint ¢re_of_rotation,
bool interpolating,
1149 wxPoint *offset_after_rotation) {
1154 bool has_alpha = base_image.HasAlpha();
1156 const int w = base_image.GetWidth(), h = base_image.GetHeight();
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);
1164 unsigned char **alpha = NULL;
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;
1173 const double cos_angle = cos(angle);
1174 const double sin_angle = sin(angle);
1180 const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
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);
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)));
1193 wxImage rotated(x2a - x1a + 1, y2a - y1a + 1,
false);
1195 if (has_alpha) rotated.SetAlpha();
1197 if (offset_after_rotation != NULL) {
1198 *offset_after_rotation = wxPoint(x1a, y1a);
1205 unsigned char *dst = rotated.GetData();
1207 unsigned char *alpha_dst = NULL;
1208 if (has_alpha) alpha_dst = rotated.GetAlpha();
1213 unsigned char blank_r = 0;
1214 unsigned char blank_g = 0;
1215 unsigned char blank_b = 0;
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);
1228 const int rH = rotated.GetHeight();
1229 const int rW = rotated.GetWidth();
1234 if (interpolating) {
1235 for (
int y = 0; y < rH; y++) {
1236 for (
int x = 0; x < rW; x++) {
1238 wxRotatePoint(x + x1a, y + y1a, cos_angle, -sin_angle, p0);
1240 if (-0.25 < src.x && src.x < w - 0.75 && -0.25 < src.y &&
1247 if (0 < src.x && src.x < w - 1) {
1248 x1 = wxRound(floor(src.x));
1249 x2 = wxRound(ceil(src.x));
1252 x1 = x2 = wxRound(src.x);
1255 if (0 < src.y && src.y < h - 1) {
1256 y1 = wxRound(floor(src.y));
1257 y2 = wxRound(ceil(src.y));
1259 y1 = y2 = wxRound(src.y);
1271 (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
1273 (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
1275 (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
1277 (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
1286 if (d1 < wxROTATE_EPSILON) {
1287 unsigned char *p = data[y1] + (3 * x1);
1292 if (has_alpha) *(alpha_dst++) = *(alpha[y1] + x1);
1293 }
else if (d2 < wxROTATE_EPSILON) {
1294 unsigned char *p = data[y1] + (3 * x2);
1299 if (has_alpha) *(alpha_dst++) = *(alpha[y1] + x2);
1300 }
else if (d3 < wxROTATE_EPSILON) {
1301 unsigned char *p = data[y2] + (3 * x2);
1306 if (has_alpha) *(alpha_dst++) = *(alpha[y2] + x2);
1307 }
else if (d4 < wxROTATE_EPSILON) {
1308 unsigned char *p = data[y2] + (3 * x1);
1313 if (has_alpha) *(alpha_dst++) = *(alpha[y2] + x1);
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);
1322 const double w1 = 1 / d1, w2 = 1 / d2, w3 = 1 / d3, w4 = 1 / d4;
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));
1333 (
unsigned char)((w1 * *v1 + w2 * *v2 + w3 * *v3 + w4 * *v4) /
1334 (w1 + w2 + w3 + w4));
1337 v1 = alpha[y1] + (x1);
1338 v2 = alpha[y1] + (x2);
1339 v3 = alpha[y2] + (x2);
1340 v4 = alpha[y2] + (x1);
1343 (
unsigned char)((w1 * *v1 + w2 * *v2 + w3 * *v3 + w4 * *v4) /
1344 (w1 + w2 + w3 + w4));
1352 if (has_alpha) *(alpha_dst++) = 0;
1360 double x1b = x1a - p0.x;
1361 double y1b = y1a - p0.y;
1362 double msa = -sin_angle;
1364 for (
int y = 0; y < rH; y++) {
1365 for (
int x = 0; x < rW; x++) {
1379 double sx = x0 + (x + x1b) * cos_angle - (y + y1b) * msa;
1380 double sy = y0 + (y + y1b) * cos_angle + (x + x1b) * msa;
1382 const int xs = (int)sx;
1383 const int ys = (int)sy;
1403 if (0 <= xs && xs < w && 0 <= ys && ys < h) {
1404 unsigned char *p = data[ys] + (3 * xs);
1409 if (has_alpha) *(alpha_dst++) = *(alpha[ys] + (xs));
1415 if (has_alpha) *(alpha_dst++) = 255;
1423 if (has_alpha)
delete[] alpha;