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)
457ocpnBitmap::ocpnBitmap() {}
465#ifdef opcnUSE_GTK_OPTIMIZE
471bool ocpnBitmap::CreateFromData(
void *pPix,
int width,
int height,
int depth) {
532 Create(width, height, 32);
540 GdkPixbuf *pixbuf = GetPixbuf();
542 if (!pixbuf)
return false;
544 unsigned char *in = (
unsigned char *)pPix;
545 unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
547 int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
549 for (
int y = 0; y < height; y++, out += rowpad) {
554 for (
int x = 0; x < width; x++, out += 4, in += 4) {
609bool ocpnBitmap::CreateFromocpnXImage(ocpnXImage *poXI,
int width,
int height,
612 Create(width, height, -1);
614 Display *xdisplay = (Display *)GetDisplay();
616 XImage *data_image = poXI->m_img;
617 bool bShared = poXI->buse_mit;
621 Pixmap mypixmap = ((Pixmap)GetPixmap());
623 GC gc = XCreateGC(xdisplay, mypixmap, 0, NULL);
626 XShmPutImage(xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height,
629 XPutImage(xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height);
631 XFreeGC(xdisplay, gc);
640bool ocpnBitmap::CreateFromData(
void *pPix,
int width,
int height,
int depth) {
644 Create(width, height, -1);
646 Display *xdisplay = (Display *)GetDisplay();
648 ocpnXImage *pXI =
new ocpnXImage(width, height);
653 if ((pPix != NULL) && (NULL != img)) {
654 unsigned char *data = (
unsigned char *)pPix;
657 for (
int y = 0; y < height; y++) {
658 char *pd = img->data + (y * img->bytes_per_line);
659 unsigned char *ps = data + (y * width * 4);
660 memcpy(pd, ps, width * 4);
665 int *pi = (
int *)img->data;
667 for (
int y = 0; y < height; y++) {
668 for (
int x = 0; x < width; x++) {
669 int ri = *(
int *)(&data[index]);
683 Pixmap mypixmap = ((Pixmap)GetPixmap());
684 GC gc = XCreateGC(xdisplay, mypixmap, 0, NULL);
686 pXI->PutImage(mypixmap, gc);
690 XFreeGC(xdisplay, gc);
702bool ocpnBitmap::CreateFromData(
void *pPix,
int width,
int height,
int depth) {
703 m_refData = CreateData();
707 int height0 = height;
708 int sizeLimit = 1280 * 1024 * 3;
710 int bmpHeight = height0;
714 int bytePerLine = width * 3;
715 int sizeDWORD =
sizeof(DWORD);
716 int lineBoundary = bytePerLine % sizeDWORD;
718 if (lineBoundary > 0) {
719 padding = sizeDWORD - lineBoundary;
720 bytePerLine += padding;
725 SetHeight(bmpHeight);
726 if (depth == -1) depth = wxDisplayDepth();
730 int headersize =
sizeof(BITMAPINFOHEADER);
731 BITMAPINFO *lpDIBh = (BITMAPINFO *)malloc(headersize);
732 wxCHECK_MSG(lpDIBh, FALSE, wxT(
"could not allocate memory for DIB header"));
734 lpDIBh->bmiHeader.biSize = headersize;
735 lpDIBh->bmiHeader.biWidth = (DWORD)width;
736 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
737 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
740 lpDIBh->bmiHeader.biPlanes = 1;
741 lpDIBh->bmiHeader.biBitCount = 24;
742 lpDIBh->bmiHeader.biCompression = BI_RGB;
743 lpDIBh->bmiHeader.biClrUsed = 0;
745 lpDIBh->bmiHeader.biClrImportant = 0;
746 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
747 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
749 unsigned char *lpBits;
750 lpBits = (
unsigned char *)malloc(lpDIBh->bmiHeader.biSizeImage);
752 wxFAIL_MSG(wxT(
"could not allocate memory for DIB"));
758 HDC hdc = ::GetDC(NULL);
759 HDC memdc = ::CreateCompatibleDC(hdc);
769 unsigned char *data = (
unsigned char *)pPix;
771 unsigned char *ptdata = data;
772 unsigned char *ptbits;
777 for (j = 0; j < height; j++) {
778 memcpy(ptbits, ptdata, width * 3);
782 for (i = 0; i < padding; i++) *(ptbits++) = 0;
787 for (j = 0; j < height; j++) {
788 memset(ptbits, 0, width * 3);
791 for (i = 0; i < padding; i++) *(ptbits++) = 0;
795 hbitmap = CreateDIBitmap(hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh,
808 SetHBITMAP((WXHBITMAP)hbitmap);
812 ::ReleaseDC(NULL, hdc);
823bool ocpnBitmap::CreateFromImage(
const wxImage &image,
int depth) {
826 m_refData = CreateData();
829 int sizeLimit = 1280 * 1024 * 3;
832 int width = image.GetWidth();
833 int bmpHeight = image.GetHeight();
836 int bytePerLine = width * 3;
837 int sizeDWORD =
sizeof(DWORD);
838 int lineBoundary = bytePerLine % sizeDWORD;
840 if (lineBoundary > 0) {
841 padding = sizeDWORD - lineBoundary;
842 bytePerLine += padding;
847 int height = sizeLimit / bytePerLine;
848 if (height >= bmpHeight)
851 numDIB = bmpHeight / height;
852 hRemain = bmpHeight % height;
853 if (hRemain > 0) numDIB++;
857 wxCHECK_MSG(image.Ok(), FALSE, wxT(
"invalid image"));
859 SetHeight(bmpHeight);
860 if (depth == -1) depth = wxDisplayDepth();
865 SetPalette(image.GetPalette());
869 int headersize =
sizeof(BITMAPINFOHEADER);
870 BITMAPINFO *lpDIBh = (BITMAPINFO *)malloc(headersize);
871 wxCHECK_MSG(lpDIBh, FALSE, wxT(
"could not allocate memory for DIB header"));
873 lpDIBh->bmiHeader.biSize = headersize;
874 lpDIBh->bmiHeader.biWidth = (DWORD)width;
875 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
876 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
879 lpDIBh->bmiHeader.biPlanes = 1;
880 lpDIBh->bmiHeader.biBitCount = 24;
881 lpDIBh->bmiHeader.biCompression = BI_RGB;
882 lpDIBh->bmiHeader.biClrUsed = 0;
884 lpDIBh->bmiHeader.biClrImportant = 0;
885 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
886 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
888 unsigned char *lpBits;
889 lpBits = (
unsigned char *)malloc(lpDIBh->bmiHeader.biSizeImage);
891 wxFAIL_MSG(wxT(
"could not allocate memory for DIB"));
897 HDC hdc = ::GetDC(NULL);
898 HDC memdc = ::CreateCompatibleDC(hdc);
908 HPALETTE hOldPalette = 0;
909 if (image.GetPalette().Ok()) {
910 hOldPalette = ::SelectPalette(
911 memdc, (HPALETTE)image.GetPalette().GetHPALETTE(), FALSE);
912 ::RealizePalette(memdc);
917 unsigned char *data = image.GetData();
920 unsigned char *ptdata = data;
921 unsigned char *ptbits;
923 for (n = 0; n < numDIB; n++) {
924 if (numDIB > 1 && n == numDIB - 1 && hRemain > 0) {
928 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
929 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
933 for (j = 0; j < height; j++) {
934 memcpy(ptbits, ptdata, width * 3);
947 for (i = 0; i < padding; i++) *(ptbits++) = 0;
953 hbitmap = CreateDIBitmap(hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits,
954 lpDIBh, DIB_RGB_COLORS);
970 SetHBITMAP((WXHBITMAP)hbitmap);
976 if (hOldPalette) SelectPalette(memdc, hOldPalette, FALSE);
980 if (image.HasMask()) {
981 hbitmap = ::CreateBitmap((WORD)width, (WORD)bmpHeight, 1, 1, NULL);
982 HGDIOBJ hbmpOld = ::SelectObject(memdc, hbitmap);
986 height = sizeLimit / bytePerLine;
987 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
988 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
990 unsigned char r = image.GetMaskRed();
991 unsigned char g = image.GetMaskGreen();
992 unsigned char b = image.GetMaskBlue();
993 unsigned char zero = 0, one = 255;
995 for (n = 0; n < numDIB; n++) {
996 if (numDIB > 1 && n == numDIB - 1 && hRemain > 0) {
1000 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
1001 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
1004 for (
int j = 0; j < height; j++) {
1005 for (i = 0; i < width; i++) {
1008 unsigned char cr = (*(ptdata++));
1009 unsigned char cg = (*(ptdata++));
1010 unsigned char cb = (*(ptdata++));
1012 if ((cr != r) || (cg != g) || (cb != b)) {
1022 for (i = 0; i < padding; i++) *(ptbits++) = zero;
1024 ::StretchDIBits(memdc, 0, origin, width, height, 0, 0, width, height,
1025 lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
1029 wxMask *mask =
new wxMask();
1030 mask->SetMaskBitmap((WXHBITMAP)hbitmap);
1039 ::SelectObject(memdc, hbmpOld);
1044 ::ReleaseDC(NULL, hdc);
1048#if WXWIN_COMPATIBILITY_2
1050 GetBitmapData()->SetOk();
1064ocpnMemDC::ocpnMemDC() {}
1066#ifdef ocpnUSE_DIBSECTION
1067void ocpnMemDC::SelectObject(wxDIB &dib) {
1070 ::SelectObject(GetHdc(), (HBITMAP)m_oldBitmap);
1071 if (m_selectedBitmap.Ok()) {
1075 m_selectedBitmap = wxNullBitmap;
1091 m_pselectedDIB = &dib;
1092 HBITMAP hDIB = m_pselectedDIB->GetHandle();
1101 hDIB = (HBITMAP)::SelectObject(GetHdc(), hDIB);
1104 wxLogLastError(wxT(
"SelectObject(ocpnMemDC, DIB)"));
1106 wxFAIL_MSG(wxT(
"Couldn't select a DIB into ocpnMemDC"));
1109 else if (!m_oldBitmap) {
1121static const double wxROTATE_EPSILON = 1e-10;
1129static inline wxRealPoint wxRotatePoint(
const wxRealPoint &p,
double cos_angle,
1131 const wxRealPoint &p0) {
1133 p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
1134 p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
1137static inline wxRealPoint wxRotatePoint(
double x,
double y,
double cos_angle,
1139 const wxRealPoint &p0) {
1140 return wxRotatePoint(wxRealPoint(x, y), cos_angle, sin_angle, p0);
1143wxImage Image_Rotate(wxImage &base_image,
double angle,
1144 const wxPoint ¢re_of_rotation,
bool interpolating,
1145 wxPoint *offset_after_rotation) {
1150 bool has_alpha = base_image.HasAlpha();
1152 const int w = base_image.GetWidth(), h = base_image.GetHeight();
1155 unsigned char **data =
new unsigned char *[h];
1156 data[0] = base_image.GetData();
1157 for (i = 1; i < h; i++) data[i] = data[i - 1] + (3 * w);
1160 unsigned char **alpha = NULL;
1162 alpha =
new unsigned char *[h];
1163 alpha[0] = base_image.GetAlpha();
1164 for (i = 1; i < h; i++) alpha[i] = alpha[i - 1] + w;
1169 const double cos_angle = cos(angle);
1170 const double sin_angle = sin(angle);
1176 const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
1178 wxRealPoint p1 = wxRotatePoint(0, 0, cos_angle, sin_angle, p0);
1179 wxRealPoint p2 = wxRotatePoint(0, h, cos_angle, sin_angle, p0);
1180 wxRealPoint p3 = wxRotatePoint(w, 0, cos_angle, sin_angle, p0);
1181 wxRealPoint p4 = wxRotatePoint(w, h, cos_angle, sin_angle, p0);
1183 int x1a = (int)floor(wxMin(wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
1184 int y1a = (int)floor(wxMin(wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
1185 int x2a = (int)ceil(wxMax(wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
1186 int y2a = (int)ceil(wxMax(wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));
1189 wxImage rotated(x2a - x1a + 1, y2a - y1a + 1,
false);
1191 if (has_alpha) rotated.SetAlpha();
1193 if (offset_after_rotation != NULL) {
1194 *offset_after_rotation = wxPoint(x1a, y1a);
1201 unsigned char *dst = rotated.GetData();
1203 unsigned char *alpha_dst = NULL;
1204 if (has_alpha) alpha_dst = rotated.GetAlpha();
1209 unsigned char blank_r = 0;
1210 unsigned char blank_g = 0;
1211 unsigned char blank_b = 0;
1213 if (base_image.HasMask()) {
1214 blank_r = base_image.GetMaskRed();
1215 blank_g = base_image.GetMaskGreen();
1216 blank_b = base_image.GetMaskBlue();
1217 rotated.SetMaskColour(blank_r, blank_g, blank_b);
1224 const int rH = rotated.GetHeight();
1225 const int rW = rotated.GetWidth();
1230 if (interpolating) {
1231 for (
int y = 0; y < rH; y++) {
1232 for (
int x = 0; x < rW; x++) {
1234 wxRotatePoint(x + x1a, y + y1a, cos_angle, -sin_angle, p0);
1236 if (-0.25 < src.x && src.x < w - 0.75 && -0.25 < src.y &&
1243 if (0 < src.x && src.x < w - 1) {
1244 x1 = wxRound(floor(src.x));
1245 x2 = wxRound(ceil(src.x));
1248 x1 = x2 = wxRound(src.x);
1251 if (0 < src.y && src.y < h - 1) {
1252 y1 = wxRound(floor(src.y));
1253 y2 = wxRound(ceil(src.y));
1255 y1 = y2 = wxRound(src.y);
1267 (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
1269 (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
1271 (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
1273 (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
1282 if (d1 < wxROTATE_EPSILON) {
1283 unsigned char *p = data[y1] + (3 * x1);
1288 if (has_alpha) *(alpha_dst++) = *(alpha[y1] + x1);
1289 }
else if (d2 < wxROTATE_EPSILON) {
1290 unsigned char *p = data[y1] + (3 * x2);
1295 if (has_alpha) *(alpha_dst++) = *(alpha[y1] + x2);
1296 }
else if (d3 < wxROTATE_EPSILON) {
1297 unsigned char *p = data[y2] + (3 * x2);
1302 if (has_alpha) *(alpha_dst++) = *(alpha[y2] + x2);
1303 }
else if (d4 < wxROTATE_EPSILON) {
1304 unsigned char *p = data[y2] + (3 * x1);
1309 if (has_alpha) *(alpha_dst++) = *(alpha[y2] + x1);
1313 unsigned char *v1 = data[y1] + (3 * x1);
1314 unsigned char *v2 = data[y1] + (3 * x2);
1315 unsigned char *v3 = data[y2] + (3 * x2);
1316 unsigned char *v4 = data[y2] + (3 * x1);
1318 const double w1 = 1 / d1, w2 = 1 / d2, w3 = 1 / d3, w4 = 1 / d4;
1322 *(dst++) = (
unsigned char)((w1 * *(v1++) + w2 * *(v2++) +
1323 w3 * *(v3++) + w4 * *(v4++)) /
1324 (w1 + w2 + w3 + w4));
1325 *(dst++) = (
unsigned char)((w1 * *(v1++) + w2 * *(v2++) +
1326 w3 * *(v3++) + w4 * *(v4++)) /
1327 (w1 + w2 + w3 + w4));
1329 (
unsigned char)((w1 * *v1 + w2 * *v2 + w3 * *v3 + w4 * *v4) /
1330 (w1 + w2 + w3 + w4));
1333 v1 = alpha[y1] + (x1);
1334 v2 = alpha[y1] + (x2);
1335 v3 = alpha[y2] + (x2);
1336 v4 = alpha[y2] + (x1);
1339 (
unsigned char)((w1 * *v1 + w2 * *v2 + w3 * *v3 + w4 * *v4) /
1340 (w1 + w2 + w3 + w4));
1348 if (has_alpha) *(alpha_dst++) = 0;
1356 double x1b = x1a - p0.x;
1357 double y1b = y1a - p0.y;
1358 double msa = -sin_angle;
1360 for (
int y = 0; y < rH; y++) {
1361 for (
int x = 0; x < rW; x++) {
1375 double sx = x0 + (x + x1b) * cos_angle - (y + y1b) * msa;
1376 double sy = y0 + (y + y1b) * cos_angle + (x + x1b) * msa;
1378 const int xs = (int)sx;
1379 const int ys = (int)sy;
1399 if (0 <= xs && xs < w && 0 <= ys && ys < h) {
1400 unsigned char *p = data[ys] + (3 * xs);
1405 if (has_alpha) *(alpha_dst++) = *(alpha[ys] + (xs));
1411 if (has_alpha) *(alpha_dst++) = 255;
1419 if (has_alpha)
delete[] alpha;