1 // xImaSel.cpp : Selection functions
\r
2 /* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it
\r
3 * CxImage version 6.0.0 02/Feb/2008
\r
8 #if CXIMAGE_SUPPORT_SELECTION
\r
10 ////////////////////////////////////////////////////////////////////////////////
\r
12 * Checks if the image has a valid selection.
\r
14 bool CxImage::SelectionIsValid()
\r
16 return pSelection!=0;
\r
18 ////////////////////////////////////////////////////////////////////////////////
\r
20 * Gets the smallest rectangle that contains the selection
\r
22 void CxImage::SelectionGetBox(RECT& r)
\r
24 memcpy(&r,&info.rSelectionBox,sizeof(RECT));
\r
26 ////////////////////////////////////////////////////////////////////////////////
\r
28 * Empties the selection.
\r
30 bool CxImage::SelectionClear(BYTE level)
\r
34 memset(pSelection,0,head.biWidth * head.biHeight);
\r
35 info.rSelectionBox.left = head.biWidth;
\r
36 info.rSelectionBox.bottom = head.biHeight;
\r
37 info.rSelectionBox.right = info.rSelectionBox.top = 0;
\r
39 memset(pSelection,level,head.biWidth * head.biHeight);
\r
40 info.rSelectionBox.right = head.biWidth;
\r
41 info.rSelectionBox.top = head.biHeight;
\r
42 info.rSelectionBox.left = info.rSelectionBox.bottom = 0;
\r
48 ////////////////////////////////////////////////////////////////////////////////
\r
50 * Allocates an empty selection.
\r
52 bool CxImage::SelectionCreate()
\r
55 pSelection = (BYTE*)calloc(head.biWidth * head.biHeight, 1);
\r
56 return (pSelection!=0);
\r
58 ////////////////////////////////////////////////////////////////////////////////
\r
60 * Deallocates the selction.
\r
62 bool CxImage::SelectionDelete()
\r
68 info.rSelectionBox.left = head.biWidth;
\r
69 info.rSelectionBox.bottom = head.biHeight;
\r
70 info.rSelectionBox.right = info.rSelectionBox.top = 0;
\r
73 ////////////////////////////////////////////////////////////////////////////////
\r
75 * Checks if the coordinates are inside the selection.
\r
77 bool CxImage::SelectionIsInside(long x, long y)
\r
80 if (pSelection==NULL) return true;
\r
81 return pSelection[x+y*head.biWidth]!=0;
\r
85 ////////////////////////////////////////////////////////////////////////////////
\r
87 * Checks if the coordinates are inside the selection.
\r
88 * "blind" version assumes that (x,y) is inside to the image.
\r
90 bool CxImage::BlindSelectionIsInside(long x, long y)
\r
94 #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING
\r
100 if (pSelection==NULL) return true;
\r
101 return pSelection[x+y*head.biWidth]!=0;
\r
103 ////////////////////////////////////////////////////////////////////////////////
\r
105 * Adds a rectangle to the existing selection.
\r
107 bool CxImage::SelectionAddRect(RECT r, BYTE level)
\r
109 if (pSelection==NULL) SelectionCreate();
\r
110 if (pSelection==NULL) return false;
\r
113 if (r.left<r.right) {r2.left=r.left; r2.right=r.right; } else {r2.left=r.right ; r2.right=r.left; }
\r
114 if (r.bottom<r.top) {r2.bottom=r.bottom; r2.top=r.top; } else {r2.bottom=r.top ; r2.top=r.bottom; }
\r
116 if (info.rSelectionBox.top <= r2.top) info.rSelectionBox.top = max(0L,min(head.biHeight,r2.top+1));
\r
117 if (info.rSelectionBox.left > r2.left) info.rSelectionBox.left = max(0L,min(head.biWidth,r2.left));
\r
118 if (info.rSelectionBox.right <= r2.right) info.rSelectionBox.right = max(0L,min(head.biWidth,r2.right+1));
\r
119 if (info.rSelectionBox.bottom > r2.bottom) info.rSelectionBox.bottom = max(0L,min(head.biHeight,r2.bottom));
\r
121 long ymin = max(0L,min(head.biHeight,r2.bottom));
\r
122 long ymax = max(0L,min(head.biHeight,r2.top+1));
\r
123 long xmin = max(0L,min(head.biWidth,r2.left));
\r
124 long xmax = max(0L,min(head.biWidth,r2.right+1));
\r
126 for (long y=ymin; y<ymax; y++)
\r
127 memset(pSelection + xmin + y * head.biWidth, level, xmax-xmin);
\r
131 ////////////////////////////////////////////////////////////////////////////////
\r
133 * Adds an ellipse to the existing selection.
\r
135 bool CxImage::SelectionAddEllipse(RECT r, BYTE level)
\r
137 if (pSelection==NULL) SelectionCreate();
\r
138 if (pSelection==NULL) return false;
\r
140 long xradius = abs(r.right - r.left)/2;
\r
141 long yradius = abs(r.top - r.bottom)/2;
\r
142 if (xradius==0 || yradius==0) return false;
\r
144 long xcenter = (r.right + r.left)/2;
\r
145 long ycenter = (r.top + r.bottom)/2;
\r
147 if (info.rSelectionBox.left > (xcenter - xradius)) info.rSelectionBox.left = max(0L,min(head.biWidth,(xcenter - xradius)));
\r
148 if (info.rSelectionBox.right <= (xcenter + xradius)) info.rSelectionBox.right = max(0L,min(head.biWidth,(xcenter + xradius + 1)));
\r
149 if (info.rSelectionBox.bottom > (ycenter - yradius)) info.rSelectionBox.bottom = max(0L,min(head.biHeight,(ycenter - yradius)));
\r
150 if (info.rSelectionBox.top <= (ycenter + yradius)) info.rSelectionBox.top = max(0L,min(head.biHeight,(ycenter + yradius + 1)));
\r
152 long xmin = max(0L,min(head.biWidth,xcenter - xradius));
\r
153 long xmax = max(0L,min(head.biWidth,xcenter + xradius + 1));
\r
154 long ymin = max(0L,min(head.biHeight,ycenter - yradius));
\r
155 long ymax = max(0L,min(head.biHeight,ycenter + yradius + 1));
\r
158 for (y=ymin; y<min(ycenter,ymax); y++){
\r
159 for (long x=xmin; x<xmax; x++){
\r
160 yo = (long)(ycenter - yradius * sqrt(1-pow((float)(x - xcenter)/(float)xradius,2)));
\r
161 if (yo<y) pSelection[x + y * head.biWidth] = level;
\r
164 for (y=ycenter; y<ymax; y++){
\r
165 for (long x=xmin; x<xmax; x++){
\r
166 yo = (long)(ycenter + yradius * sqrt(1-pow((float)(x - xcenter)/(float)xradius,2)));
\r
167 if (yo>y) pSelection[x + y * head.biWidth] = level;
\r
172 ////////////////////////////////////////////////////////////////////////////////
\r
174 * Inverts the selection.
\r
175 * Note: the SelectionBox is set to "full image", call SelectionGetBox before (if necessary)
\r
177 bool CxImage::SelectionInvert()
\r
180 BYTE *iSrc=pSelection;
\r
181 long n=head.biHeight*head.biWidth;
\r
182 for(long i=0; i < n; i++){
\r
183 *iSrc=(BYTE)~(*(iSrc));
\r
187 SelectionRebuildBox();
\r
193 ////////////////////////////////////////////////////////////////////////////////
\r
195 * Imports an existing region from another image with the same width and height.
\r
197 bool CxImage::SelectionCopy(CxImage &from)
\r
199 if (from.pSelection == NULL || head.biWidth != from.head.biWidth || head.biHeight != from.head.biHeight) return false;
\r
200 if (pSelection==NULL) pSelection = (BYTE*)malloc(head.biWidth * head.biHeight);
\r
201 if (pSelection==NULL) return false;
\r
202 memcpy(pSelection,from.pSelection,head.biWidth * head.biHeight);
\r
203 memcpy(&info.rSelectionBox,&from.info.rSelectionBox,sizeof(RECT));
\r
206 ////////////////////////////////////////////////////////////////////////////////
\r
208 * Adds a polygonal region to the existing selection. points points to an array of POINT structures.
\r
209 * Each structure specifies the x-coordinate and y-coordinate of one vertex of the polygon.
\r
210 * npoints specifies the number of POINT structures in the array pointed to by points.
\r
212 bool CxImage::SelectionAddPolygon(POINT *points, long npoints, BYTE level)
\r
214 if (points==NULL || npoints<3) return false;
\r
216 if (pSelection==NULL) SelectionCreate();
\r
217 if (pSelection==NULL) return false;
\r
219 BYTE* plocal = (BYTE*)calloc(head.biWidth*head.biHeight, 1);
\r
220 RECT localbox = {head.biWidth,0,0,head.biHeight};
\r
224 POINT *next = NULL;
\r
225 POINT *start = NULL;
\r
227 while (i < npoints){
\r
228 current = &points[i];
\r
229 if (current->x!=-1){
\r
230 if (i==0 || (i>0 && points[i-1].x==-1)) start = &points[i];
\r
232 if ((i+1)==npoints || points[i+1].x==-1)
\r
235 next = &points[i+1];
\r
238 if (current->x != next->x){
\r
239 beta = (float)(next->y - current->y)/(float)(next->x - current->x);
\r
240 if (current->x < next->x){
\r
241 for (x=current->x; x<=next->x; x++){
\r
242 y = (long)(current->y + (x - current->x) * beta);
\r
243 if (IsInside(x,y)) plocal[x + y * head.biWidth] = 255;
\r
246 for (x=current->x; x>=next->x; x--){
\r
247 y = (long)(current->y + (x - current->x) * beta);
\r
248 if (IsInside(x,y)) plocal[x + y * head.biWidth] = 255;
\r
252 if (current->y != next->y){
\r
253 beta = (float)(next->x - current->x)/(float)(next->y - current->y);
\r
254 if (current->y < next->y){
\r
255 for (y=current->y; y<=next->y; y++){
\r
256 x = (long)(current->x + (y - current->y) * beta);
\r
257 if (IsInside(x,y)) plocal[x + y * head.biWidth] = 255;
\r
260 for (y=current->y; y>=next->y; y--){
\r
261 x = (long)(current->x + (y - current->y) * beta);
\r
262 if (IsInside(x,y)) plocal[x + y * head.biWidth] = 255;
\r
269 if (current->x < next->x) {r2.left=current->x; r2.right=next->x; } else {r2.left=next->x ; r2.right=current->x; }
\r
270 if (current->y < next->y) {r2.bottom=current->y; r2.top=next->y; } else {r2.bottom=next->y ; r2.top=current->y; }
\r
271 if (localbox.top < r2.top) localbox.top = max(0L,min(head.biHeight-1,r2.top+1));
\r
272 if (localbox.left > r2.left) localbox.left = max(0L,min(head.biWidth-1,r2.left-1));
\r
273 if (localbox.right < r2.right) localbox.right = max(0L,min(head.biWidth-1,r2.right+1));
\r
274 if (localbox.bottom > r2.bottom) localbox.bottom = max(0L,min(head.biHeight-1,r2.bottom-1));
\r
279 //fill the outer region
\r
280 long npix=(localbox.right - localbox.left)*(localbox.top - localbox.bottom);
\r
281 POINT* pix = (POINT*)calloc(npix,sizeof(POINT));
\r
282 BYTE back=0, mark=1;
\r
283 long fx, fy, fxx, fyy, first, last;
\r
289 for (int side=0; side<4; side++){
\r
292 xmin=localbox.left; xmax=localbox.right+1; ymin=localbox.bottom; ymax=localbox.bottom+1;
\r
295 xmin=localbox.right; xmax=localbox.right+1; ymin=localbox.bottom; ymax=localbox.top+1;
\r
298 xmin=localbox.left; xmax=localbox.right+1; ymin=localbox.top; ymax=localbox.top+1;
\r
301 xmin=localbox.left; xmax=localbox.left+1; ymin=localbox.bottom; ymax=localbox.top+1;
\r
304 //fill from the border points
\r
305 for(y=ymin;y<ymax;y++){
\r
306 for(x=xmin;x<xmax;x++){
\r
307 if (plocal[x+y*head.biWidth]==0){
\r
308 // Subject: FLOOD FILL ROUTINE Date: 12-23-97 (00:57)
\r
309 // Author: Petter Holmberg Code: QB, QBasic, PDS
\r
310 // Origin: petter.holmberg@usa.net Packet: GRAPHICS.ABC
\r
313 while(first!=last){
\r
320 if ((plocal[fxx + fyy*head.biWidth] == back) &&
\r
321 fxx>=localbox.left && fxx<=localbox.right && fyy>=localbox.bottom && fyy<=localbox.top )
\r
323 plocal[fxx + fyy*head.biWidth] = mark;
\r
324 if (fyy > 0 && plocal[fxx + (fyy - 1)*head.biWidth] == back){
\r
326 pix[last].y = fy - 1;
\r
328 if (last == npix) last = 0;
\r
330 if ((fyy + 1)<head.biHeight && plocal[fxx + (fyy + 1)*head.biWidth] == back){
\r
332 pix[last].y = fy + 1;
\r
334 if (last == npix) last = 0;
\r
343 fx = pix[first].x - 1;
\r
350 if ((plocal[fxx + fyy*head.biWidth] == back) &&
\r
351 fxx>=localbox.left && fxx<=localbox.right && fyy>=localbox.bottom && fyy<=localbox.top )
\r
353 plocal[fxx + (y + fy)*head.biWidth] = mark;
\r
354 if (fyy > 0 && plocal[fxx + (fyy - 1)*head.biWidth] == back){
\r
356 pix[last].y = fy - 1;
\r
358 if (last == npix) last = 0;
\r
360 if ((fyy + 1)<head.biHeight && plocal[fxx + (fyy + 1)*head.biWidth] == back){
\r
362 pix[last].y = fy + 1;
\r
364 if (last == npix) last = 0;
\r
374 if (first == npix) first = 0;
\r
381 //transfer the region
\r
383 for (y=localbox.bottom; y<=localbox.top; y++){
\r
384 yoffset = y * head.biWidth;
\r
385 for (x=localbox.left; x<=localbox.right; x++)
\r
386 if (plocal[x + yoffset]!=1) pSelection[x + yoffset]=level;
\r
388 if (info.rSelectionBox.top <= localbox.top) info.rSelectionBox.top = min(head.biHeight,localbox.top + 1);
\r
389 if (info.rSelectionBox.left > localbox.left) info.rSelectionBox.left = min(head.biWidth,localbox.left);
\r
390 if (info.rSelectionBox.right <= localbox.right) info.rSelectionBox.right = min(head.biWidth,localbox.right + 1);
\r
391 if (info.rSelectionBox.bottom > localbox.bottom) info.rSelectionBox.bottom = min(head.biHeight,localbox.bottom);
\r
398 ////////////////////////////////////////////////////////////////////////////////
\r
400 * Adds to the selection all the pixels matching the specified color.
\r
402 bool CxImage::SelectionAddColor(RGBQUAD c, BYTE level)
\r
404 if (pSelection==NULL) SelectionCreate();
\r
405 if (pSelection==NULL) return false;
\r
407 RECT localbox = {head.biWidth,0,0,head.biHeight};
\r
409 for (long y = 0; y < head.biHeight; y++){
\r
410 for (long x = 0; x < head.biWidth; x++){
\r
411 RGBQUAD color = BlindGetPixelColor(x, y);
\r
412 if (color.rgbRed == c.rgbRed &&
\r
413 color.rgbGreen == c.rgbGreen &&
\r
414 color.rgbBlue == c.rgbBlue)
\r
416 pSelection[x + y * head.biWidth] = level;
\r
418 if (localbox.top < y) localbox.top = y;
\r
419 if (localbox.left > x) localbox.left = x;
\r
420 if (localbox.right < x) localbox.right = x;
\r
421 if (localbox.bottom > y) localbox.bottom = y;
\r
426 if (info.rSelectionBox.top <= localbox.top) info.rSelectionBox.top = localbox.top + 1;
\r
427 if (info.rSelectionBox.left > localbox.left) info.rSelectionBox.left = localbox.left;
\r
428 if (info.rSelectionBox.right <= localbox.right) info.rSelectionBox.right = localbox.right + 1;
\r
429 if (info.rSelectionBox.bottom > localbox.bottom) info.rSelectionBox.bottom = localbox.bottom;
\r
433 ////////////////////////////////////////////////////////////////////////////////
\r
435 * Adds a single pixel to the existing selection.
\r
437 bool CxImage::SelectionAddPixel(long x, long y, BYTE level)
\r
439 if (pSelection==NULL) SelectionCreate();
\r
440 if (pSelection==NULL) return false;
\r
442 if (IsInside(x,y)) {
\r
443 pSelection[x + y * head.biWidth] = level; // set the correct mask bit
\r
445 if (info.rSelectionBox.top <= y) info.rSelectionBox.top = y+1;
\r
446 if (info.rSelectionBox.left > x) info.rSelectionBox.left = x;
\r
447 if (info.rSelectionBox.right <= x) info.rSelectionBox.right = x+1;
\r
448 if (info.rSelectionBox.bottom > y) info.rSelectionBox.bottom = y;
\r
455 ////////////////////////////////////////////////////////////////////////////////
\r
457 * Exports the selection channel in a 8bpp grayscale image.
\r
459 bool CxImage::SelectionSplit(CxImage *dest)
\r
461 if (!pSelection || !dest) return false;
\r
463 CxImage tmp(head.biWidth,head.biHeight,8);
\r
464 if (!tmp.IsValid()){
\r
465 strcpy(info.szLastError,tmp.GetLastError());
\r
469 for(long y=0; y<head.biHeight; y++){
\r
470 for(long x=0; x<head.biWidth; x++){
\r
471 tmp.BlindSetPixelIndex(x,y,pSelection[x+y*head.biWidth]);
\r
475 tmp.SetGrayPalette();
\r
476 dest->Transfer(tmp);
\r
480 ////////////////////////////////////////////////////////////////////////////////
\r
482 * Creates the selection channel from a gray scale image.
\r
483 * black = unselected
\r
485 bool CxImage::SelectionSet(CxImage &from)
\r
487 if (!from.IsGrayScale() || head.biWidth != from.head.biWidth || head.biHeight != from.head.biHeight){
\r
488 strcpy(info.szLastError,"CxImage::SelectionSet: wrong width or height, or image is not gray scale");
\r
492 if (pSelection==NULL) pSelection = (BYTE*)malloc(head.biWidth * head.biHeight);
\r
494 BYTE* src = from.info.pImage;
\r
495 BYTE* dst = pSelection;
\r
496 if (src==NULL || dst==NULL){
\r
497 strcpy(info.szLastError,"CxImage::SelectionSet: null pointer");
\r
501 for (long y=0; y<head.biHeight; y++){
\r
502 memcpy(dst,src,head.biWidth);
\r
503 dst += head.biWidth;
\r
504 src += from.info.dwEffWidth;
\r
507 SelectionRebuildBox();
\r
511 ////////////////////////////////////////////////////////////////////////////////
\r
513 * Sets the Selection level for a single pixel
\r
514 * internal use only: doesn't set SelectionBox. Use SelectionAddPixel
\r
516 void CxImage::SelectionSet(const long x,const long y,const BYTE level)
\r
518 if (pSelection && IsInside(x,y)) pSelection[x+y*head.biWidth]=level;
\r
520 ////////////////////////////////////////////////////////////////////////////////
\r
522 * Gets the Selection level for a single pixel
\r
524 BYTE CxImage::SelectionGet(const long x,const long y)
\r
526 if (pSelection && IsInside(x,y)) return pSelection[x+y*head.biWidth];
\r
529 ////////////////////////////////////////////////////////////////////////////////
\r
531 * Rebuilds the SelectionBox
\r
533 void CxImage::SelectionRebuildBox()
\r
535 info.rSelectionBox.left = head.biWidth;
\r
536 info.rSelectionBox.bottom = head.biHeight;
\r
537 info.rSelectionBox.right = info.rSelectionBox.top = 0;
\r
544 for (y=0; y<head.biHeight; y++){
\r
545 for (x=0; x<info.rSelectionBox.left; x++){
\r
546 if (pSelection[x+y*head.biWidth]){
\r
547 info.rSelectionBox.left = x;
\r
553 for (y=0; y<head.biHeight; y++){
\r
554 for (x=head.biWidth-1; x>=info.rSelectionBox.right; x--){
\r
555 if (pSelection[x+y*head.biWidth]){
\r
556 info.rSelectionBox.right = x+1;
\r
562 for (x=0; x<head.biWidth; x++){
\r
563 for (y=0; y<info.rSelectionBox.bottom; y++){
\r
564 if (pSelection[x+y*head.biWidth]){
\r
565 info.rSelectionBox.bottom = y;
\r
571 for (x=0; x<head.biWidth; x++){
\r
572 for (y=head.biHeight-1; y>=info.rSelectionBox.top; y--){
\r
573 if (pSelection[x+y*head.biWidth]){
\r
574 info.rSelectionBox.top = y+1;
\r
581 ////////////////////////////////////////////////////////////////////////////////
\r
583 * Gets the Selection level for a single pixel
\r
584 * "blind" version assumes that (x,y) is inside to the image.
\r
586 BYTE CxImage::BlindSelectionGet(const long x,const long y)
\r
589 if (!IsInside(x,y) || (pSelection==0))
\r
590 #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING
\r
596 return pSelection[x+y*head.biWidth];
\r
598 ////////////////////////////////////////////////////////////////////////////////
\r
600 * Returns pointer to selection data for pixel (x,y).
\r
602 BYTE* CxImage::SelectionGetPointer(const long x,const long y)
\r
604 if (pSelection && IsInside(x,y)) return pSelection+x+y*head.biWidth;
\r
607 ////////////////////////////////////////////////////////////////////////////////
\r
608 bool CxImage::SelectionFlip()
\r
610 if (!pSelection) return false;
\r
612 BYTE *buff = (BYTE*)malloc(head.biWidth);
\r
613 if (!buff) return false;
\r
616 iSrc = pSelection + (head.biHeight-1)*head.biWidth;
\r
618 for (long i=0; i<(head.biHeight/2); ++i)
\r
620 memcpy(buff, iSrc, head.biWidth);
\r
621 memcpy(iSrc, iDst, head.biWidth);
\r
622 memcpy(iDst, buff, head.biWidth);
\r
623 iSrc-=head.biWidth;
\r
624 iDst+=head.biWidth;
\r
629 long top = info.rSelectionBox.top;
\r
630 info.rSelectionBox.top = head.biHeight - info.rSelectionBox.bottom;
\r
631 info.rSelectionBox.bottom = head.biHeight - top;
\r
634 ////////////////////////////////////////////////////////////////////////////////
\r
635 bool CxImage::SelectionMirror()
\r
637 if (!pSelection) return false;
\r
638 BYTE* pSelection2 = (BYTE*)malloc(head.biWidth * head.biHeight);
\r
639 if (!pSelection2) return false;
\r
642 long wdt=head.biWidth-1;
\r
643 iSrc=pSelection + wdt;
\r
645 for(long y=0; y < head.biHeight; y++){
\r
646 for(long x=0; x <= wdt; x++)
\r
647 *(iDst+x)=*(iSrc-x);
\r
648 iSrc+=head.biWidth;
\r
649 iDst+=head.biWidth;
\r
652 pSelection=pSelection2;
\r
654 long left = info.rSelectionBox.left;
\r
655 info.rSelectionBox.left = head.biWidth - info.rSelectionBox.right;
\r
656 info.rSelectionBox.right = head.biWidth - left;
\r
659 ////////////////////////////////////////////////////////////////////////////////
\r
660 #if CXIMAGE_SUPPORT_WINDOWS
\r
662 * Converts the selection in a HRGN object.
\r
664 bool CxImage::SelectionToHRGN(HRGN& region)
\r
666 if (pSelection && region){
\r
667 for(int y = 0; y < head.biHeight; y++){
\r
671 for(; x < head.biWidth; x++){
\r
672 if (pSelection[x + y * head.biWidth] != 0){
\r
673 if (iStart == -1) iStart = x;
\r
677 hTemp = CreateRectRgn(iStart, y, x, y + 1);
\r
678 CombineRgn(region, hTemp, region, RGN_OR);
\r
679 DeleteObject(hTemp);
\r
685 hTemp = CreateRectRgn(iStart, y, x, y + 1);
\r
686 CombineRgn(region, hTemp, region, RGN_OR);
\r
687 DeleteObject(hTemp);
\r
695 #endif //CXIMAGE_SUPPORT_WINDOWS
\r
696 ////////////////////////////////////////////////////////////////////////////////
\r
697 #endif //CXIMAGE_SUPPORT_SELECTION
\r