1 /*
2  * Author:      William Chia-Wei Cheng (bill.cheng@acm.org)
3  *
4  * Copyright (C) 2001-2009, William Chia-Wei Cheng.
5  *
6  * This file may be distributed under the terms of the Q Public License
7  * as defined by Trolltech AS of Norway and appearing in the file
8  * LICENSE.QPL included in the packaging of this file.
9  *
10  * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
11  * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
12  * PURPOSE.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
13  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
14  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
16  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * @(#)$Header: /mm2/home/cvs/bc-src/tgif/imgproc.c,v 1.82 2011/05/25 16:54:27 cvsps Exp $
19  */
20 
21 #define _INCLUDE_FROM_IMGPROC_C_
22 
23 #include "tgifdefs.h"
24 #include "cmdids.h"
25 
26 #include "auxtext.e"
27 #include "choice.e"
28 #include "cmd.e"
29 #include "color.e"
30 #include "cursor.e"
31 #include "dialog.e"
32 #include "drawing.e"
33 #include "dup.e"
34 #include "file.e"
35 #include "grid.e"
36 #include "hash.e"
37 #include "imgproc.e"
38 #include "list.e"
39 #include "mainloop.e"
40 #include "mainmenu.e"
41 #include "mark.e"
42 #include "menu.e"
43 #include "menuinfo.e"
44 #include "move.e"
45 #include "msg.e"
46 #include "names.e"
47 #include "navigate.e"
48 #include "obj.e"
49 #include "page.e"
50 #include "pngtrans.e"
51 #include "poly.e"
52 #include "polygon.e"
53 #include "raster.e"
54 #include "rect.e"
55 #include "remote.e"
56 #include "ruler.e"
57 #include "select.e"
58 #include "setup.e"
59 #include "special.e"
60 #include "strtbl.e"
61 #include "util.e"
62 #include "xbitmap.e"
63 #include "xpixmap.e"
64 #include "z_intrf.e"
65 
66 #define HISTOGRAMCOUNT(i) (gpHistogram[(i)].pixel)
67 #define HISTOGRAMRED(i) (gpHistogram[(i)].red)
68 #define HISTOGRAMGREEN(i) (gpHistogram[(i)].green)
69 #define HISTOGRAMBLUE(i) (gpHistogram[(i)].blue)
70 
71 #define MAXIMAGEPROCS 34
72 
73 int numImageProc=MAXIMAGEPROCS;
74 int gnInImageProc=FALSE;
75 int gnConvolving=FALSE;
76 int gnNumNewColorsInPixmapFile=0;
77 
78 char gszImageProcXPmFile[MAXPATHLENGTH+1];
79 
80 float gfVectorWarpSoftness=2.0;
81 
82 int threshFillReplaceEnabled=FALSE;
83 int fillReplaceRedThresh=15;
84 int fillReplaceGreenThresh=15;
85 int fillReplaceBlueThresh=15;
86 
87 static NLFN *gpImageMapColorFunc=NULL;
88 static NLFN *gpConvolveFunc=NULL;
89 static int gpConvolveCmdID=(-1);
90 static int gnCombining=FALSE;
91 
92 static int gnCombineW=0, gnCombineH=0;
93 
94 static char bggenToXpmCmd[MAXSTRING+1];
95 static char bggenToPpm6Cmd[MAXSTRING+1];
96 static char ppmquantCmd[MAXSTRING+1];
97 static char ppmFSquantCmd[MAXSTRING+1];
98 
99 static XColor gDefErrorDiffuseLevel;
100 
101 static struct ObjRec *gpFgObj=NULL, *gpBgObj=NULL, *gpAlphaObj=NULL;
102 static struct BBRec gTotalCombineBBox;
103 static struct BBRec gFgCombineBBox, gBgCombineBBox, gAlphaCombineBBox;
104 static XImage *gpFgImage=NULL, *gpBgImage=NULL, *gpAlphaImage=NULL;
105 static XImage *gpFgBitmapImage=NULL, *gpBgBitmapImage=NULL,
106       *gpAlphaBitmapImage=NULL;
107 
108 typedef struct tagConvExtraInfo {
109    FILE *fp; /* non-NULL only if (fullTrueColorMode && HasZlibSupport()) */
110    XColor my_bg_xcolor;
111    int alpha_combine;
112    /* if alpha_combine is TRUE, these are for the foreground object */
113    int image_w, image_h;
114    XImage *image, *bitmap_image;
115    XColor **xcolors;
116    /* if alpha_combine is not TRUE, everything below are not used */
117    int final_w, final_h;
118    int alpha_image_w, alpha_image_h, bg_image_w, bg_image_h;
119    XImage *alpha_image, *alpha_bitmap_image, *bg_image, *bg_bitmap_image;
120    XColor **alpha_xcolors, **bg_xcolors;
121 } ConvExtraInfo;
122 
123 static ConvExtraInfo gConvExtraInfo;
124 static TrueColorInfo gTrueColorInfo;
125 
126 typedef struct tagThreshFillReplaceInfo {
127    int use_thresholding;
128    /* for true color */
129    int min_r, max_r, min_g, max_g, min_b, max_b;
130    TrueColorInfo tci;
131    /* for non-true color */
132    TgHash hash_table;
133    int *within_threshold;
134 } ThreshFillReplaceInfo;
135 
136 static ThreshFillReplaceInfo gThreshFillReplaceInfo;
137 
138 static
ParseThreshFillReplaceSpec(spec)139 int ParseThreshFillReplaceSpec(spec)
140    char *spec;
141 {
142    char *psz=NULL, sz_spec_copy[MAXSTRING];
143    int red=0, green=0, blue=0;
144 
145    *sz_spec_copy = '\0';
146    UtilStrCpyN(sz_spec_copy, sizeof(sz_spec_copy), spec);
147    UtilTrimBlanks(sz_spec_copy);
148 
149    if ((psz=strtok(sz_spec_copy, " ,\t\n\r")) == NULL ||
150          sscanf(psz, "%d", &red) != 1) {
151       sprintf(gszMsgBox, TgLoadString(STID_ERR_PARSE_THRESH_COLORS), spec);
152       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
153       return FALSE;
154    }
155    if (red < 0) red = 0;
156    if (red > 255) red = 255;
157 
158    psz = strtok(NULL, " ,\t\n\r");
159    if (psz == NULL) {
160       fillReplaceRedThresh = fillReplaceGreenThresh = fillReplaceBlueThresh =
161             red;
162       return TRUE;
163    }
164    if (sscanf(psz, "%d", &green) != 1) {
165       sprintf(gszMsgBox, TgLoadString(STID_ERR_PARSE_THRESH_COLORS), spec);
166       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
167       return FALSE;
168    }
169    if (green < 0) green = 0;
170    if (green > 255) green = 255;
171 
172    psz = strtok(NULL, " ,\t\n\r");
173    if (psz == NULL || sscanf(psz, "%d", &blue) != 1) {
174       sprintf(gszMsgBox, TgLoadString(STID_ERR_PARSE_THRESH_COLORS), spec);
175       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
176       return FALSE;
177    }
178    if (blue < 0) blue = 0;
179    if (blue > 255) blue = 255;
180 
181    psz = strtok(NULL, " ,\t\n\r");
182    if (psz != NULL) {
183       sprintf(gszMsgBox, TgLoadString(STID_ERR_PARSE_THRESH_COLORS), spec);
184       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
185       return FALSE;
186    }
187    fillReplaceRedThresh = red;
188    fillReplaceGreenThresh = green;
189    fillReplaceBlueThresh = blue;
190 
191    return TRUE;
192 }
193 
194 static
ResetThreshFillReplaceInfo()195 void ResetThreshFillReplaceInfo()
196 {
197    if (threshFillReplaceEnabled) {
198       gThreshFillReplaceInfo.use_thresholding = TRUE;
199       if (fullTrueColorMode) {
200          SetupTrueColorInfo(&gThreshFillReplaceInfo.tci);
201       }
202    } else {
203       gThreshFillReplaceInfo.use_thresholding = FALSE;
204    }
205 }
206 
207 static
TrueColorPixelWithinRange(pixel,ptfri)208 int TrueColorPixelWithinRange(pixel, ptfri)
209    int pixel;
210    ThreshFillReplaceInfo *ptfri;
211 {
212    uint32_t pix=(uint32_t)pixel;
213    unsigned int r=0, g=0, b=0;
214    double dr=(double)0, dg=(double)0, db=(double)0;
215    TrueColorInfo *ptci=(&ptfri->tci);
216    XColor xcolor;
217 
218    r = (pix & ptci->r_mask) >> ptci->r_shift;
219    g = (pix & ptci->g_mask) >> ptci->g_shift;
220    b = (pix & ptci->b_mask) >> ptci->b_shift;
221    dr = ((double)r) / ptci->dr_maxval_div255;
222    dg = ((double)g) / ptci->dg_maxval_div255;
223    db = ((double)b) / ptci->db_maxval_div255;
224    xcolor.red = round(dr);
225    xcolor.green = round(dg);
226    xcolor.blue = round(db);
227    if (xcolor.red > 255) xcolor.red = 255;
228    if (xcolor.green > 255) xcolor.green = 255;
229    if (xcolor.blue > 255) xcolor.blue = 255;
230 
231    return (xcolor.red >= ptfri->min_r && xcolor.red <= ptfri->max_r &&
232          xcolor.green >= ptfri->min_g && xcolor.green <= ptfri->max_g &&
233          xcolor.blue >= ptfri->min_b && xcolor.blue <= ptfri->max_b);
234 }
235 
236 static
OtherPixelWithinRange(pixel,ptfri)237 int OtherPixelWithinRange(pixel, ptfri)
238    int pixel;
239    ThreshFillReplaceInfo *ptfri;
240 {
241    double dr=(double)0, dg=(double)0, db=(double)0;
242    XColor xcolor;
243    int global_color_index=(-1);
244 
245    if (!HashLookUpInt(&ptfri->hash_table, (char*)(&pixel), sizeof(int),
246          &global_color_index)) {
247       return FALSE;
248    }
249    if (global_color_index == (-1)) {
250       return FALSE;
251    }
252    memcpy(&xcolor, &tgifColors[global_color_index], sizeof(XColor));
253 
254    dr = ((double)xcolor.red) / ((double)maxRGB) * ((double)255);
255    dg = ((double)xcolor.green) / ((double)maxRGB) * ((double)255);
256    db = ((double)xcolor.blue) / ((double)maxRGB) * ((double)255);
257    xcolor.red = round(dr);
258    xcolor.green = round(dg);
259    xcolor.blue = round(db);
260    if (xcolor.red > 255) xcolor.red = 255;
261    if (xcolor.green > 255) xcolor.green = 255;
262    if (xcolor.blue > 255) xcolor.blue = 255;
263 
264    return (xcolor.red >= ptfri->min_r && xcolor.red <= ptfri->max_r &&
265          xcolor.green >= ptfri->min_g && xcolor.green <= ptfri->max_g &&
266          xcolor.blue >= ptfri->min_b && xcolor.blue <= ptfri->max_b);
267 }
268 
269 static
GetPixelRGB(ptfri,pixel,pxc_pixel)270 int GetPixelRGB(ptfri, pixel, pxc_pixel)
271    ThreshFillReplaceInfo *ptfri;
272    int pixel;
273    XColor *pxc_pixel;
274 {
275    if (fullTrueColorMode) {
276       uint32_t pix=(uint32_t)pixel;
277       unsigned int r=0, g=0, b=0;
278       double dr=(double)0, dg=(double)0, db=(double)0;
279       TrueColorInfo *ptci=(&ptfri->tci);
280       XColor xcolor;
281 
282       r = (pix & ptci->r_mask) >> ptci->r_shift;
283       g = (pix & ptci->g_mask) >> ptci->g_shift;
284       b = (pix & ptci->b_mask) >> ptci->b_shift;
285       dr = ((double)r) / ptci->dr_maxval_div255;
286       dg = ((double)g) / ptci->dg_maxval_div255;
287       db = ((double)b) / ptci->db_maxval_div255;
288       pxc_pixel->red = round(dr);
289       pxc_pixel->green = round(dg);
290       pxc_pixel->blue = round(db);
291       if (pxc_pixel->red > 255) pxc_pixel->red = 255;
292       if (pxc_pixel->green > 255) pxc_pixel->green = 255;
293       if (pxc_pixel->blue > 255) xcolor.blue = 255;
294    } else {
295       int i=0, global_color_index=(-1);
296 
297       for (i=0; i < maxColors; i++) {
298          if (pixel == colorPixels[i]) {
299             global_color_index = i;
300             break;
301          }
302       }
303       if (global_color_index != (-1)) {
304          double dr=(double)0, dg=(double)0, db=(double)0;
305 
306          memcpy(pxc_pixel, &tgifColors[global_color_index], sizeof(XColor));
307          dr = ((double)pxc_pixel->red) / ((double)maxRGB) * ((double)255);
308          dg = ((double)pxc_pixel->green) / ((double)maxRGB) * ((double)255);
309          db = ((double)pxc_pixel->blue) / ((double)maxRGB) * ((double)255);
310          pxc_pixel->red = round(dr);
311          pxc_pixel->green = round(dg);
312          pxc_pixel->blue = round(db);
313          if (pxc_pixel->red > 255) pxc_pixel->red = 255;
314          if (pxc_pixel->green > 255) pxc_pixel->green = 255;
315          if (pxc_pixel->blue > 255) pxc_pixel->blue = 255;
316       } else {
317 #ifdef _TGIF_DBG /* debug, do not translate */
318          fprintf(stderr,
319                "Canot find pixel indel for pixel=0x%08x in GetPixelRGB().\n",
320                pixel);
321 #endif /* _TGIF_DBG */
322          return FALSE;
323       }
324    }
325    return TRUE;
326 }
327 
328 static
SetupThreshFillReplaceInfo(ptfri,pixel,pxc_pixel)329 int SetupThreshFillReplaceInfo(ptfri, pixel, pxc_pixel)
330    ThreshFillReplaceInfo *ptfri;
331    int pixel;
332    XColor *pxc_pixel;
333 {
334    if (fullTrueColorMode) {
335       if (!GetPixelRGB(ptfri, pixel, pxc_pixel)) return FALSE;
336 
337       ptfri->min_r = (int)(pxc_pixel->red) - fillReplaceRedThresh;
338       ptfri->max_r = (int)(pxc_pixel->red) + fillReplaceRedThresh;
339       if (ptfri->min_r < 0) ptfri->min_r = 0;
340       if (ptfri->max_r > 255) ptfri->max_r = 255;
341 
342       ptfri->min_g = (int)(pxc_pixel->green) - fillReplaceGreenThresh;
343       ptfri->max_g = (int)(pxc_pixel->green) + fillReplaceGreenThresh;
344       if (ptfri->min_g < 0) ptfri->min_g = 0;
345       if (ptfri->max_g > 255) ptfri->max_g = 255;
346 
347       ptfri->min_b = (int)(pxc_pixel->blue) - fillReplaceBlueThresh;
348       ptfri->max_b = (int)(pxc_pixel->blue) + fillReplaceBlueThresh;
349       if (ptfri->min_b < 0) ptfri->min_b = 0;
350       if (ptfri->max_b > 255) ptfri->max_b = 255;
351    } else {
352       int i=0;
353       TgHash *pth=(&ptfri->hash_table);
354       int global_color_index=(-1);
355 
356       InitHash(pth, TG_HASH_SIZE_LARGE);
357       for (i=0; i < maxColors; i++) {
358          HashStoreInt(pth, (char*)(&colorPixels[i]), sizeof(int), i);
359       }
360       if (HashLookUpInt(pth, (char*)(&pixel), sizeof(int),
361             &global_color_index)) {
362          double dr=(double)0, dg=(double)0, db=(double)0;
363          XColor xcolor;
364 
365          ptfri->within_threshold = (int*)malloc(maxColors*sizeof(int));
366          if (ptfri->within_threshold == NULL) FailAllocMessage();
367          memset(ptfri->within_threshold, 0, maxColors*sizeof(int));
368 
369          memcpy(&xcolor, &tgifColors[global_color_index], sizeof(XColor));
370          dr = ((double)xcolor.red) / ((double)maxRGB) * ((double)255);
371          dg = ((double)xcolor.green) / ((double)maxRGB) * ((double)255);
372          db = ((double)xcolor.blue) / ((double)maxRGB) * ((double)255);
373          xcolor.red = round(dr);
374          xcolor.green = round(dg);
375          xcolor.blue = round(db);
376          if (xcolor.red > 255) xcolor.red = 255;
377          if (xcolor.green > 255) xcolor.green = 255;
378          if (xcolor.blue > 255) xcolor.blue = 255;
379 
380          ptfri->min_r = (int)(xcolor.red) - fillReplaceRedThresh;
381          ptfri->max_r = (int)(xcolor.red) + fillReplaceRedThresh;
382          if (ptfri->min_r < 0) ptfri->min_r = 0;
383          if (ptfri->max_r > 255) ptfri->max_r = 255;
384 
385          ptfri->min_g = (int)(xcolor.green) - fillReplaceGreenThresh;
386          ptfri->max_g = (int)(xcolor.green) + fillReplaceGreenThresh;
387          if (ptfri->min_g < 0) ptfri->min_g = 0;
388          if (ptfri->max_g > 255) ptfri->max_g = 255;
389 
390          ptfri->min_b = (int)(xcolor.blue) - fillReplaceBlueThresh;
391          ptfri->max_b = (int)(xcolor.blue) + fillReplaceBlueThresh;
392          if (ptfri->min_b < 0) ptfri->min_b = 0;
393          if (ptfri->max_b > 255) ptfri->max_b = 255;
394 
395          for (i=0; i < maxColors; i++) {
396             ptfri->within_threshold[i] =
397                   OtherPixelWithinRange(colorPixels[i], ptfri);
398          }
399       } else {
400 #ifdef _TGIF_DBG /* debug, do not translate */
401          fprintf(stderr,
402                "HashLookup() failed in SetupThreshFillReplaceInfo() for pixel=0x%08x.\n",
403                pixel);
404 #endif /* _TGIF_DBG */
405          return FALSE;
406       }
407    }
408    return TRUE;
409 }
410 
411 static
DoPpm6(xpm_ptr)412 int DoPpm6(xpm_ptr)
413    struct XPmRec *xpm_ptr;
414 {
415    if (gnCombining || xpm_ptr == NULL) {
416       return (fullTrueColorMode && HasZlibSupport());
417    } else if (xpm_ptr != NULL && (xpm_ptr->real_type == PPM_TRUE ||
418          xpm_ptr->real_type == XPM_JPEG) && xpm_ptr->ppm_mask_data == NULL &&
419          fullTrueColorMode && HasZlibSupport()) {
420       return TRUE;
421    }
422    return FALSE;
423 }
424 
425 static
FreePreprocessPixels(h,xcolors)426 int FreePreprocessPixels(h, xcolors)
427    int h;
428    XColor **xcolors;
429 {
430    int i=0;
431 
432    for (i=0; i < h; i++) {
433       if (xcolors[i] != NULL) {
434          free(xcolors[i]);
435       } else {
436          break;
437       }
438    }
439    free(xcolors);
440    gConvExtraInfo.xcolors = NULL;
441 
442    return FALSE;
443 }
444 
445 static
ClearConvExtraInfo()446 void ClearConvExtraInfo()
447 {
448    if (gConvExtraInfo.xcolors != NULL) {
449       FreePreprocessPixels(gConvExtraInfo.image_h, gConvExtraInfo.xcolors);
450    }
451    if (gConvExtraInfo.alpha_xcolors != NULL) {
452       FreePreprocessPixels(gConvExtraInfo.alpha_image_h,
453             gConvExtraInfo.alpha_xcolors);
454    }
455    if (gConvExtraInfo.bg_xcolors != NULL) {
456       FreePreprocessPixels(gConvExtraInfo.bg_image_h,
457             gConvExtraInfo.bg_xcolors);
458    }
459    memset(&gConvExtraInfo, 0, sizeof(ConvExtraInfo));
460 }
461 
462 static
PreprocessOneImagePixels(w,h,image,bitmap_image)463 XColor **PreprocessOneImagePixels(w, h, image, bitmap_image)
464    int w, h;
465    XImage *image, *bitmap_image;
466 {
467    int i=0;
468    XColor **xcolors=NULL;
469    ProgressInfo pi;
470 
471    xcolors = (XColor**)malloc(h*sizeof(XColor*));
472    if (xcolors == NULL) {
473       FailAllocMessage();
474       return NULL;
475    }
476    memset(xcolors, 0, h*sizeof(XColor*));
477 
478    for (i=0; i < h; i++) {
479       xcolors[i] = (XColor*)malloc(w*sizeof(XColor));
480       if (xcolors[i] == NULL) {
481          FailAllocMessage();
482          FreePreprocessPixels(h, xcolors);
483          return NULL;
484       }
485       memset(xcolors[i], 0, w*sizeof(XColor));
486    }
487    BeginProgress(&pi, h);
488    for (i=0; i < h; i++) {
489       int j=0;
490 
491       UpdateProgress(&pi, i);
492       for (j=0; j < w; j++) {
493          if (bitmap_image != NULL && XGetPixel(bitmap_image,j,i) == 0) {
494             TgAssert(FALSE, "transparent pixel not supported", NULL);
495             FreePreprocessPixels(h, xcolors);
496             return NULL;
497          } else {
498             unsigned int pixel=(unsigned int)XGetPixel(image,j,i);
499             uint32_t pix=(uint32_t)pixel;
500             unsigned int r=0, g=0, b=0;
501             double dr=(double)0, dg=(double)0, db=(double)0;
502 
503             r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
504             g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
505             b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
506             dr = ((double)r) / gTrueColorInfo.dr_maxval_div255;
507             dg = ((double)g) / gTrueColorInfo.dg_maxval_div255;
508             db = ((double)b) / gTrueColorInfo.db_maxval_div255;
509             xcolors[i][j].red = round(dr);
510             xcolors[i][j].green = round(dg);
511             xcolors[i][j].blue = round(db);
512             if (xcolors[i][j].red > 255) xcolors[i][j].red = 255;
513             if (xcolors[i][j].green > 255) xcolors[i][j].green = 255;
514             if (xcolors[i][j].blue > 255) xcolors[i][j].blue = 255;
515             xcolors[i][j].pixel = pixel;
516          }
517       }
518    }
519    return xcolors;
520 }
521 
522 static
PreprocessPixels()523 int PreprocessPixels()
524 {
525    if (gConvExtraInfo.alpha_combine) {
526       memset(&gConvExtraInfo.my_bg_xcolor, 0, sizeof(XColor));
527       unsigned int r=(unsigned int)myBgColor.red;
528       unsigned int g=(unsigned int)myBgColor.green;
529       unsigned int b=(unsigned int)myBgColor.blue;
530       double dr=((double)r)/((double)maxRGB)*((double)255);
531       double dg=((double)g)/((double)maxRGB)*((double)255);
532       double db=((double)b)/((double)maxRGB)*((double)255);
533 
534       gConvExtraInfo.my_bg_xcolor.red = round(dr);
535       gConvExtraInfo.my_bg_xcolor.green = round(dg);
536       gConvExtraInfo.my_bg_xcolor.blue = round(db);
537       gConvExtraInfo.my_bg_xcolor.pixel = myBgPixel;
538 
539       /* foreground object */
540       gConvExtraInfo.xcolors = PreprocessOneImagePixels(
541             gConvExtraInfo.image_w, gConvExtraInfo.image_h,
542             gConvExtraInfo.image, gConvExtraInfo.bitmap_image);
543       if (gConvExtraInfo.xcolors == NULL) {
544          return FALSE;
545       }
546       /* background object */
547       gConvExtraInfo.bg_xcolors = PreprocessOneImagePixels(
548             gConvExtraInfo.bg_image_w, gConvExtraInfo.bg_image_h,
549             gConvExtraInfo.bg_image, gConvExtraInfo.bg_bitmap_image);
550       if (gConvExtraInfo.bg_xcolors == NULL) {
551          return FALSE;
552       }
553       if (gpConvolveCmdID == CMDID_ALPHACOMBINE) {
554          gConvExtraInfo.alpha_xcolors = PreprocessOneImagePixels(
555                gConvExtraInfo.alpha_image_w, gConvExtraInfo.alpha_image_h,
556                gConvExtraInfo.alpha_image, gConvExtraInfo.alpha_bitmap_image);
557          if (gConvExtraInfo.alpha_xcolors == NULL) {
558             return FALSE;
559          }
560       }
561    } else {
562       gConvExtraInfo.xcolors = PreprocessOneImagePixels(gConvExtraInfo.image_w,
563             gConvExtraInfo.image_h, gConvExtraInfo.image,
564             gConvExtraInfo.bitmap_image);
565       if (gConvExtraInfo.xcolors == NULL) {
566          return FALSE;
567       }
568       if (gpConvolveCmdID == CMDID_VECTORWARP) {
569          gConvExtraInfo.bg_xcolors = PreprocessOneImagePixels(
570                gConvExtraInfo.image_w, gConvExtraInfo.image_h,
571                gConvExtraInfo.image, gConvExtraInfo.bitmap_image);
572          if (gConvExtraInfo.bg_xcolors == NULL) {
573             return FALSE;
574          }
575       }
576    }
577    return TRUE;
578 }
579 
580 static
SetConvExtraInfo(fp,image_w,image_h,image,bitmap_image)581 int SetConvExtraInfo(fp, image_w, image_h, image, bitmap_image)
582    FILE *fp;
583    int image_w, image_h;
584    XImage *image, *bitmap_image;
585 {
586    memset(&gConvExtraInfo, 0, sizeof(ConvExtraInfo));
587    gConvExtraInfo.fp = fp;
588    gConvExtraInfo.image_w = gConvExtraInfo.bg_image_w = image_w;
589    gConvExtraInfo.image_h = gConvExtraInfo.bg_image_h = image_h;
590    gConvExtraInfo.image = image;
591    gConvExtraInfo.bitmap_image = bitmap_image;
592 
593    switch (gpConvolveCmdID) {
594    case CMDID_EDGEDETECT:
595    case CMDID_EMBOSS:
596    case CMDID_SPREAD:
597    case CMDID_SHARPEN:
598    case CMDID_BLUR3:
599    case CMDID_VECTORWARP:
600       if (!PreprocessPixels()) {
601          return FALSE;
602       }
603       break;
604    case CMDID_SUBTRACT:
605    case CMDID_XORCOLORS:
606       gConvExtraInfo.alpha_combine = TRUE;
607 
608       gConvExtraInfo.final_w = gnCombineW;
609       gConvExtraInfo.final_h = gnCombineH;
610 
611       gConvExtraInfo.image_w = gFgCombineBBox.rbx-gFgCombineBBox.ltx;
612       gConvExtraInfo.image_h = gFgCombineBBox.rby-gFgCombineBBox.lty;
613       gConvExtraInfo.image = gpFgImage;
614       gConvExtraInfo.bitmap_image = gpFgBitmapImage;
615 
616       if (!InitTrueColorInfo(gConvExtraInfo.image, &gTrueColorInfo,
617             gConvExtraInfo.image_w)) {
618          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
619                gConvExtraInfo.image_w, gConvExtraInfo.image_h);
620          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
621          return FALSE;
622       }
623       gConvExtraInfo.bg_image_w = gBgCombineBBox.rbx-gBgCombineBBox.ltx;
624       gConvExtraInfo.bg_image_h = gBgCombineBBox.rby-gBgCombineBBox.lty;
625       gConvExtraInfo.bg_image = gpBgImage;
626       gConvExtraInfo.bg_bitmap_image = gpBgBitmapImage;
627       if (!PreprocessPixels()) {
628          return FALSE;
629       }
630       break;
631    case CMDID_ALPHACOMBINE:
632       gConvExtraInfo.alpha_combine = TRUE;
633 
634       gConvExtraInfo.final_w = gnCombineW;
635       gConvExtraInfo.final_h = gnCombineH;
636 
637       gConvExtraInfo.image_w = gFgCombineBBox.rbx-gFgCombineBBox.ltx;
638       gConvExtraInfo.image_h = gFgCombineBBox.rby-gFgCombineBBox.lty;
639       gConvExtraInfo.image = gpFgImage;
640       gConvExtraInfo.bitmap_image = gpFgBitmapImage;
641 
642       if (!InitTrueColorInfo(gConvExtraInfo.image, &gTrueColorInfo,
643             gConvExtraInfo.image_w)) {
644          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
645                gConvExtraInfo.image_w, gConvExtraInfo.image_h);
646          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
647          return FALSE;
648       }
649       gConvExtraInfo.alpha_image_w =
650             gAlphaCombineBBox.rbx-gAlphaCombineBBox.ltx;
651       gConvExtraInfo.alpha_image_h =
652             gAlphaCombineBBox.rby-gAlphaCombineBBox.lty;
653       gConvExtraInfo.alpha_image = gpAlphaImage;
654       gConvExtraInfo.alpha_bitmap_image = gpAlphaBitmapImage;
655 
656       gConvExtraInfo.bg_image_w = gBgCombineBBox.rbx-gBgCombineBBox.ltx;
657       gConvExtraInfo.bg_image_h = gBgCombineBBox.rby-gBgCombineBBox.lty;
658       gConvExtraInfo.bg_image = gpBgImage;
659       gConvExtraInfo.bg_bitmap_image = gpBgBitmapImage;
660       if (!PreprocessPixels()) {
661          return FALSE;
662       }
663       break;
664    default: break;
665    }
666    return TRUE;
667 }
668 
669 static
GetImageProcName(nCmdId)670 char *GetImageProcName(nCmdId)
671    int nCmdId;
672 {
673    TgMenuItemInfo *item_info=NULL;
674 
675    for (item_info=imageProcMenuInfo.items; item_info->menu_str != NULL;
676          item_info++) {
677       if (item_info->menu_str != TGMUITEM_SEPARATOR &&
678             item_info->cmdid == nCmdId) {
679          /* must use gettext() here */
680          return _(item_info->menu_str);
681       } else if (item_info->shortcut_str == TGMUITEM_SUBMENU) {
682          TgMenuInfo *submenu_info=item_info->submenu_info;
683          TgMenuItemInfo *submenu_item_info=NULL;
684 
685          for (submenu_item_info=submenu_info->items;
686                submenu_item_info->menu_str != NULL;
687                submenu_item_info++) {
688             if (submenu_item_info->menu_str != TGMUITEM_SEPARATOR &&
689                   submenu_item_info->cmdid == nCmdId) {
690                /* must use gettext() here */
691                return _(submenu_item_info->menu_str);
692             }
693          }
694       }
695    }
696    return TgLoadCachedString(CSTID_PARANED_UNKNOWN);
697 }
698 
699 static
TrueColorTransPixelCheck(obj_ptr,cmdid)700 int TrueColorTransPixelCheck(obj_ptr, cmdid)
701    struct ObjRec *obj_ptr;
702    int cmdid;
703 {
704    if (ObjHasTrueColorTransPixel(obj_ptr, NULL, NULL, NULL)) {
705       sprintf(gszMsgBox, TgLoadString(STID_BAD_CMD_FOR_TRANS_PPMTRUE),
706             GetImageProcName(cmdid));
707       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
708       return TRUE;
709    }
710    return FALSE;
711 }
712 
713 static
CurColorIsTranscolor(trans_color_r,trans_color_g,trans_color_b)714 int CurColorIsTranscolor(trans_color_r, trans_color_g, trans_color_b)
715    unsigned char trans_color_r, trans_color_g, trans_color_b;
716 {
717    TrueColorInfo tci;
718    unsigned int r=0, g=0, b=0;
719    double dr=(double)0, dg=(double)0, db=(double)0;
720    uint32_t pixel=0;
721    double dmaxval=(double)255;
722 
723    if (!SetupTrueColorInfo(&tci)) return FALSE;
724 
725    r = (unsigned int)trans_color_r;
726    g = (unsigned int)trans_color_g;
727    b = (unsigned int)trans_color_b;
728    dr = ((double)r) / dmaxval * tci.dr_maxval;
729    dg = ((double)g) / dmaxval * tci.dg_maxval;
730    db = ((double)b) / dmaxval * tci.db_maxval;
731    r = round(dr);
732    g = round(dg);
733    b = round(db);
734    pixel = ((r << tci.r_shift) & mainVisual->red_mask) |
735            ((g << tci.g_shift) & mainVisual->green_mask) |
736            ((b << tci.b_shift) & mainVisual->blue_mask) ;
737 
738    return (((int)pixel) == colorPixels[colorIndex]);
739 }
740 
741 static
CheckSelectionForImageProc(cmdid)742 int CheckSelectionForImageProc(cmdid)
743    int cmdid;
744 {
745    char szBuf[MAXSTRING+1];
746 
747    UtilStrCpyN(szBuf, sizeof(szBuf), GetImageProcName(cmdid));
748    if (topSel == NULL || topSel != botSel || topSel->obj->type != OBJ_XPM) {
749       sprintf(gszMsgBox, TgLoadString(STID_SINGLE_XPM_IMGPROC), szBuf);
750       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
751       return FALSE;
752    }
753    if (topSel->obj->fattr != NULL) {
754       sprintf(gszMsgBox, TgLoadString(STID_XPM_HAS_ATTR_IMGPROC), szBuf);
755       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
756       return FALSE;
757    }
758    return TRUE;
759 }
760 
761 #define LONG_AXIS_IS_RED 0
762 #define LONG_AXIS_IS_GREEN 1
763 #define LONG_AXIS_IS_BLUE 2
764 
765 typedef struct CubeRec { /* not really a ``cube'' */
766    int min_index, max_index, level, long_axis;
767    unsigned long num_points;
768    unsigned short red_length, green_length, blue_length;
769 } *CubePointer;
770 
771 typedef struct HGBucketRec {
772    int index;
773    struct HGBucketRec *next;
774 } *HGBucketPoiner;
775 
776 static struct HGBucketRec *gaHGBucket[256];
777 
778 typedef struct tagTmpBucketInfo {
779    int pixel;
780    int index;
781    struct tagTmpBucketInfo *next;
782 } TmpBucketInfo;
783 
784 static XColor *gpHistogram=NULL;
785 static int *gpnSortedIndex=NULL;
786 static int **gnOrigImageIndex=NULL, **gnFinalImageIndex=NULL;
787 static int gnImageW=(-1), gnImageH=(-1);
788 static int *gpnPixelToIndexMap=NULL, gnPixelToIndexMapSize=0;
789 static int gnHistogramEntries=0, gnHistogramSize=0;
790 static int gnQuantizingLevels=222, gnUserSpecifiedLevels=(-1);
791 static struct CubeRec *gpCube=NULL;
792 static int gnCubeEntries=0;
793 static int gnTransparentIndex=(-1);
794 
795 static
GetXPmImages(xpm_ptr,p_image,p_bitmap_image)796 int GetXPmImages(xpm_ptr, p_image, p_bitmap_image)
797    struct XPmRec *xpm_ptr;
798    XImage **p_image, **p_bitmap_image;
799 {
800    Pixmap pixmap=xpm_ptr->pixmap, bitmap=xpm_ptr->bitmap;
801    int image_w=xpm_ptr->image_w, image_h=xpm_ptr->image_h;
802 
803    *p_image = XGetImage(mainDisplay, pixmap, 0, 0, image_w, image_h, AllPlanes,
804          ZPixmap);
805    if (bitmap != None) {
806       *p_bitmap_image = XGetImage(mainDisplay, bitmap, 0, 0, image_w, image_h,
807          1, ZPixmap);
808    } else {
809       *p_bitmap_image = NULL;
810    }
811    if ((*p_image) == NULL || (bitmap != None && (*p_bitmap_image) == NULL)) {
812       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
813             image_w, image_h);
814       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
815       return FALSE;
816    }
817    return TRUE;
818 }
819 
820 static
CleanUpTmpBuckets()821 void CleanUpTmpBuckets()
822 {
823    if (gpnPixelToIndexMap != NULL) {
824       int i=0;
825       TmpBucketInfo **buckets=(TmpBucketInfo**)gpnPixelToIndexMap;
826 
827       for (i=0; i < gnPixelToIndexMapSize; i++) {
828          TmpBucketInfo *ptbi=NULL, *ptbi_next=NULL;
829 
830          for (ptbi=buckets[i]; ptbi != NULL; ptbi=ptbi_next) {
831             ptbi_next = ptbi->next;
832             free(ptbi);
833          }
834       }
835       free(buckets);
836       gpnPixelToIndexMap = NULL;
837    }
838    gnPixelToIndexMapSize = 0;
839 }
840 
841 static
CleanUpIndexOfPixel()842 void CleanUpIndexOfPixel()
843 {
844    if (mainVisual->class == TrueColor) {
845       CleanUpTmpBuckets();
846    } else {
847       if (gpnPixelToIndexMap != NULL) free(gpnPixelToIndexMap);
848       gpnPixelToIndexMap = NULL;
849       gnPixelToIndexMapSize = 0;
850    }
851 }
852 
CleanUpConvolution()853 void CleanUpConvolution()
854 {
855    register int i;
856 
857    CleanUpIndexOfPixel();
858 
859    if (gpHistogram != NULL) {
860       free(gpHistogram);
861       gpHistogram = NULL;
862    }
863    if (gpnSortedIndex != NULL) {
864       free(gpnSortedIndex);
865       gpnSortedIndex = NULL;
866    }
867    gnHistogramEntries = gnHistogramSize = 0;
868 
869    if (gpCube != NULL) {
870       free(gpCube);
871       gpCube = NULL;
872    }
873    gnCubeEntries = 0;
874    if (gnOrigImageIndex != NULL) {
875       for (i=0; i < gnImageH; i++) {
876          if (gnOrigImageIndex[i] != NULL) {
877             free(gnOrigImageIndex[i]);
878          } else {
879             break;
880          }
881       }
882       free(gnOrigImageIndex);
883       gnOrigImageIndex = NULL;
884    }
885    if (gnFinalImageIndex != NULL) {
886       for (i=0; i < gnImageH; i++) {
887          if (gnFinalImageIndex[i] != NULL) {
888             free(gnFinalImageIndex[i]);
889          } else {
890             break;
891          }
892       }
893       free(gnFinalImageIndex);
894       gnFinalImageIndex = NULL;
895    }
896    gnImageW = gnImageH = (-1);
897 
898    for (i=0; i < 256; i++) {
899       if (gaHGBucket[i] != NULL) {
900          struct HGBucketRec *bucket_ptr=gaHGBucket[i], *next_bucket;
901 
902          for ( ; bucket_ptr != NULL; bucket_ptr=next_bucket) {
903             next_bucket = bucket_ptr->next;
904             free(bucket_ptr);
905          }
906          gaHGBucket[i] = NULL;
907       }
908    }
909 }
910 
911 static
GetOrAllocHistogramIndex(pcolor)912 int GetOrAllocHistogramIndex(pcolor)
913    XColor *pcolor;
914 {
915    int hashvalue=0;
916 
917    if (pcolor == NULL) {
918       if (gnTransparentIndex != (-1)) return gnTransparentIndex;
919    } else {
920       struct HGBucketRec *bucket_ptr;
921 
922       hashvalue = (int)((pcolor->red ^ pcolor->green ^ pcolor->blue) & 0xff);
923       if (gaHGBucket[hashvalue] != NULL) {
924          struct HGBucketRec *bucket_ptr=gaHGBucket[hashvalue];
925 
926          for ( ; bucket_ptr != NULL; bucket_ptr=bucket_ptr->next) {
927             int i=bucket_ptr->index;
928 
929             if (gpHistogram[i].red == pcolor->red &&
930                   gpHistogram[i].green == pcolor->green &&
931                   gpHistogram[i].blue == pcolor->blue) {
932                HISTOGRAMCOUNT(i)++;
933                return i;
934             }
935          }
936       }
937       bucket_ptr = (struct HGBucketRec *)malloc(sizeof(struct HGBucketRec));
938       if (bucket_ptr == NULL) {
939          FailAllocMessage();
940          return (-1);
941       }
942       bucket_ptr->index = gnHistogramEntries;
943       bucket_ptr->next = gaHGBucket[hashvalue];
944       gaHGBucket[hashvalue] = bucket_ptr;
945    }
946    if (gnHistogramEntries >= gnHistogramSize) {
947       gnHistogramSize += 256;
948       if ((gpHistogram=(XColor*)realloc(gpHistogram,
949             gnHistogramSize*sizeof(XColor))) == NULL) {
950          FailAllocMessage();
951          return (-1);
952       }
953    }
954    memset(&gpHistogram[gnHistogramEntries], 0, sizeof(XColor));
955    HISTOGRAMCOUNT(gnHistogramEntries) = 1;
956    if (pcolor == NULL) {
957       gpHistogram[gnHistogramEntries].red = 0;
958       gpHistogram[gnHistogramEntries].green = 0;
959       gpHistogram[gnHistogramEntries].blue = 0;
960    } else {
961       gpHistogram[gnHistogramEntries].red = pcolor->red;
962       gpHistogram[gnHistogramEntries].green = pcolor->green;
963       gpHistogram[gnHistogramEntries].blue = pcolor->blue;
964    }
965    return (gnHistogramEntries++);
966 }
967 
968 static
PixelToIndexHash(pixel)969 int PixelToIndexHash(pixel)
970    int pixel;
971 {
972    return (((pixel)==(-1)) ? (gnPixelToIndexMapSize-1) :
973          (pixel % gnPixelToIndexMapSize));
974 }
975 
976 static
GetIndexOfPixel(pixel)977 int GetIndexOfPixel(pixel)
978    int pixel;
979 {
980    if (mainVisual->class == TrueColor) {
981       TmpBucketInfo **buckets=(TmpBucketInfo**)gpnPixelToIndexMap, *ptbi=NULL;
982       int bucket=PixelToIndexHash(pixel);
983 
984       for (ptbi=buckets[bucket]; ptbi != NULL; ptbi=ptbi->next) {
985          if (ptbi->pixel == pixel) {
986             return ptbi->index;
987          }
988       }
989       return INVALID;
990    } else {
991       return gpnPixelToIndexMap[pixel];
992    }
993 }
994 
995 static
UpdatePixelToIndexMapping(buckets,pixel,index)996 void UpdatePixelToIndexMapping(buckets, pixel, index)
997    TmpBucketInfo **buckets;
998    int pixel, index;
999 {
1000    int bucket=PixelToIndexHash(pixel);
1001    TmpBucketInfo *ptbi=NULL;
1002 
1003    for (ptbi=buckets[bucket]; ptbi != NULL; ptbi=ptbi->next) {
1004       if (ptbi->pixel == pixel) {
1005          return;
1006       }
1007    }
1008    ptbi = (TmpBucketInfo *)malloc(sizeof(TmpBucketInfo));
1009    if (ptbi == NULL) FailAllocMessage();
1010    memset(ptbi, 0, sizeof(TmpBucketInfo));
1011    ptbi->next = buckets[bucket];
1012    buckets[bucket] = ptbi;
1013    ptbi->pixel = pixel;
1014    ptbi->index = index;
1015 }
1016 
1017 static
AllocTmpBuckets(populate_with_color_pixels)1018 int AllocTmpBuckets(populate_with_color_pixels)
1019    int populate_with_color_pixels;
1020 {
1021    TmpBucketInfo **buckets=(TmpBucketInfo **)gpnPixelToIndexMap;
1022 
1023    gnPixelToIndexMapSize = 257;
1024    buckets = (TmpBucketInfo**)malloc(gnPixelToIndexMapSize *
1025          sizeof(TmpBucketInfo*));
1026    if (buckets == NULL) {
1027       FailAllocMessage();
1028       return FALSE;
1029    }
1030    memset(buckets, 0, gnPixelToIndexMapSize*sizeof(TmpBucketInfo*));
1031 
1032    gpnPixelToIndexMap = (int*)buckets;
1033    if (populate_with_color_pixels) {
1034       int i=0;
1035 
1036       for (i=0; i < maxColors; i++) {
1037          UpdatePixelToIndexMapping(buckets, colorPixels[i], i);
1038       }
1039    }
1040    return TRUE;
1041 }
1042 
1043 static
CreatePixelToIndexMapping()1044 int CreatePixelToIndexMapping()
1045 {
1046    register int i=0;
1047 
1048    if (mainVisual->class == TrueColor) {
1049       if (!AllocTmpBuckets(TRUE)) {
1050          CleanUpConvolution();
1051          return FALSE;
1052       }
1053    } else {
1054       int max_pixel=(-1);
1055 
1056       for (i=0; i < maxColors; i++) {
1057          if (colorPixels[i] > max_pixel) {
1058             max_pixel = colorPixels[i];
1059          }
1060       }
1061       if (max_pixel == (-1)) return FALSE;
1062 
1063       gpnPixelToIndexMap = (int*)malloc((max_pixel+1)*sizeof(int));
1064       if (gpnPixelToIndexMap == NULL) {
1065          FailAllocMessage();
1066          CleanUpConvolution();
1067          return FALSE;
1068       }
1069       memset(gpnPixelToIndexMap, (-1), (max_pixel+1)*sizeof(int));
1070       for (i=0; i < maxColors; i++) {
1071          gpnPixelToIndexMap[colorPixels[i]] = i;
1072       }
1073    }
1074    return TRUE;
1075 }
1076 
1077 static
CreateObjPixelToIndexMapping(xpm_ptr)1078 int CreateObjPixelToIndexMapping(xpm_ptr)
1079    struct XPmRec *xpm_ptr;
1080 {
1081    register int i=0;
1082 
1083    gnTransparentIndex = (-1);
1084 
1085    if (mainVisual->class == TrueColor) {
1086       int start_index=0;
1087       XColor *xcolors=NULL;
1088 
1089       xcolors = (XColor*)malloc(xpm_ptr->ncolors*sizeof(XColor));
1090       if (xcolors == NULL) FailAllocMessage();
1091       memset(xcolors, 0, xpm_ptr->ncolors*sizeof(XColor));
1092       if (!AllocTmpBuckets(TRUE)) {
1093          free(xcolors);
1094          CleanUpConvolution();
1095          return FALSE;
1096       }
1097       start_index = (xpm_ptr->first_pixel_is_bg ? 1 : 0);
1098       for (i=start_index; i < xpm_ptr->ncolors; i++) {
1099          int pixel=xpm_ptr->pixels[i];
1100 
1101          /* do not translate -- program constants */
1102          if (UtilStrICmp(xpm_ptr->color_str[i], "None") == 0) {
1103             if (gnTransparentIndex == (-1)) {
1104                gnTransparentIndex = GetOrAllocHistogramIndex(NULL);
1105             }
1106          } else {
1107             ((ImageMapColorFunc*)gpImageMapColorFunc)(GetIndexOfPixel(pixel),
1108                   &xcolors[i]);
1109          }
1110       }
1111       CleanUpTmpBuckets();
1112       if (!AllocTmpBuckets(FALSE)) {
1113          free(xcolors);
1114          CleanUpConvolution();
1115          return FALSE;
1116       }
1117       for (i=start_index; i < xpm_ptr->ncolors; i++) {
1118          UpdatePixelToIndexMapping((TmpBucketInfo**)gpnPixelToIndexMap,
1119                xpm_ptr->pixels[i], GetOrAllocHistogramIndex(&xcolors[i]));
1120       }
1121       free(xcolors);
1122    } else {
1123       int max_pixel=(-1), start_index, *pixel_to_index_map=NULL;
1124 
1125       for (i=0; i < maxColors; i++) {
1126          if (colorPixels[i] > max_pixel) {
1127             max_pixel = colorPixels[i];
1128          }
1129       }
1130       if (max_pixel == (-1)) return FALSE;
1131 
1132       gpnPixelToIndexMap = (int*)malloc((max_pixel+1)*sizeof(int));
1133       pixel_to_index_map = (int*)malloc((max_pixel+1)*sizeof(int));
1134       if (gpnPixelToIndexMap == NULL || pixel_to_index_map == NULL) {
1135          if (gpnPixelToIndexMap != NULL) free(gpnPixelToIndexMap);
1136          if (pixel_to_index_map != NULL) free(pixel_to_index_map);
1137          gpnPixelToIndexMap = NULL;
1138          FailAllocMessage();
1139          CleanUpConvolution();
1140          return FALSE;
1141       }
1142       memset(gpnPixelToIndexMap, (-1), (max_pixel+1)*sizeof(int));
1143       memset(pixel_to_index_map, (-1), (max_pixel+1)*sizeof(int));
1144       for (i=0; i < maxColors; i++) {
1145          pixel_to_index_map[colorPixels[i]] = i;
1146       }
1147       start_index = (xpm_ptr->first_pixel_is_bg ? 1 : 0);
1148       for (i=start_index; i < xpm_ptr->ncolors; i++) {
1149          XColor xcolor;
1150          int pixel=xpm_ptr->pixels[i];
1151 
1152          memset(&xcolor, 0, sizeof(XColor));
1153          /* do not translate -- program constants */
1154          if (UtilStrICmp(xpm_ptr->color_str[i], "None") == 0) {
1155             if (gnTransparentIndex == (-1)) {
1156                gnTransparentIndex = GetOrAllocHistogramIndex(NULL);
1157             }
1158          } else {
1159             ((ImageMapColorFunc*)gpImageMapColorFunc)(pixel_to_index_map[pixel],
1160                   &xcolor);
1161             gpnPixelToIndexMap[pixel] = GetOrAllocHistogramIndex(&xcolor);
1162          }
1163       }
1164       free(pixel_to_index_map);
1165    }
1166    return TRUE;
1167 }
1168 
1169 static
DumpConvolution(fp)1170 int DumpConvolution(fp)
1171    FILE *fp;
1172 {
1173    register int j, i;
1174    int chars_per_pixel=(gnHistogramEntries > 20 ? 2 : 1);
1175    char c0[27], c1[11];
1176    ProgressInfo pi;
1177 
1178    /* do not translate -- program constants */
1179    strcpy(c0, "abcdefghijklmnopqrstuvwxyz");
1180    strcpy(c1, "0123456789");
1181    if (fprintf(fp, "#define conv_format 1\n") == EOF ||
1182          fprintf(fp, "#define conv_width %1d\n", gnImageW) == EOF ||
1183          fprintf(fp, "#define conv_height %1d\n", gnImageH) == EOF ||
1184          fprintf(fp, "#define conv_ncolors %1d\n", gnHistogramEntries) == EOF ||
1185          fprintf(fp, "#define conv_chars_per_pixel %1d\n",
1186          chars_per_pixel) == EOF ||
1187          fprintf(fp, "static char *conv_colors[] = {\n") == EOF) {
1188       writeFileFailed = TRUE;
1189    }
1190    for (j=0; j < gnHistogramEntries; j++) {
1191       int red=(int)gpHistogram[j].red;
1192       int green=(int)gpHistogram[j].green;
1193       int blue=(int)gpHistogram[j].blue;
1194 
1195       /* do not translate -- program constants */
1196       if (gnTransparentIndex == j) {
1197          if (chars_per_pixel == 1) {
1198             if (fprintf(fp, "   \"%c\", \"None\"", c0[j]) == EOF) {
1199                writeFileFailed = TRUE;
1200             }
1201          } else {
1202             if (fprintf(fp, "   \"%c%c\", \"None\"",
1203                   c0[(int)(j/10)], c1[j % 10]) == EOF) {
1204                writeFileFailed = TRUE;
1205             }
1206          }
1207       } else {
1208          if (chars_per_pixel == 1) {
1209             if (fprintf(fp, "   \"%c\", \"#%04x%04x%04x\"",
1210                   c0[j], red&0x0ffff, green&0x0ffff, blue&0x0ffff) == EOF) {
1211                writeFileFailed = TRUE;
1212             }
1213          } else {
1214             if (fprintf(fp, "   \"%c%c\", \"#%04x%04x%04x\"",
1215                   c0[(int)(j/10)], c1[j % 10],
1216                   red&0x0ffff, green&0x0ffff, blue&0x0ffff) == EOF) {
1217                writeFileFailed = TRUE;
1218             }
1219          }
1220       }
1221       if (j == gnHistogramEntries-1) {
1222          fprintf(fp, "\n};\n");
1223       } else {
1224          fprintf(fp, ",\n");
1225       }
1226    }
1227    if (fprintf(fp, "static char *conv_pixels[] = {\n") == EOF) {
1228       writeFileFailed = TRUE;
1229    }
1230    BeginProgress(&pi, gnImageH);
1231    for (i=0; i < gnImageH; i++) {
1232       UpdateProgress(&pi, i);
1233       fprintf(fp, "\"");
1234       for (j=0; j < gnImageW; j++) {
1235          int index=gnFinalImageIndex[i][j];
1236 
1237          if (chars_per_pixel == 1) {
1238             if (fprintf(fp, "%c", c0[index]) == EOF) {
1239                writeFileFailed = TRUE;
1240             }
1241          } else {
1242             if (fprintf(fp, "%c%c",
1243                   c0[(int)(index/10)], c1[index % 10]) == EOF) {
1244                writeFileFailed = TRUE;
1245             }
1246          }
1247       }
1248       if (i == gnImageH-1) {
1249          if (fprintf(fp, "\"\n};\n") == EOF) writeFileFailed = TRUE;
1250       } else {
1251          if (fprintf(fp, "\",\n") == EOF) writeFileFailed = TRUE;
1252       }
1253    }
1254    return TRUE;
1255 }
1256 
1257 static int gnDebugQuantization=FALSE;
1258 
1259 static
DumpQuantizedConvolution(fp)1260 int DumpQuantizedConvolution(fp)
1261    FILE *fp;
1262 {
1263    register int j, i;
1264    int chars_per_pixel=(gnCubeEntries > 20 ? 2 : 1);
1265    char c0[27], c1[11];
1266    ProgressInfo pi;
1267 
1268    /* do not translate -- program constants */
1269    strcpy(c0, "abcdefghijklmnopqrstuvwxyz");
1270    strcpy(c1, "0123456789");
1271    if (fprintf(fp, "#define conv_format 1\n") == EOF ||
1272          fprintf(fp, "#define conv_width %1d\n", gnImageW) == EOF ||
1273          fprintf(fp, "#define conv_height %1d\n", gnImageH) == EOF ||
1274          fprintf(fp, "#define conv_ncolors %1d\n", gnCubeEntries) == EOF ||
1275          fprintf(fp, "#define conv_chars_per_pixel %1d\n",
1276          chars_per_pixel) == EOF ||
1277          fprintf(fp, "static char *conv_colors[] = {\n") == EOF) {
1278       writeFileFailed = TRUE;
1279    }
1280    if (gnDebugQuantization) { /* debug, do not translate */
1281       fprintf(stderr, "Dumping colors...\n");
1282    }
1283    for (j=0; j < gnCubeEntries; j++) {
1284       int min_index=gpCube[j].min_index;
1285       int max_index=gpCube[j].max_index;
1286       int idx=gpnSortedIndex[min_index];
1287       double num_points=(double)HISTOGRAMCOUNT(idx);
1288       double red=((double)HISTOGRAMRED(idx))*num_points;
1289       double green=((double)HISTOGRAMGREEN(idx))*num_points;
1290       double blue=((double)HISTOGRAMBLUE(idx))*num_points;
1291       long lred, lgreen, lblue;
1292 
1293       for (i=min_index+1; i <= max_index; i++) {
1294          double n;
1295 
1296          idx = gpnSortedIndex[i];
1297          n = (double)HISTOGRAMCOUNT(idx);
1298          num_points += n;
1299          red += ((long)HISTOGRAMRED(idx))*n;
1300          green += ((long)HISTOGRAMGREEN(idx))*n;
1301          blue += ((long)HISTOGRAMBLUE(idx))*n;
1302       }
1303       red /= num_points; green /= num_points; blue /= num_points;
1304       lred = (long)red; lgreen = (long)green; lblue = (long)blue;
1305       if (gnDebugQuantization) {
1306          fprintf(stderr, "\t#%02x%02x%02x %6ld\n",
1307                (int)((lred>>8) & 0xff), (int)((lgreen>>8) & 0xff),
1308                (int)((lblue>>8) & 0xff), (long)num_points);
1309       }
1310       if (chars_per_pixel == 1) {
1311          if (fprintf(fp, "   \"%c\", \"#%04x%04x%04x\"",
1312                c0[j], (int)(lred&0x0ffff), (int)(lgreen&0x0ffff),
1313                (int)(lblue&0x0ffff)) == EOF) {
1314             writeFileFailed = TRUE;
1315          }
1316       } else {
1317          if (fprintf(fp, "   \"%c%c\", \"#%04x%04x%04x\"",
1318                c0[(int)(j/10)], c1[j % 10], (int)(lred&0x0ffff),
1319                (int)(lgreen&0x0ffff), (int)(lblue&0x0ffff)) == EOF) {
1320             writeFileFailed = TRUE;
1321          }
1322       }
1323       if (j == gnCubeEntries-1) {
1324          fprintf(fp, "\n};\n");
1325       } else {
1326          fprintf(fp, ",\n");
1327       }
1328       /*
1329        * use the gpHistogram[*].pixel as the reverse color index
1330        */
1331       for (i=min_index; i <= max_index; i++) {
1332          HISTOGRAMCOUNT(gpnSortedIndex[i]) = (long)j;
1333       }
1334    }
1335    if (fprintf(fp, "static char *conv_pixels[] = {\n") == EOF) {
1336       writeFileFailed = TRUE;
1337    }
1338    BeginProgress(&pi, gnImageH);
1339    for (i=0; i < gnImageH; i++) {
1340       UpdateProgress(&pi, i);
1341       fprintf(fp, "\"");
1342       for (j=0; j < gnImageW; j++) {
1343          int orig_index=gnFinalImageIndex[i][j];
1344          int index=HISTOGRAMCOUNT(orig_index);
1345 
1346          if (chars_per_pixel == 1) {
1347             if (fprintf(fp, "%c", c0[index]) == EOF) {
1348                writeFileFailed = TRUE;
1349             }
1350          } else {
1351             if (fprintf(fp, "%c%c",
1352                   c0[(int)(index/10)], c1[index % 10]) == EOF) {
1353                writeFileFailed = TRUE;
1354             }
1355          }
1356       }
1357       if (i == gnImageH-1) {
1358          if (fprintf(fp, "\"\n};\n") == EOF) writeFileFailed = TRUE;
1359       } else {
1360          if (fprintf(fp, "\",\n") == EOF) writeFileFailed = TRUE;
1361       }
1362    }
1363    return TRUE;
1364 }
1365 
1366 static
AlreadySorted(nMinIndex,nMaxIndex,nLongAxis)1367 int AlreadySorted(nMinIndex, nMaxIndex, nLongAxis)
1368    int nMinIndex, nMaxIndex, nLongAxis;
1369 {
1370    register int i;
1371 
1372    switch (nLongAxis) {
1373    case LONG_AXIS_IS_RED:
1374       for (i=nMinIndex; i < nMaxIndex; i++) {
1375          if (HISTOGRAMRED(gpnSortedIndex[i]) <
1376                HISTOGRAMRED(gpnSortedIndex[i+1])) {
1377             return FALSE;
1378          }
1379       }
1380       break;
1381    case LONG_AXIS_IS_GREEN:
1382       for (i=nMinIndex; i < nMaxIndex; i++) {
1383          if (HISTOGRAMGREEN(gpnSortedIndex[i]) <
1384                HISTOGRAMGREEN(gpnSortedIndex[i+1])) {
1385             return FALSE;
1386          }
1387       }
1388       break;
1389    case LONG_AXIS_IS_BLUE:
1390       for (i=nMinIndex; i < nMaxIndex; i++) {
1391          if (HISTOGRAMBLUE(gpnSortedIndex[i]) <
1392                HISTOGRAMBLUE(gpnSortedIndex[i+1])) {
1393             return FALSE;
1394          }
1395       }
1396       break;
1397    }
1398    return TRUE;
1399 }
1400 
1401 static
DebugSortACube(nMinIndex,nMaxIndex,nLevel,nLongAxis)1402 void DebugSortACube(nMinIndex, nMaxIndex, nLevel, nLongAxis)
1403    int nMinIndex, nMaxIndex, nLevel, nLongAxis;
1404 {
1405    register int i;
1406    int sorted=TRUE;
1407 
1408    /* debug, do not translate */
1409    fprintf(stderr, "Level %1d done (long axis is '%s'):\n", nLevel,
1410          (nLongAxis==LONG_AXIS_IS_RED ? "red" :
1411          (nLongAxis==LONG_AXIS_IS_GREEN ? "green" : "blue")));
1412    for (i=nMinIndex; i <= nMaxIndex; i++) {
1413       fprintf(stderr, "\t%6ld: %6d %6d %6d\n",
1414             (long)HISTOGRAMCOUNT(gpnSortedIndex[i]),
1415             (int)HISTOGRAMRED(gpnSortedIndex[i]),
1416             (int)HISTOGRAMGREEN(gpnSortedIndex[i]),
1417             (int)HISTOGRAMBLUE(gpnSortedIndex[i]));
1418       switch (nLongAxis) {
1419       case LONG_AXIS_IS_RED:
1420          if (sorted && i != nMinIndex && HISTOGRAMRED(gpnSortedIndex[i-1]) <
1421                HISTOGRAMRED(gpnSortedIndex[i])) {
1422             sorted = FALSE;
1423          }
1424          break;
1425       case LONG_AXIS_IS_GREEN:
1426          if (sorted && i != nMinIndex && HISTOGRAMGREEN(gpnSortedIndex[i-1]) <
1427                HISTOGRAMGREEN(gpnSortedIndex[i])) {
1428             sorted = FALSE;
1429          }
1430          break;
1431       case LONG_AXIS_IS_BLUE:
1432          if (sorted && i != nMinIndex && HISTOGRAMBLUE(gpnSortedIndex[i-1]) <
1433                HISTOGRAMBLUE(gpnSortedIndex[i])) {
1434             sorted = FALSE;
1435          }
1436          break;
1437       }
1438    }
1439    if (!sorted) fprintf(stderr, "Not sorted!\n");
1440 }
1441 
1442 /*
1443 static
1444 void DisplaySortACube(nMinIndex, nMaxIndex)
1445    int nMinIndex, nMaxIndex;
1446 {
1447    register int i;
1448    int sorted=TRUE;
1449 
1450    for (i=nMinIndex; i <= nMaxIndex; i++) {
1451       fprintf(stderr, "\t%6ld %6d %6d %6d %6d %3d\n",
1452             (long)HISTOGRAMCOUNT(gpnSortedIndex[i]),
1453             (int)HISTOGRAMRED(gpnSortedIndex[i]),
1454             (int)HISTOGRAMGREEN(gpnSortedIndex[i]),
1455             (int)HISTOGRAMBLUE(gpnSortedIndex[i]),
1456             gpnSortedIndex[i], i);
1457    }
1458    if (!sorted) fprintf(stderr, "Not sorted!\n");
1459 }
1460  */
1461 
1462 static int dbg_sort=FALSE;
1463 
1464 static
QuickSortACube(nMinIndex,nMaxIndex,nLevel,nLongAxis)1465 void QuickSortACube(nMinIndex, nMaxIndex, nLevel, nLongAxis)
1466    int nMinIndex, nMaxIndex, nLevel, nLongAxis;
1467 {
1468    register int i, j;
1469    int pivot_index, tmp, something_swapped;
1470    unsigned long pivot_value=0;
1471 
1472    if (nMinIndex > nMaxIndex) return;
1473    if (AlreadySorted(nMinIndex, nMaxIndex, nLongAxis)) return;
1474 
1475    pivot_index = nMaxIndex;
1476    switch (nLongAxis) {
1477    case LONG_AXIS_IS_RED:
1478       pivot_value = HISTOGRAMRED(gpnSortedIndex[pivot_index]);
1479       break;
1480    case LONG_AXIS_IS_GREEN:
1481       pivot_value = HISTOGRAMGREEN(gpnSortedIndex[pivot_index]);
1482       break;
1483    case LONG_AXIS_IS_BLUE:
1484       pivot_value = HISTOGRAMBLUE(gpnSortedIndex[pivot_index]);
1485       break;
1486    }
1487    i = nMinIndex;
1488    j = nMaxIndex-1;
1489    something_swapped = FALSE;
1490    do {
1491       switch (nLongAxis) {
1492       case LONG_AXIS_IS_RED:
1493          while (HISTOGRAMRED(gpnSortedIndex[i]) > pivot_value) i++;
1494          while (j > i && HISTOGRAMRED(gpnSortedIndex[j]) < pivot_value) j--;
1495          break;
1496       case LONG_AXIS_IS_GREEN:
1497          while (HISTOGRAMGREEN(gpnSortedIndex[i]) > pivot_value) i++;
1498          while (j > i && HISTOGRAMGREEN(gpnSortedIndex[j]) < pivot_value) j--;
1499          break;
1500       case LONG_AXIS_IS_BLUE:
1501          while (HISTOGRAMBLUE(gpnSortedIndex[i]) > pivot_value) i++;
1502          while (j > i && HISTOGRAMBLUE(gpnSortedIndex[j]) < pivot_value) j--;
1503          break;
1504       }
1505       if (j > i) {
1506          tmp = gpnSortedIndex[j];
1507          gpnSortedIndex[j] = gpnSortedIndex[i];
1508          gpnSortedIndex[i] = tmp;
1509          if (something_swapped == FALSE) {
1510             something_swapped = TRUE;
1511          }
1512          if (j == i+1) break;
1513          i++; j--;
1514       } else {
1515          break;
1516       }
1517    } while (TRUE);
1518    if (i == nMaxIndex) {
1519       /* pivot_value is the smallest */
1520       if (something_swapped) {
1521 #ifdef _TGIF_DBG /* debug, do not translate */
1522          fprintf(stderr, "Huh? nMinIndex=%1d, nMaxIndex=%1d, nLevel=%1d\n",
1523                nMinIndex, nMaxIndex, nLevel);
1524 #endif /* _TGIF_DBG */
1525       } else {
1526          QuickSortACube(nMinIndex, j, nLevel+1, nLongAxis);
1527       }
1528    } else if (j > i) {
1529       tmp = gpnSortedIndex[nMaxIndex];
1530       gpnSortedIndex[nMaxIndex] = gpnSortedIndex[j];
1531       gpnSortedIndex[j] = tmp;
1532       QuickSortACube(nMinIndex, j-1, nLevel+1, nLongAxis);
1533       QuickSortACube(j+1, nMaxIndex, nLevel+1, nLongAxis);
1534    } else {
1535       tmp = gpnSortedIndex[nMaxIndex];
1536       gpnSortedIndex[nMaxIndex] = gpnSortedIndex[i];
1537       gpnSortedIndex[i] = tmp;
1538       QuickSortACube(nMinIndex, i-1, nLevel+1, nLongAxis);
1539       QuickSortACube(i+1, nMaxIndex, nLevel+1, nLongAxis);
1540    }
1541    if (dbg_sort) {
1542       DebugSortACube(nMinIndex, nMaxIndex, nLevel, nLongAxis);
1543    }
1544 }
1545 
1546 static
SweepACube(cube_index)1547 void SweepACube(cube_index)
1548    int cube_index;
1549 {
1550    register int i;
1551    int min_index=gpCube[cube_index].min_index;
1552    int max_index=gpCube[cube_index].max_index;
1553    unsigned short min_r, max_r, min_g, max_g, min_b, max_b;
1554 
1555    min_r = max_r = gpHistogram[gpnSortedIndex[min_index]].red;
1556    min_g = max_g = gpHistogram[gpnSortedIndex[min_index]].green;
1557    min_b = max_b = gpHistogram[gpnSortedIndex[min_index]].blue;
1558    gpCube[cube_index].num_points = HISTOGRAMCOUNT(gpnSortedIndex[min_index]);
1559    for (i=min_index+1; i <= max_index; i++) {
1560       unsigned short red=gpHistogram[gpnSortedIndex[i]].red;
1561       unsigned short green=gpHistogram[gpnSortedIndex[i]].green;
1562       unsigned short blue=gpHistogram[gpnSortedIndex[i]].blue;
1563 
1564       gpCube[cube_index].num_points += HISTOGRAMCOUNT(gpnSortedIndex[i]);
1565       if (red < min_r) min_r = red;
1566       if (red > max_r) max_r = red;
1567       if (green < min_g) min_g = green;
1568       if (green > max_g) max_g = green;
1569       if (blue < min_b) min_b = blue;
1570       if (blue > max_b) max_b = blue;
1571    }
1572    gpCube[cube_index].red_length = max_r-min_r;
1573    gpCube[cube_index].green_length = max_g-min_g;
1574    gpCube[cube_index].blue_length = max_b-min_b;
1575    if (gpCube[cube_index].red_length >= gpCube[cube_index].green_length) {
1576       if (gpCube[cube_index].red_length >= gpCube[cube_index].blue_length) {
1577          gpCube[cube_index].long_axis = LONG_AXIS_IS_RED;
1578       } else {
1579          gpCube[cube_index].long_axis = LONG_AXIS_IS_BLUE;
1580       }
1581    } else {
1582       if (gpCube[cube_index].green_length >= gpCube[cube_index].blue_length) {
1583          gpCube[cube_index].long_axis = LONG_AXIS_IS_GREEN;
1584       } else {
1585          gpCube[cube_index].long_axis = LONG_AXIS_IS_BLUE;
1586       }
1587    }
1588 }
1589 
1590 static
SplitACube(cube_index,pul_before_count,pul_after_count)1591 int SplitACube(cube_index, pul_before_count, pul_after_count)
1592    int cube_index;
1593    unsigned long *pul_before_count, *pul_after_count;
1594    /*
1595     * cube to be split into (min_index,return_index)
1596     * and (return_index+1,max_index)
1597     */
1598 {
1599    register int i;
1600    int min_index=gpCube[cube_index].min_index;
1601    int max_index=gpCube[cube_index].max_index;
1602    unsigned long count;
1603    unsigned long half_num_points;
1604 
1605    if (max_index == min_index+1) {
1606       *pul_before_count = HISTOGRAMCOUNT(gpnSortedIndex[min_index]);
1607       *pul_after_count = HISTOGRAMCOUNT(gpnSortedIndex[max_index]);
1608       return min_index;
1609    }
1610    count = 0;
1611    half_num_points = (gpCube[cube_index].num_points>>1);
1612    for (i=min_index; i <= max_index; i++) {
1613       unsigned long inc=HISTOGRAMCOUNT(gpnSortedIndex[i]);
1614 
1615       if (count+inc >= half_num_points) {
1616          if (i == min_index) {
1617             *pul_before_count = inc;
1618             *pul_after_count = gpCube[cube_index].num_points-inc;
1619             return i;
1620          } else if (i == max_index) {
1621             *pul_before_count = count;
1622             *pul_after_count = gpCube[cube_index].num_points-count;
1623             return i-1;
1624          } else if (count+inc == half_num_points) {
1625             *pul_before_count = count+inc;
1626             *pul_after_count = gpCube[cube_index].num_points-count-inc;
1627             return i;
1628          } else if (half_num_points-count >= count+inc-half_num_points) {
1629             if (i+1 == max_index) {
1630                *pul_before_count = count;
1631                *pul_after_count = gpCube[cube_index].num_points-count;
1632                return i;
1633             } else {
1634                *pul_before_count = count+inc;
1635                *pul_after_count = gpCube[cube_index].num_points-count-inc;
1636                return i+1;
1637             }
1638          } else {
1639             *pul_before_count = count;
1640             *pul_after_count = gpCube[cube_index].num_points-count;
1641             return i;
1642          }
1643       }
1644       count += inc;
1645    }
1646    count = HISTOGRAMCOUNT(gpnSortedIndex[max_index-1]);
1647    *pul_before_count = gpCube[cube_index].num_points-count;
1648    *pul_after_count = count;
1649    return max_index-1;
1650 }
1651 
1652 static
Quantize()1653 int Quantize()
1654    /* median-cut quantization */
1655 {
1656    int smallest_level=0, max_level=0, cube_index;
1657 
1658    gpCube = (struct CubeRec *)malloc(gnQuantizingLevels*sizeof(struct CubeRec));
1659    if (gpCube == NULL) {
1660       FailAllocMessage();
1661       return FALSE;
1662    }
1663    memset(gpCube, 0, gnQuantizingLevels*sizeof(struct CubeRec));
1664    gnCubeEntries = 1;
1665    gpCube[0].min_index = 0;
1666    gpCube[0].max_index = gnHistogramEntries-1;
1667    gpCube[0].level = 0;
1668    cube_index = 0;
1669    SweepACube(0);
1670    if (gnDebugQuantization) {
1671       int i;
1672 
1673       /* debug, do not translate */
1674       fprintf(stderr, "Original histogram in Quantize():\n");
1675       for (i=0; i < gnHistogramEntries; i++) {
1676          unsigned long count=(int)HISTOGRAMCOUNT(gpnSortedIndex[i]);
1677          int red=(int)((HISTOGRAMRED(gpnSortedIndex[i])>>8) & 0xff);
1678          int green=(int)((HISTOGRAMGREEN(gpnSortedIndex[i])>>8) & 0xff);
1679          int blue=(int)((HISTOGRAMBLUE(gpnSortedIndex[i])>>8) & 0xff);
1680 
1681          fprintf(stderr, "\t#%02x%02x%02x %6ld\n",
1682                red&0x0ff, green&0x0ff, blue&0x0ff, count);
1683       }
1684    }
1685    while (gnCubeEntries < gnQuantizingLevels) {
1686       unsigned long before_count, after_count;
1687       int split_index, new_level;
1688 
1689       while (smallest_level <= max_level) {
1690          int saved_cube_index=cube_index, found=FALSE;
1691 
1692          for ( ; cube_index < gnCubeEntries; cube_index++) {
1693             if (gpCube[cube_index].min_index != gpCube[cube_index].max_index &&
1694                   gpCube[cube_index].level == smallest_level) {
1695                found = TRUE;
1696                break;
1697             }
1698          }
1699          if (found) break;
1700          for (cube_index=0; cube_index < saved_cube_index; cube_index++) {
1701             if (gpCube[cube_index].min_index != gpCube[cube_index].max_index &&
1702                   gpCube[cube_index].level == smallest_level) {
1703                found = TRUE;
1704                break;
1705             }
1706          }
1707          if (found) break;
1708          smallest_level++;
1709       }
1710       if (smallest_level > max_level) break;
1711 
1712       /*
1713        * determine which is the longest axis
1714        */
1715       QuickSortACube(gpCube[cube_index].min_index, gpCube[cube_index].max_index,
1716             0, gpCube[cube_index].long_axis);
1717       /*
1718        * cube to be split into (min_index,split_index)
1719        * and (split_index+1,max_index)
1720        */
1721       split_index = SplitACube(cube_index, &before_count, &after_count);
1722       new_level = gpCube[cube_index].level+1;
1723       if (gnDebugQuantization) {
1724          /* debug, do not translate */
1725          fprintf(stderr,
1726                "Level %2d (%2d): [%3d,%3d] -> %6ld/[%3d,%3d] %6ld/[%3d,%3d]\n",
1727                gpCube[cube_index].level, cube_index,
1728                gpCube[cube_index].min_index, gpCube[cube_index].max_index,
1729                before_count, gpCube[cube_index].min_index, split_index,
1730                after_count, split_index+1, gpCube[cube_index].max_index);
1731       }
1732       gpCube[gnCubeEntries].min_index = split_index+1;
1733       gpCube[gnCubeEntries].max_index = gpCube[cube_index].max_index;
1734       gpCube[gnCubeEntries].level = new_level;
1735       gpCube[gnCubeEntries].num_points = after_count;
1736       SweepACube(gnCubeEntries);
1737       gnCubeEntries++;
1738 
1739       gpCube[cube_index].min_index = gpCube[cube_index].min_index;
1740       gpCube[cube_index].max_index = split_index;
1741       gpCube[cube_index].level = new_level;
1742       gpCube[cube_index].num_points = before_count;
1743       SweepACube(cube_index);
1744 
1745       if (max_level < new_level) max_level = new_level;
1746 
1747       cube_index++;
1748    }
1749    if (gnDebugQuantization) {
1750       for (cube_index=0; cube_index < gnCubeEntries; cube_index++) {
1751          int i;
1752 
1753          /* debug, do not translate */
1754          fprintf(stderr, "cube %3d: (%3d) [%3d,%3d] %6ld\n",
1755                cube_index, gpCube[cube_index].level,
1756                gpCube[cube_index].min_index, gpCube[cube_index].max_index,
1757                (long)gpCube[cube_index].num_points);
1758          for (i=gpCube[cube_index].min_index; i <= gpCube[cube_index].max_index;
1759                i++) {
1760             unsigned long count=(int)HISTOGRAMCOUNT(gpnSortedIndex[i]);
1761             int red=(int)((HISTOGRAMRED(gpnSortedIndex[i])>>8) & 0xff);
1762             int green=(int)((HISTOGRAMGREEN(gpnSortedIndex[i])>>8) & 0xff);
1763             int blue=(int)((HISTOGRAMBLUE(gpnSortedIndex[i])>>8) & 0xff);
1764 
1765             fprintf(stderr, "\t#%02x%02x%02x %6ld\n",
1766                   red&0x0ff, green&0x0ff, blue&0x0ff, count);
1767          }
1768       }
1769    }
1770    return TRUE;
1771 }
1772 
DoConvolution(fp,image,bitmap_image,w,h,xpm_ptr)1773 int DoConvolution(fp, image, bitmap_image, w, h, xpm_ptr)
1774    FILE *fp;
1775    XImage *image, *bitmap_image;
1776    int w, h;
1777    struct XPmRec *xpm_ptr;
1778 {
1779    register int j, i;
1780    int interrupted=FALSE, rc;
1781    ProgressInfo pi;
1782 
1783    if (gpConvolveFunc == NULL) {
1784       return FALSE;
1785    }
1786    memset(gaHGBucket, 0, sizeof(gaHGBucket));
1787    gnHistogramEntries = 0;
1788 
1789    if (DoPpm6(xpm_ptr)) {
1790       writeFileFailed = FALSE;
1791       if (fprintf(fp, "P6\n%1d %1d\n255\n", w, h) == EOF) {
1792          writeFileFailed = TRUE;
1793       }
1794       if (gpConvolveCmdID == CMDID_VECTORWARP) {
1795          /* already initialized in ComputeVectorWarpData(), except for fp */
1796          gConvExtraInfo.fp = fp;
1797       } else {
1798          if (!SetConvExtraInfo(fp, w, h, image, bitmap_image)) {
1799             CleanUpConvolution();
1800             return FALSE;
1801          }
1802       }
1803       ShowInterrupt(1);
1804       BeginProgress(&pi, h);
1805       for (i=0; i < h; i++) {
1806          UpdateProgress(&pi, i);
1807 
1808          if (ESCPressed() || CheckInterrupt(TRUE)) {
1809             Msg(TgLoadString(STID_USER_INTR));
1810             interrupted = TRUE;
1811             break;
1812          }
1813          for (j=0; j < w; j++) {
1814             ((ConvolveFunc*)gpConvolveFunc)(j, i);
1815          }
1816       }
1817       HideInterrupt();
1818       ClearConvExtraInfo();
1819       if (interrupted) {
1820          CleanUpConvolution();
1821          return FALSE;
1822       }
1823       CleanUpConvolution();
1824 
1825       return (writeFileFailed == FALSE);
1826    }
1827    SetStringStatus(TgLoadCachedString(CSTID_BUILDING_HISTOGRAM_DOTS));
1828    XSync(mainDisplay, False);
1829    if (!CreatePixelToIndexMapping()) {
1830       return FALSE;
1831    }
1832    gnHistogramSize = 256;
1833    gpHistogram = (XColor*)malloc(gnHistogramSize*sizeof(XColor));
1834    if (gpHistogram == NULL) {
1835       FailAllocMessage();
1836       CleanUpConvolution();
1837       return FALSE;
1838    }
1839    gnImageW = w;
1840    gnImageH = h;
1841    gnOrigImageIndex = (int**)malloc(h*sizeof(int*));
1842    if (gnOrigImageIndex == NULL) {
1843       FailAllocMessage();
1844       CleanUpConvolution();
1845       return FALSE;
1846    }
1847    memset(gnOrigImageIndex, 0, h*sizeof(int*));
1848    for (i=0; i < h; i++) {
1849       gnOrigImageIndex[i] = (int*)malloc(w*sizeof(int));
1850       if (gnOrigImageIndex[i] == NULL) {
1851          FailAllocMessage();
1852          CleanUpConvolution();
1853          return FALSE;
1854       }
1855    }
1856    if (image != NULL) {
1857       BeginProgress(&pi, h);
1858       for (i=0; i < h; i++) {
1859          UpdateProgress(&pi, i);
1860          for (j=0; j < w; j++) {
1861             /* int pixel=XGetPixel(image,j,i); */
1862             /* int index=pnPixelToIndexMap[pixel]; */
1863 
1864             gnOrigImageIndex[i][j] = GetIndexOfPixel(XGetPixel(image,j,i));
1865          }
1866       }
1867    }
1868    gnFinalImageIndex = (int**)malloc(h*sizeof(int*));
1869    if (gnFinalImageIndex == NULL) {
1870       FailAllocMessage();
1871       CleanUpConvolution();
1872       return FALSE;
1873    }
1874    memset(gnFinalImageIndex, 0, h*sizeof(int*));
1875    for (i=0; i < h; i++) {
1876       gnFinalImageIndex[i] = (int*)malloc(w*sizeof(int));
1877       if (gnFinalImageIndex[i] == NULL) {
1878          FailAllocMessage();
1879          CleanUpConvolution();
1880          return FALSE;
1881       }
1882    }
1883    ShowInterrupt(1);
1884    BeginProgress(&pi, h);
1885    for (i=0; i < h; i++) {
1886       UpdateProgress(&pi, i);
1887       if (ESCPressed() || CheckInterrupt(TRUE)) {
1888          Msg(TgLoadString(STID_USER_INTR));
1889          interrupted = TRUE;
1890          break;
1891       }
1892       for (j=0; j < w; j++) {
1893          gnFinalImageIndex[i][j] = ((ConvolveFunc*)gpConvolveFunc)(j, i);
1894       }
1895    }
1896    HideInterrupt();
1897    if (interrupted) {
1898       CleanUpConvolution();
1899       return FALSE;
1900    }
1901    gpnSortedIndex = (int*)malloc(gnHistogramEntries*sizeof(int));
1902    if (gpnSortedIndex == NULL) {
1903       FailAllocMessage();
1904       CleanUpConvolution();
1905       return FALSE;
1906    }
1907    for (i=0; i < gnHistogramEntries; i++) gpnSortedIndex[i] = i;
1908    if (gnUserSpecifiedLevels != (-1) ||
1909          gnHistogramEntries > gnQuantizingLevels) {
1910       int saved_levels=gnQuantizingLevels;
1911 
1912       if (gnUserSpecifiedLevels != (-1)) {
1913          gnQuantizingLevels = gnUserSpecifiedLevels;
1914       }
1915       sprintf(gszMsgBox, TgLoadCachedString(CSTID_QUANTIZING_COLORS_DOTS),
1916             gnHistogramEntries, gnQuantizingLevels);
1917       Msg(gszMsgBox);
1918       SetStringStatus(gszMsgBox);
1919       XSync(mainDisplay, False);
1920       if (Quantize()) {
1921          rc = DumpQuantizedConvolution(fp);
1922          if (gnUserSpecifiedLevels != (-1)) {
1923             gnQuantizingLevels = saved_levels;
1924          }
1925          CleanUpConvolution();
1926          return rc;
1927       }
1928       CleanUpConvolution();
1929       gnQuantizingLevels = saved_levels;;
1930       return FALSE;
1931    }
1932    rc = DumpConvolution(fp);
1933    CleanUpConvolution();
1934    return rc;
1935 }
1936 
1937 static
DoColorMapping(fp,image,bitmap_image,w,h,xpm_ptr)1938 int DoColorMapping(fp, image, bitmap_image, w, h, xpm_ptr)
1939    FILE *fp;
1940    XImage *image, *bitmap_image;
1941    int w, h;
1942    struct XPmRec *xpm_ptr;
1943 {
1944    register int j, i;
1945    int interrupted=FALSE, rc;
1946    ProgressInfo pi;
1947 
1948    if (gpImageMapColorFunc == NULL) {
1949       return FALSE;
1950    }
1951    SetStringStatus(TgLoadCachedString(CSTID_REMAPPING_COLORS_DOTS));
1952    XSync(mainDisplay, False);
1953 
1954    memset(gaHGBucket, 0, sizeof(gaHGBucket));
1955    gnHistogramEntries = 0;
1956 
1957    if (DoPpm6(xpm_ptr)) {
1958       writeFileFailed = FALSE;
1959       if (fprintf(fp, "P6\n%1d %1d\n255\n", w, h) == EOF) {
1960          writeFileFailed = TRUE;
1961       }
1962       ShowInterrupt(1);
1963       BeginProgress(&pi, h);
1964       for (i=0; i < h; i++) {
1965          UpdateProgress(&pi, i);
1966 
1967          if (ESCPressed() || CheckInterrupt(TRUE)) {
1968             Msg(TgLoadString(STID_USER_INTR));
1969             interrupted = TRUE;
1970             break;
1971          }
1972          for (j=0; j < w; j++) {
1973             if (bitmap_image != NULL && XGetPixel(bitmap_image,j,i) == 0) {
1974                TgAssert(FALSE, "transparent pixel not supported", NULL);
1975             } else {
1976                ((ImageMapColorFunc*)gpImageMapColorFunc)(XGetPixel(image,j,i),
1977                      (XColor*)fp);
1978             }
1979          }
1980       }
1981       HideInterrupt();
1982       if (interrupted) {
1983          CleanUpConvolution();
1984          return FALSE;
1985       }
1986       CleanUpConvolution();
1987 
1988       return (writeFileFailed == FALSE);
1989    }
1990    gnHistogramSize = 256;
1991    gpHistogram = (XColor*)malloc(gnHistogramSize*sizeof(XColor));
1992    if (gpHistogram == NULL) {
1993       FailAllocMessage();
1994       CleanUpConvolution();
1995       return FALSE;
1996    }
1997    if (!CreateObjPixelToIndexMapping(xpm_ptr)) {
1998       CleanUpConvolution();
1999       return FALSE;
2000    }
2001    gnImageW = w;
2002    gnImageH = h;
2003    gnFinalImageIndex = (int**)malloc(h*sizeof(int*));
2004    if (gnFinalImageIndex == NULL) {
2005       FailAllocMessage();
2006       CleanUpConvolution();
2007       return FALSE;
2008    }
2009    memset(gnFinalImageIndex, 0, h*sizeof(int*));
2010    for (i=0; i < h; i++) {
2011       gnFinalImageIndex[i] = (int*)malloc(w*sizeof(int));
2012       if (gnFinalImageIndex[i] == NULL) {
2013          FailAllocMessage();
2014          CleanUpConvolution();
2015          return FALSE;
2016       }
2017    }
2018    ShowInterrupt(1);
2019    BeginProgress(&pi, h);
2020    for (i=0; i < h; i++) {
2021       UpdateProgress(&pi, i);
2022 
2023       if (ESCPressed() || CheckInterrupt(TRUE)) {
2024          Msg(TgLoadString(STID_USER_INTR));
2025          interrupted = TRUE;
2026          break;
2027       }
2028       for (j=0; j < w; j++) {
2029          /* int pixel=XGetPixel(image,j,i); */
2030          /* int index=pnPixelToIndexMap[pixel]; */
2031 
2032          if (bitmap_image != NULL && XGetPixel(bitmap_image,j,i) == 0) {
2033             gnFinalImageIndex[i][j] = gnTransparentIndex;
2034          } else {
2035             gnFinalImageIndex[i][j] = GetIndexOfPixel(XGetPixel(image,j,i));
2036          }
2037       }
2038    }
2039    HideInterrupt();
2040    if (interrupted) {
2041       CleanUpConvolution();
2042       return FALSE;
2043    }
2044    rc = DumpConvolution(fp);
2045    CleanUpConvolution();
2046    return rc;
2047 }
2048 
2049 /* ----------------------- ProcessImage ----------------------- */
2050 
GetImageProcOutputFileName(pszPath,path_buf_sz,pnShortName,ppszRest)2051 FILE *GetImageProcOutputFileName(pszPath, path_buf_sz, pnShortName, ppszRest)
2052    char *pszPath, **ppszRest;
2053    int path_buf_sz, *pnShortName;
2054 {
2055    FILE *fp=NULL;
2056 
2057    if (MkTempFile(pszPath, path_buf_sz, tmpDir, TOOL_NAME) == NULL) {
2058       return NULL;
2059    }
2060    if ((*pnShortName=IsPrefix(bootDir, pszPath, ppszRest))) {
2061       *ppszRest = (&(*ppszRest)[1]);
2062    }
2063    if ((fp=fopen(pszPath, "w")) == NULL) {
2064       if (*pnShortName) {
2065          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_WRITING),
2066                *ppszRest);
2067       } else {
2068          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_WRITING),
2069                pszPath);
2070       }
2071       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2072       return NULL;
2073    }
2074    return fp;
2075 }
2076 
2077 static
CleanUpProcessImage(fp,image,bitmap_image)2078 int CleanUpProcessImage(fp, image, bitmap_image)
2079    FILE *fp;
2080    XImage *image, *bitmap_image;
2081 {
2082    if (fp != NULL) fclose(fp);
2083    if (image != NULL) XDestroyImage(image);
2084    if (bitmap_image != NULL) XDestroyImage(bitmap_image);
2085    return FALSE;
2086 }
2087 
2088 static
ProcessImage()2089 int ProcessImage()
2090 {
2091    int short_name=FALSE, ok=TRUE;
2092    char path[MAXPATHLENGTH+1], *rest=NULL;
2093    Pixmap pixmap=None, bitmap=None;
2094    XImage *image=NULL, *bitmap_image=NULL;
2095    FILE *fp=NULL;
2096    struct ObjRec *obj_ptr=NULL;
2097    int image_w=0, image_h=0;
2098    struct XPmRec *xpm_ptr=NULL;
2099 
2100    if ((fp=GetImageProcOutputFileName(path, sizeof(path), &short_name,
2101          &rest)) == NULL) {
2102       return FALSE;
2103    }
2104    if (gnCombining) {
2105       obj_ptr = NULL;
2106    } else {
2107       obj_ptr = topSel->obj;
2108    }
2109    if (obj_ptr == NULL) {
2110       pixmap = None;
2111       bitmap = None;
2112       image_w = gnCombineW;
2113       image_h = gnCombineH;
2114       image = bitmap_image = NULL;
2115    } else if (obj_ptr->type == OBJ_XPM) {
2116       xpm_ptr = obj_ptr->detail.xpm;
2117 
2118       pixmap = xpm_ptr->pixmap;
2119       bitmap = xpm_ptr->bitmap;
2120       image_w = xpm_ptr->image_w;
2121       image_h = xpm_ptr->image_h;
2122 
2123       image = XGetImage(mainDisplay, pixmap, 0, 0, image_w, image_h, AllPlanes,
2124             ZPixmap);
2125       if (bitmap != None) {
2126          bitmap_image = XGetImage(mainDisplay, bitmap, 0, 0, image_w, image_h,
2127             1, ZPixmap);
2128       }
2129       if (image == NULL || (bitmap != None && bitmap_image == NULL)) {
2130          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
2131                image_w, image_h);
2132          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2133          return CleanUpProcessImage(fp, image, bitmap_image);
2134       }
2135       if (DoPpm6(xpm_ptr)) {
2136          if (!InitTrueColorInfo(image, &gTrueColorInfo, image_w)) {
2137             sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
2138                   image_w, image_h);
2139             MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2140             return CleanUpProcessImage(fp, image, bitmap_image);
2141          }
2142          if (gpConvolveCmdID == CMDID_REDUCECOLORS) {
2143             int floyd=FALSE;
2144             char tmp_fname[MAXPATHLENGTH];
2145 
2146             switch (MsgBox(TgLoadString(STID_Q_FS_ERROR_DIFFUSE), TOOL_NAME,
2147                   YNC_MB)) {
2148             case MB_ID_YES: floyd = TRUE; break;
2149             case MB_ID_NO: floyd = FALSE; break;
2150             case MB_ID_CANCEL:
2151                return CleanUpProcessImage(fp, image, bitmap_image);
2152             }
2153             if (MkTempFile(tmp_fname, sizeof(tmp_fname), tmpDir, TOOL_NAME) ==
2154                   NULL) {
2155                return CleanUpProcessImage(fp, image, bitmap_image);
2156             }
2157             if (DumpXImageToPpmFile(image, image_w, image_h, tmp_fname,
2158                   FALSE)) {
2159                FILE *pfp=NULL;
2160                char psz_cmd[MAXPATHLENGTH<<1];
2161                int bytes_read=0;
2162 
2163                if (floyd) {
2164                   snprintf(psz_cmd, sizeof(psz_cmd), ppmFSquantCmd,
2165                         gnUserSpecifiedLevels, tmp_fname);
2166                } else {
2167                   snprintf(psz_cmd, sizeof(psz_cmd), ppmquantCmd,
2168                         gnUserSpecifiedLevels, tmp_fname);
2169                }
2170                if ((pfp=(FILE*)popen(psz_cmd,"r")) == NULL) {
2171                   sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_EXECUTE_CMD),
2172                         psz_cmd);
2173                   MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2174                   unlink(tmp_fname);
2175                   return CleanUpProcessImage(fp, image, bitmap_image);
2176                }
2177                writeFileFailed = FALSE;
2178                while ((bytes_read=fread(gszMsgBox, sizeof(char),
2179                      sizeof(gszMsgBox), pfp)) > 0) {
2180                   if ((int)fwrite(gszMsgBox, sizeof(char), bytes_read,
2181                         fp) <= 0) {
2182                      writeFileFailed = TRUE;
2183                      break;
2184                   }
2185                }
2186                pclose(pfp);
2187                if (writeFileFailed) {
2188                   FailToWriteFileMessage(short_name ? rest : tmp_fname);
2189                   unlink(tmp_fname);
2190                   return CleanUpProcessImage(fp, image, bitmap_image);
2191                }
2192                unlink(tmp_fname);
2193                CleanUpProcessImage(fp, image, bitmap_image);
2194                strcpy(gszImageProcXPmFile, path);
2195                if (gnConvolving) {
2196                   CleanUpConvolution();
2197                }
2198                return TRUE;
2199             }
2200          }
2201       }
2202    } else {
2203       return CleanUpProcessImage(fp, image, bitmap_image);
2204    }
2205    SaveStatusStrings();
2206    if (gnConvolving) {
2207       ok = DoConvolution(fp, image, bitmap_image, image_w, image_h, xpm_ptr);
2208    } else {
2209       ok = DoColorMapping(fp, image, bitmap_image, image_w, image_h, xpm_ptr);
2210    }
2211    RestoreStatusStrings();
2212    CleanUpProcessImage(fp, image, bitmap_image);
2213    if (!ok) return FALSE;
2214    strcpy(gszImageProcXPmFile, path);
2215    if (gnConvolving) {
2216       CleanUpConvolution();
2217    }
2218    return TRUE;
2219 }
2220 
2221 static
DoImageProc(pvImageMapColorFunc)2222 int DoImageProc(pvImageMapColorFunc)
2223    NLFN *pvImageMapColorFunc;
2224 {
2225    int saved_colordump=colorDump, saved_left=leftExportPixelTrim;
2226    int saved_top=topExportPixelTrim, saved_right=rightExportPixelTrim;
2227    int saved_bottom=bottomExportPixelTrim, saved_where_to_print=whereToPrint;
2228    int saved_ltx, saved_lty, saved_cur_file_defined=curFileDefined;
2229    int ltx, lty, rbx, rby, saved_w, saved_h, saved_x=0, saved_y=0;
2230    int w, h, image_w, image_h, ncolors, first_pixel_is_bg, ctm_saved=FALSE;
2231    int rc, chars_per_pixel, *pixels=NULL, retry_count=0;
2232    struct XfrmMtrxRec saved_ctm;
2233    struct BBRec saved_orig_obbox;
2234    XPoint saved_rotated_obbox[5];
2235    char *color_char=NULL, **color_str=NULL, *xpm_data=NULL;
2236    Pixmap pixmap=None, bitmap=None;
2237    XImage *image=NULL, *bitmap_image=NULL;
2238    struct ObjRec *obj_ptr;
2239    struct AttrRec *saved_fattr=NULL, *saved_lattr=NULL;
2240    struct XPmRec *xpm_ptr=NULL;
2241 
2242    if (gnCombining) {
2243       saved_ltx = selObjLtX;
2244       saved_lty = selObjLtY;
2245       saved_w = selObjRbX - saved_ltx;
2246       saved_h = selObjRbY - saved_lty;
2247       ltx = selLtX;
2248       lty = selLtY;
2249       rbx = selRbX;
2250       rby = selRbY;
2251    } else {
2252       if (topSel->obj->ctm != NULL) {
2253          ctm_saved = TRUE;
2254          saved_x = topSel->obj->x;
2255          saved_y = topSel->obj->y;
2256          memcpy(&saved_ctm, topSel->obj->ctm,
2257                sizeof(struct XfrmMtrxRec));
2258 
2259          memcpy(&saved_orig_obbox, &topSel->obj->orig_obbox,
2260                sizeof(struct BBRec));
2261          memcpy(saved_rotated_obbox, topSel->obj->rotated_obbox,
2262                5*sizeof(XPoint));
2263       }
2264       saved_ltx = topSel->obj->obbox.ltx;
2265       saved_lty = topSel->obj->obbox.lty;
2266       saved_w = topSel->obj->obbox.rbx - saved_ltx;
2267       saved_h = topSel->obj->obbox.rby - saved_lty;
2268       ltx = topSel->obj->bbox.ltx;
2269       lty = topSel->obj->bbox.lty;
2270       rbx = topSel->obj->bbox.rbx;
2271       rby = topSel->obj->bbox.rby;
2272    }
2273    leftExportPixelTrim = topExportPixelTrim = rightExportPixelTrim =
2274          bottomExportPixelTrim = 0;
2275    *gszImageProcXPmFile = '\0';
2276 
2277    curFileDefined = TRUE;
2278    whereToPrint = XBM_FILE;
2279    colorDump = TRUE;
2280    gnInImageProc = TRUE;
2281    gpImageMapColorFunc = (NLFN*)pvImageMapColorFunc;
2282 
2283    SetWatchCursor(drawWindow);
2284    SetWatchCursor(mainWindow);
2285    ProcessImage();
2286    SetDefaultCursor(mainWindow);
2287    ShowCursor();
2288 
2289    gpImageMapColorFunc = NULL;
2290    colorDump = saved_colordump;
2291    whereToPrint = saved_where_to_print;
2292    curFileDefined = saved_cur_file_defined;
2293    leftExportPixelTrim = saved_left;
2294    topExportPixelTrim = saved_top;
2295    rightExportPixelTrim = saved_right;
2296    bottomExportPixelTrim = saved_bottom;
2297 
2298    if (*gszImageProcXPmFile == '\0') {
2299       gnInImageProc = FALSE;
2300       return FALSE;
2301    }
2302    if (gnCombining) {
2303       struct SelRec *sel_ptr;
2304 
2305       HighLightReverse();
2306       PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
2307 
2308       obj_ptr = NULL;
2309       for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
2310          UnlinkObj(sel_ptr->obj);
2311          FreeObj(sel_ptr->obj);
2312       }
2313       RemoveAllSel();
2314    } else {
2315       HighLightReverse();
2316       PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
2317 
2318       obj_ptr = topSel->obj;
2319       xpm_ptr = obj_ptr->detail.xpm;
2320       saved_fattr = obj_ptr->fattr;
2321       saved_lattr = obj_ptr->lattr;
2322       obj_ptr->fattr = obj_ptr->lattr = NULL;
2323       RemoveAllSel();
2324    }
2325    gnInImageProc = FALSE;
2326    if (DoPpm6(xpm_ptr)) {
2327       char deflated_fname[MAXPATHLENGTH+1];
2328 
2329       if (obj_ptr != NULL) UnlinkObj(obj_ptr);
2330       if (obj_ptr != NULL) FreeObj(obj_ptr);
2331 
2332       ResetPngHeaderInfo(&gPngHeaderInfo);
2333       obj_ptr = CreatePpmTrueObjFromFile(gszImageProcXPmFile);
2334       if (obj_ptr != NULL &&
2335             MkTempFile(deflated_fname, sizeof(deflated_fname), tmpDir,
2336             TOOL_NAME) != NULL &&
2337             DeflateFile(gszImageProcXPmFile, deflated_fname)) {
2338          /* good */
2339       } else {
2340          FreeObj(obj_ptr);
2341 
2342          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_GIVEN_PPM),
2343                gszImageProcXPmFile);
2344          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2345          unlink(gszImageProcXPmFile);
2346          *gszImageProcXPmFile = '\0';
2347          AbortPrepareCmd(CMD_REPLACE);
2348 
2349          return FALSE;
2350       }
2351       xpm_ptr = obj_ptr->detail.xpm;
2352       xpm_ptr->real_type = PPM_TRUE;
2353       xpm_ptr->ppm_data_compress = PPM_DATA_DEFLATED;
2354       xpm_ptr->ppm_data = ReadFileIntoBuf(deflated_fname,
2355             &xpm_ptr->ppm_data_size);
2356       xpm_ptr->ppm_mask_data = NULL;
2357       xpm_ptr->ppm_mask_size = 0;
2358       unlink(deflated_fname);
2359    } else {
2360       do {
2361          int new_colormap_used=newColormapUsed;
2362 
2363          if (obj_ptr != NULL) UnlinkObj(obj_ptr);
2364          if (obj_ptr != NULL) FreeObj(obj_ptr);
2365 
2366          gnInImageProc = TRUE;
2367          if (FlushColormap()) {
2368             Msg(TgLoadString(STID_COLORMAP_FLUSHED));
2369          }
2370          gnInImageProc = FALSE;
2371 
2372          allocColorFailed = FALSE;
2373          SetWatchCursor(drawWindow);
2374          SetWatchCursor(mainWindow);
2375          rc = MyReadPixmapFile(gszImageProcXPmFile, &image_w, &image_h, &w, &h,
2376                &pixmap, &image, &bitmap, &bitmap_image, &ncolors,
2377                &chars_per_pixel, &first_pixel_is_bg, &color_char, &color_str,
2378                &pixels, &xpm_data);
2379          SetDefaultCursor(mainWindow);
2380          ShowCursor();
2381 
2382          if (rc != BitmapSuccess) {
2383             sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_XPM_FILE),
2384                   gszImageProcXPmFile);
2385             MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2386             unlink(gszImageProcXPmFile);
2387             *gszImageProcXPmFile = '\0';
2388             if (!PRTGIF && colorLayers && needToRedrawColorWindow) {
2389                RedrawColorWindow();
2390             }
2391             AbortPrepareCmd(CMD_REPLACE);
2392 
2393             return FALSE;
2394          }
2395          obj_ptr = CreateXPmObj(image_w, image_h, w, h, pixmap, image, bitmap,
2396                bitmap_image, ncolors, chars_per_pixel, FALSE, color_char,
2397                color_str, pixels, xpm_data);
2398          if (!new_colormap_used && newColormapUsed && allocColorFailed) {
2399             if (retry_count > 1) {
2400                break;
2401             }
2402             if (MsgBox(TgLoadString(STID_MAY_USED_UP_COLORS_RETRY), TOOL_NAME,
2403                   YNC_MB) != MB_ID_YES) {
2404                break;
2405             }
2406             retry_count++;
2407             if (saved_fattr != NULL) {
2408                obj_ptr->fattr = obj_ptr->lattr = NULL;
2409             }
2410          } else {
2411             break;
2412          }
2413       } while (retry_count > 0);
2414    }
2415    unlink(gszImageProcXPmFile);
2416    *gszImageProcXPmFile = '\0';
2417 
2418    obj_ptr->obbox.rbx = obj_ptr->obbox.ltx+saved_w;
2419    obj_ptr->obbox.rby = obj_ptr->obbox.lty+saved_h;
2420    AdjObjBBox(obj_ptr);
2421    AddObj(NULL, topObj, obj_ptr);
2422    MoveObj(obj_ptr, saved_ltx-obj_ptr->obbox.ltx,
2423          saved_lty-obj_ptr->obbox.lty);
2424    if (ctm_saved) {
2425       obj_ptr->x = saved_x;
2426       obj_ptr->y = saved_y;
2427       obj_ptr->ctm =
2428             (struct XfrmMtrxRec*)malloc(sizeof(struct XfrmMtrxRec));
2429       if (obj_ptr->ctm == NULL) FailAllocMessage();
2430       memcpy(obj_ptr->ctm, &saved_ctm, sizeof(struct XfrmMtrxRec));
2431 
2432       memcpy(&obj_ptr->orig_obbox, &saved_orig_obbox,
2433             sizeof(struct BBRec));
2434       memcpy(obj_ptr->rotated_obbox, saved_rotated_obbox,
2435             5*sizeof(XPoint));
2436    }
2437    if (saved_fattr != NULL) {
2438       obj_ptr->fattr = saved_fattr;
2439       obj_ptr->lattr = saved_lattr;
2440    }
2441    RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
2442          rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
2443          obj_ptr->bbox.ltx-GRID_ABS_SIZE(1),
2444          obj_ptr->bbox.lty-GRID_ABS_SIZE(1),
2445          obj_ptr->bbox.rbx+GRID_ABS_SIZE(1),
2446          obj_ptr->bbox.rby+GRID_ABS_SIZE(1));
2447 
2448    if (saved_fattr != NULL && topObj->fattr == NULL) {
2449       topObj->fattr = saved_fattr;
2450       topObj->lattr = saved_lattr;
2451    }
2452    if (!PRTGIF && colorLayers && needToRedrawColorWindow) {
2453       RedrawColorWindow();
2454    }
2455    if (gnCombining) {
2456       SelectTopObj();
2457 
2458       recordCmdUsesNewColormap = TRUE;
2459       RecordCmd(CMD_MANY_TO_ONE, NULL, topSel, botSel, 1);
2460       recordCmdUsesNewColormap = FALSE;
2461    } else {
2462       SelectTopObj();
2463 
2464       recordCmdUsesNewColormap = TRUE;
2465       RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
2466       recordCmdUsesNewColormap = FALSE;
2467    }
2468    SetFileModified(TRUE);
2469    justDupped = FALSE;
2470    return TRUE;
2471 }
2472 
2473 /* ----------------------- MakeGray ----------------------- */
2474 
2475 static
ChangeToGray(nColorIndex,pColor)2476 void ChangeToGray(nColorIndex, pColor)
2477    int nColorIndex;
2478    XColor *pColor;
2479 {
2480    struct XPmRec *xpm_ptr=topObj->detail.xpm;
2481 
2482    if (DoPpm6(xpm_ptr)) {
2483       int pixel=nColorIndex;
2484       FILE *fp=(FILE*)pColor;
2485       uint32_t pix=(uint32_t)(unsigned int)pixel;
2486       unsigned int r=0, g=0, b=0, igray=0;
2487       double dr=(double)0, dg=(double)0, db=(double)0;
2488       double dgray=(double)0;
2489       unsigned char buf[3];
2490 
2491       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
2492       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
2493       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
2494       dr = ((double)r) / gTrueColorInfo.dr_maxval;
2495       dg = ((double)g) / gTrueColorInfo.dg_maxval;
2496       db = ((double)b) / gTrueColorInfo.db_maxval;
2497 
2498       dgray = ((double)(0.299*dr + 0.587*dg + 0.114*db))*((double)256.0);
2499       igray = (dgray < ((double)0)) ? 0 : ((unsigned int)dgray);
2500       if (igray > 255) igray = 255;
2501       buf[0] = buf[1] = buf[2] = (unsigned char)(igray&0xff);
2502       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
2503    } else {
2504       int red=(int)tgifColors[nColorIndex].red;
2505       int green=(int)tgifColors[nColorIndex].green;
2506       int blue=(int)tgifColors[nColorIndex].blue;
2507       float gray=0.299*((float)red)+0.587*((float)green)+0.114*((float)blue);
2508       int val=(int)gray;
2509       int real_gray=((val>0x0ffff) ? 0x0ffff : ((val<0) ? 0 : val));
2510 
2511       pColor->red = pColor->green = pColor->blue = real_gray;
2512    }
2513 }
2514 
MakeGray()2515 void MakeGray()
2516 {
2517    if (!CheckSelectionForImageProc(CMDID_MAKEGRAY)) return;
2518    if (TrueColorTransPixelCheck(topSel->obj, CMDID_MAKEGRAY)) return;
2519 
2520    DoImageProc((NLFN*)ChangeToGray);
2521 }
2522 
2523 /* ----------------------- InvertColor ----------------------- */
2524 
2525 static
ChangeToInvertColor(nColorIndex,pColor)2526 void ChangeToInvertColor(nColorIndex, pColor)
2527    int nColorIndex;
2528    XColor *pColor;
2529 {
2530    struct XPmRec *xpm_ptr=topObj->detail.xpm;
2531 
2532    if (DoPpm6(xpm_ptr)) {
2533       int pixel=nColorIndex;
2534       FILE *fp=(FILE*)pColor;
2535       uint32_t pix=(uint32_t)(unsigned int)pixel;
2536       unsigned int r=0, g=0, b=0;
2537       double dr=(double)0, dg=(double)0, db=(double)0;
2538       unsigned char buf[3];
2539 
2540       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
2541       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
2542       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
2543       dr = ((double)r) / gTrueColorInfo.dr_maxval;
2544       dg = ((double)g) / gTrueColorInfo.dg_maxval;
2545       db = ((double)b) / gTrueColorInfo.db_maxval;
2546 
2547       dr = (((double)1.0) - dr) * ((double)256);
2548       dg = (((double)1.0) - dg) * ((double)256);
2549       db = (((double)1.0) - db) * ((double)256);
2550 
2551       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
2552       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
2553       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
2554       if (r > 255) r = 255;
2555       if (g > 255) g = 255;
2556       if (b > 255) b = 255;
2557       buf[0] = (unsigned char)r;
2558       buf[1] = (unsigned char)g;
2559       buf[2] = (unsigned char)b;
2560       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
2561    } else {
2562       int red=(int)tgifColors[nColorIndex].red;
2563       int green=(int)tgifColors[nColorIndex].green;
2564       int blue=(int)tgifColors[nColorIndex].blue;
2565 
2566       pColor->red = 0x0ffff-((unsigned int)red);
2567       pColor->green = 0x0ffff-((unsigned int)green);
2568       pColor->blue = 0x0ffff-((unsigned int)blue);
2569    }
2570 }
2571 
InvertColor()2572 void InvertColor()
2573 {
2574    if (!CheckSelectionForImageProc(CMDID_INVERTCOLOR)) return;
2575    if (TrueColorTransPixelCheck(topSel->obj, CMDID_INVERTCOLOR)) return;
2576 
2577    DoImageProc((NLFN*)ChangeToInvertColor);
2578 }
2579 
2580 /* ----------------------- InterpolateColor ----------------------- */
2581 
2582 static XColor gInterpFromColor, gInterpToColor;
2583 
2584 static
ChangeToInterpolateColor(nColorIndex,pColor)2585 void ChangeToInterpolateColor(nColorIndex, pColor)
2586    int nColorIndex;
2587    XColor *pColor;
2588 {
2589    struct XPmRec *xpm_ptr=topObj->detail.xpm;
2590 
2591    if (DoPpm6(xpm_ptr)) {
2592       int pixel=nColorIndex;
2593       FILE *fp=(FILE*)pColor;
2594       uint32_t pix=(uint32_t)(unsigned int)pixel;
2595       unsigned int r=0, g=0, b=0;
2596       double dr=(double)0, dg=(double)0, db=(double)0;
2597       unsigned char buf[3];
2598       double dgray=(double)0;
2599 
2600       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
2601       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
2602       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
2603       dr = ((double)r) / gTrueColorInfo.dr_maxval;
2604       dg = ((double)g) / gTrueColorInfo.dg_maxval;
2605       db = ((double)b) / gTrueColorInfo.db_maxval;
2606 
2607       dgray = (0.299*dr) + (0.587*dg) + (0.114*db);
2608 
2609       dr = (((double)gInterpFromColor.red) +
2610             dgray * (((double)gInterpToColor.red) -
2611             ((double)gInterpFromColor.red))) / ((double)256);
2612       dg = (((double)gInterpFromColor.green) +
2613             dgray * (((double)gInterpToColor.green) -
2614             ((double)gInterpFromColor.green))) / ((double)256);
2615       db = (((double)gInterpFromColor.blue) +
2616             dgray * (((double)gInterpToColor.blue) -
2617             ((double)gInterpFromColor.blue))) / ((double)256);
2618 
2619       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
2620       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
2621       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
2622       if (r > 255) r = 255;
2623       if (g > 255) g = 255;
2624       if (b > 255) b = 255;
2625       buf[0] = (unsigned char)r;
2626       buf[1] = (unsigned char)g;
2627       buf[2] = (unsigned char)b;
2628       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
2629    } else {
2630       int red=(int)tgifColors[nColorIndex].red;
2631       int green=(int)tgifColors[nColorIndex].green;
2632       int blue=(int)tgifColors[nColorIndex].blue;
2633       float gray=(0.299*((float)red)+0.587*((float)green)+0.114*((float)blue)) /
2634             (float)(0x0000ffff), tmp_fval;
2635       int val, real_red, real_green, real_blue;
2636 
2637       tmp_fval = ((float)gInterpFromColor.red) +
2638             gray*(((float)gInterpToColor.red)-((float)gInterpFromColor.red));
2639       val = round(tmp_fval);
2640       real_red = ((val>0x0ffff) ? 0x0ffff : ((val<0) ? 0 : val));
2641 
2642       tmp_fval = ((float)gInterpFromColor.green) +
2643             gray*(((float)gInterpToColor.green)-((float)gInterpFromColor.green));
2644       val = round(tmp_fval);
2645       real_green = ((val>0x0ffff) ? 0x0ffff : ((val<0) ? 0 : val));
2646 
2647       tmp_fval = ((float)gInterpFromColor.blue) +
2648             gray*(((float)gInterpToColor.blue)-((float)gInterpFromColor.blue));
2649       val = round(tmp_fval);
2650       real_blue = ((val>0x0ffff) ? 0x0ffff : ((val<0) ? 0 : val));
2651 
2652       pColor->red = (unsigned int)real_red;
2653       pColor->green = (unsigned int)real_green;
2654       pColor->blue = (unsigned int)real_blue;
2655    }
2656 }
2657 
InterpolateColor()2658 void InterpolateColor()
2659 {
2660    char *c_ptr, szFrom[MAXSTRING+1], szTo[MAXSTRING+1];
2661    char szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1];
2662 
2663    if (!CheckSelectionForImageProc(CMDID_INTERPOLATECOLOR)) return;
2664    if (TrueColorTransPixelCheck(topSel->obj, CMDID_INTERPOLATECOLOR)) return;
2665 
2666    *szSpec = '\0';
2667    Dialog(TgLoadString(STID_ENTER_PAIR_COLORS_INTERPOLATE),
2668          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
2669    UtilTrimBlanks(szSpec);
2670    if (*szSpec == '\0') return;
2671 
2672    strcpy(szSpecCopy, szSpec);
2673    *szFrom = *szTo = '\0';
2674    if ((c_ptr=strtok(szSpec, " ,-\t\n\r")) != NULL) {
2675       strcpy(szFrom, c_ptr);
2676       if ((c_ptr=strtok(NULL, " ,-\t\n\r")) != NULL) {
2677          strcpy(szTo, c_ptr);
2678       }
2679    }
2680    if (*szFrom == '\0' || *szTo == '\0') {
2681       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), szSpecCopy);
2682       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2683       return;
2684    }
2685    if (!TgifParseColor(szFrom, &gInterpFromColor)) {
2686       sprintf(gszMsgBox, TgLoadString(STID_GIVEN_IS_NOT_A_VALID_COLOR), szFrom);
2687       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2688       return;
2689    } else if (!TgifParseColor(szTo, &gInterpToColor)) {
2690       sprintf(gszMsgBox, TgLoadString(STID_GIVEN_IS_NOT_A_VALID_COLOR), szTo);
2691       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2692       return;
2693    }
2694    DoImageProc((NLFN*)ChangeToInterpolateColor);
2695 }
2696 
2697 /* ----------------------- Brighten/Darken ----------------------- */
2698 
2699 static double gdBrighten=(double)0;
2700 static int gnBrighten=0;
2701 
2702 static
ChangeToBrightenDarken(nColorIndex,pColor)2703 void ChangeToBrightenDarken(nColorIndex, pColor)
2704    int nColorIndex;
2705    XColor *pColor;
2706 {
2707    struct XPmRec *xpm_ptr=topObj->detail.xpm;
2708 
2709    if (DoPpm6(xpm_ptr)) {
2710       int pixel=nColorIndex;
2711       FILE *fp=(FILE*)pColor;
2712       uint32_t pix=(uint32_t)(unsigned int)pixel;
2713       unsigned int r=0, g=0, b=0;
2714       double dr=(double)0, dg=(double)0, db=(double)0;
2715       unsigned char buf[3];
2716 
2717       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
2718       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
2719       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
2720       dr = ((double)r) / gTrueColorInfo.dr_maxval;
2721       dg = ((double)g) / gTrueColorInfo.dg_maxval;
2722       db = ((double)b) / gTrueColorInfo.db_maxval;
2723 
2724       dr = (dr + gdBrighten) * ((double)256);
2725       dg = (dg + gdBrighten) * ((double)256);
2726       db = (db + gdBrighten) * ((double)256);
2727 
2728       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
2729       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
2730       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
2731       if (r > 255) r = 255;
2732       if (g > 255) g = 255;
2733       if (b > 255) b = 255;
2734       buf[0] = (unsigned char)r;
2735       buf[1] = (unsigned char)g;
2736       buf[2] = (unsigned char)b;
2737       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
2738    } else {
2739       int red=((int)tgifColors[nColorIndex].red)+gnBrighten;
2740       int green=((int)tgifColors[nColorIndex].green)+gnBrighten;
2741       int blue=((int)tgifColors[nColorIndex].blue)+gnBrighten;
2742       int real_red, real_green, real_blue;
2743 
2744       real_red = ((red>0x0ffff) ? 0x0ffff : ((red<0) ? 0 : red));
2745       real_green = ((green>0x0ffff) ? 0x0ffff : ((green<0) ? 0 : green));
2746       real_blue = ((blue>0x0ffff) ? 0x0ffff : ((blue<0) ? 0 : blue));
2747 
2748       pColor->red = (unsigned int)real_red;
2749       pColor->green = (unsigned int)real_green;
2750       pColor->blue = (unsigned int)real_blue;
2751    }
2752 }
2753 
BrightenDarken()2754 void BrightenDarken()
2755 {
2756    char *c_ptr, szValue[MAXSTRING+1];
2757    char szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1];
2758    float fVal;
2759 
2760    if (!CheckSelectionForImageProc(CMDID_BRIGHTENDARKEN)) return;
2761    if (TrueColorTransPixelCheck(topSel->obj, CMDID_BRIGHTENDARKEN)) return;
2762 
2763    *szSpec = '\0';
2764    Dialog(TgLoadString(STID_ENTER_VAL_MINUS_PLUS_ONE_BW),
2765          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
2766    UtilTrimBlanks(szSpec);
2767    if (*szSpec == '\0') return;
2768 
2769    strcpy(szSpecCopy, szSpec);
2770    if ((c_ptr=strtok(szSpec, " ,\t\n\r")) == NULL) return;
2771    strcpy(szValue, c_ptr);
2772    if (sscanf(szValue, "%f", &fVal) != 1) {
2773       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_A_VAL),
2774             szSpecCopy);
2775       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2776       return;
2777    }
2778    gdBrighten = ((double)fVal);
2779    gnBrighten = (int)round(gdBrighten*((double)0x0000ffff));
2780 
2781    DoImageProc((NLFN*)ChangeToBrightenDarken);
2782 }
2783 
2784 /* ----------------------- ChangeSaturation ----------------------- */
2785 
2786 /*
2787  * 0                  65535          spread
2788  * |--------------------r-----| s = -------- * 65535
2789  * |---g----------------------|        v
2790  * |-----------b--------------|
2791  * |<---------v-------->|              x
2792  *     |<----spread---->|       h = -------- * 60
2793  *     |<--x-->|                     spread
2794  *
2795  * 0     60   120   180   240   300   360
2796  * |--r--|--g--|--g--|--b--|--b--|--r--|
2797  */
2798 
2799 static double gfSaturation=(double)0.0;
2800 
RGBtoHSV(r,g,b,h,s,v)2801 void RGBtoHSV(r, g, b, h, s, v)
2802    int r, g, b, *h, *v;
2803    double *s;
2804    /* 0 <= r,g,b <= 0x0ffff */
2805    /* 0 <= *h < 360 */
2806    /* 0 <= *s,*v <= 0x0ffff */
2807 {
2808    int max_val=max(r,max(g,b)), min_val=min(r,min(g,b));
2809    double spread=(double)(max_val-min_val);
2810 
2811    *v = max_val;
2812    if (max_val == 0) {
2813       *s = (double)0.0;
2814    } else {
2815       *s = (double)((spread*((double)0x0ffff))/((double)max_val));
2816    }
2817    if (*s < (double)0.0) *s = (double)0.0;
2818    if (*s > INT_TOL) {
2819       int hue=0;
2820 
2821       if (r == max_val) {
2822          hue = (int)(((double)(g-b))/spread*((double)60.0));
2823          if (hue < -60) hue = -60;
2824          if (hue < 0) {
2825             hue += 360;
2826          } else if (hue > 60) {
2827             hue = 60;
2828          }
2829       } else if (g == max_val) {
2830          hue = (int)(((double)120.0) + (((double)(b-r))/spread*((double)60.0)));
2831          if (hue < 60) hue = 60;
2832          if (hue > 180) hue = 180;
2833       } else if (b == max_val) {
2834          hue = (int)(((double)240.0) + (((double)(r-g))/spread*((double)60.0)));
2835          if (hue < 180) hue = 180;
2836          if (hue > 300) hue = 300;
2837       }
2838       *h = hue;
2839    } else {
2840       *h = 0;
2841    }
2842 }
2843 
2844 #define R_IS_MAX 0
2845 #define G_IS_MAX 1
2846 #define B_IS_MAX 2
2847 
HSVtoRGB(h,s,v,r,g,b)2848 void HSVtoRGB(h, s, v, r, g, b)
2849    double s;
2850    int h, v, *r, *g, *b;
2851    /* 0 <= *r,*g,*b <= 0x0ffff */
2852    /* 0 <= h < 360 */
2853    /* 0 <= s,v <= 0x0ffff */
2854 {
2855    if (s <= INT_TOL) {
2856       *r = *g = *b = v;
2857    } else {
2858       double frac, spread;
2859       int which, min_val, mid_val;
2860 
2861       if (h >= 300) {
2862          frac = (((double)(360-h))/((double)60.0));
2863          which = R_IS_MAX;
2864       } else if (h >= 240) {
2865          frac = (((double)(h-240))/((double)60.0));
2866          which = B_IS_MAX;
2867       } else if (h >= 180) {
2868          frac = (((double)(240-h))/((double)60.0));
2869          which = B_IS_MAX;
2870       } else if (h >= 120) {
2871          frac = (((double)(h-120))/((double)60.0));
2872          which = G_IS_MAX;
2873       } else if (h >= 60) {
2874          frac = (((double)(120-h))/((double)60.0));
2875          which = G_IS_MAX;
2876       } else {
2877          frac = (((double)h)/((double)60.0));
2878          which = R_IS_MAX;
2879       }
2880       spread = (((double)v)*s/((double)0x0ffff));
2881       min_val = (int)(v-spread);
2882       mid_val = min_val+((int)(frac*spread));
2883 
2884       switch (which) {
2885       case R_IS_MAX:
2886          *r = v;
2887          if (h >= 300) {
2888             /* g < b */  *g = min_val; *b = mid_val;
2889          } else {
2890             /* g >= b */ *g = mid_val; *b = min_val;
2891          }
2892          break;
2893       case G_IS_MAX:
2894          *g = v;
2895          if (h >= 120) {
2896             /* b >= r */ *b = mid_val; *r = min_val;
2897          } else {
2898             /* b < r */  *b = min_val; *r = mid_val;
2899          }
2900          break;
2901       case B_IS_MAX:
2902          *b = v;
2903          if (h >= 240) {
2904             /* r >= g */ *r = mid_val; *g = min_val;
2905          } else {
2906             /* r < g */  *r = min_val; *g = mid_val;
2907          }
2908          break;
2909       }
2910    }
2911 }
2912 
2913 static
ChangeToChangeSaturation(nColorIndex,pColor)2914 void ChangeToChangeSaturation(nColorIndex, pColor)
2915    int nColorIndex;
2916    XColor *pColor;
2917 {
2918    struct XPmRec *xpm_ptr=topObj->detail.xpm;
2919 
2920    if (DoPpm6(xpm_ptr)) {
2921       int pixel=nColorIndex;
2922       FILE *fp=(FILE*)pColor;
2923       uint32_t pix=(uint32_t)(unsigned int)pixel;
2924       unsigned int r=0, g=0, b=0;
2925       double dr=(double)0, dg=(double)0, db=(double)0;
2926       unsigned char buf[3];
2927       int h=0, v=0;
2928       double s=(double)0;
2929 
2930       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
2931       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
2932       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
2933       dr = ((double)r) / gTrueColorInfo.dr_maxval;
2934       dg = ((double)g) / gTrueColorInfo.dg_maxval;
2935       db = ((double)b) / gTrueColorInfo.db_maxval;
2936 
2937       dr *= ((double)0x10000);
2938       dg *= ((double)0x10000);
2939       db *= ((double)0x10000);
2940 
2941       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
2942       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
2943       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
2944       if (r > 0x0ffff) r = 0x0ffff;
2945       if (g > 0x0ffff) g = 0x0ffff;
2946       if (b > 0x0ffff) b = 0x0ffff;
2947 
2948       RGBtoHSV(r, g, b, &h, &s, &v);
2949       s = s * (((double)1.0)+gfSaturation);
2950       if (s > (double)0x0ffff) s = (double)0x0ffff;
2951       if (s < (double)0.0) s = (double)0.0;
2952       HSVtoRGB(h, s, v, (int*)(&r), (int*)(&g), (int*)(&b));
2953 
2954 
2955       dr = ((double)r) / ((double)256);
2956       dg = ((double)g) / ((double)256);
2957       db = ((double)b) / ((double)256);
2958 
2959       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
2960       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
2961       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
2962       if (r > 255) r = 255;
2963       if (g > 255) g = 255;
2964       if (b > 255) b = 255;
2965       buf[0] = (unsigned char)r;
2966       buf[1] = (unsigned char)g;
2967       buf[2] = (unsigned char)b;
2968       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
2969    } else {
2970       int red=((int)tgifColors[nColorIndex].red);
2971       int green=((int)tgifColors[nColorIndex].green);
2972       int blue=((int)tgifColors[nColorIndex].blue);
2973       int h, v, real_red, real_green, real_blue;
2974       double s=(double)0;
2975 
2976       RGBtoHSV(red, green, blue, &h, &s, &v);
2977       s = s * (((double)1.0)+gfSaturation);
2978       if (s > (double)0x0ffff) s = (double)0x0ffff;
2979       if (s < (double)0.0) s = (double)0.0;
2980       HSVtoRGB(h, s, v, &red, &green, &blue);
2981 
2982       real_red = ((red>0x0ffff) ? 0x0ffff : ((red<0) ? 0 : red));
2983       real_green = ((green>0x0ffff) ? 0x0ffff : ((green<0) ? 0 : green));
2984       real_blue = ((blue>0x0ffff) ? 0x0ffff : ((blue<0) ? 0 : blue));
2985 
2986       pColor->red = (unsigned int)real_red;
2987       pColor->green = (unsigned int)real_green;
2988       pColor->blue = (unsigned int)real_blue;
2989    }
2990 }
2991 
ChangeSaturation()2992 void ChangeSaturation()
2993 {
2994    char *c_ptr, szValue[MAXSTRING+1];
2995    char szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1];
2996    float fVal;
2997 
2998    if (!CheckSelectionForImageProc(CMDID_CHANGESATURATION)) return;
2999    if (TrueColorTransPixelCheck(topSel->obj, CMDID_CHANGESATURATION)) return;
3000 
3001    *szSpec = '\0';
3002    Dialog(TgLoadString(STID_ENTER_VAL_MINUS_PLUS_ONE_SAT),
3003          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
3004    UtilTrimBlanks(szSpec);
3005    if (*szSpec == '\0') return;
3006 
3007    strcpy(szSpecCopy, szSpec);
3008    if ((c_ptr=strtok(szSpec, " ,\t\n\r")) == NULL) return;
3009    strcpy(szValue, c_ptr);
3010    if (sscanf(szValue, "%f", &fVal) != 1) {
3011       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_A_VAL),
3012             szSpecCopy);
3013       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3014       return;
3015    }
3016    if (fVal > (float)1.0) fVal = (float)1.0;
3017    if (fVal < (float)(-1.0)) fVal = (float)(-1.0);
3018    gfSaturation = fVal;
3019 
3020    DoImageProc((NLFN*)ChangeToChangeSaturation);
3021 }
3022 
3023 /* ----------------------- ChangeHue ----------------------- */
3024 
3025 static int gnFromHue=0, gnToHue=0;
3026 static float gfFromAngle=(float)0.0, gfToAngle=(float)0.0;
3027 static float gfFromStart=(float)0.0, gfToStart=(float)0.0;
3028 static float gfFromEnd=(float)0.0, gfToEnd=(float)0.0;
3029 
3030 static
HueInFromRange(fHue)3031 int HueInFromRange(fHue)
3032    float fHue;
3033 {
3034    if (gfFromStart >= gfFromEnd) {
3035       if (gfFromEnd <= fHue && fHue <= gfFromStart) {
3036          return TRUE;
3037       }
3038    } else {
3039       if (gfFromStart <= fHue && fHue <= gfFromEnd) {
3040          return TRUE;
3041       }
3042    }
3043    return FALSE;
3044 }
3045 
3046 static
ChangeToChangeHue(nColorIndex,pColor)3047 void ChangeToChangeHue(nColorIndex, pColor)
3048    int nColorIndex;
3049    XColor *pColor;
3050 {
3051    struct XPmRec *xpm_ptr=topObj->detail.xpm;
3052 
3053    if (DoPpm6(xpm_ptr)) {
3054       int pixel=nColorIndex;
3055       FILE *fp=(FILE*)pColor;
3056       uint32_t pix=(uint32_t)(unsigned int)pixel;
3057       unsigned int r=0, g=0, b=0;
3058       double dr=(double)0, dg=(double)0, db=(double)0;
3059       unsigned char buf[3];
3060       int h=0, v=0;
3061       double s=(double)0;
3062 
3063       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
3064       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
3065       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
3066       dr = ((double)r) / gTrueColorInfo.dr_maxval;
3067       dg = ((double)g) / gTrueColorInfo.dg_maxval;
3068       db = ((double)b) / gTrueColorInfo.db_maxval;
3069 
3070       dr *= ((double)0x10000);
3071       dg *= ((double)0x10000);
3072       db *= ((double)0x10000);
3073 
3074       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
3075       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
3076       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
3077       if (r > 0x0ffff) r = 0x0ffff;
3078       if (g > 0x0ffff) g = 0x0ffff;
3079       if (b > 0x0ffff) b = 0x0ffff;
3080 
3081       RGBtoHSV(r, g, b, &h, &s, &v);
3082       if (s > INT_TOL && h > 300) h -= 360;
3083       if (s > INT_TOL && HueInFromRange((double)h)) {
3084          double fFraction=(double)0.0;
3085 
3086          if (fabs(gfFromAngle) > INT_TOL) {
3087             fFraction = (((double)h)-gfFromStart)/(gfFromAngle*((double)2.0));
3088          }
3089          h = (int)(fFraction*gfToAngle*((double)2.0) + gfToStart);
3090          while (h >= 360) h -= 360;
3091          while (h < 0) h += 360;
3092          HSVtoRGB(h, s, v, (int*)(&r), (int*)(&g), (int*)(&b));
3093       }
3094       dr = ((double)r) / ((double)256);
3095       dg = ((double)g) / ((double)256);
3096       db = ((double)b) / ((double)256);
3097 
3098       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
3099       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
3100       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
3101       if (r > 255) r = 255;
3102       if (g > 255) g = 255;
3103       if (b > 255) b = 255;
3104       buf[0] = (unsigned char)r;
3105       buf[1] = (unsigned char)g;
3106       buf[2] = (unsigned char)b;
3107       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
3108    } else {
3109       int red=((int)tgifColors[nColorIndex].red);
3110       int green=((int)tgifColors[nColorIndex].green);
3111       int blue=((int)tgifColors[nColorIndex].blue);
3112       int h, v, real_red, real_green, real_blue;
3113       double s=(double)0;
3114 
3115       RGBtoHSV(red, green, blue, &h, &s, &v);
3116       if (s > INT_TOL && h > 300) h -= 360;
3117       if (s > INT_TOL && HueInFromRange((double)h)) {
3118          double fFraction=(double)0.0;
3119 
3120          if (fabs(gfFromAngle) > INT_TOL) {
3121             fFraction = (((double)h)-gfFromStart)/(gfFromAngle*((double)2.0));
3122          }
3123          h = (int)(fFraction*gfToAngle*((double)2.0) + gfToStart);
3124          while (h >= 360) h -= 360;
3125          while (h < 0) h += 360;
3126          HSVtoRGB(h, s, v, &red, &green, &blue);
3127 
3128          real_red = ((red>0x0ffff) ? 0x0ffff : ((red<0) ? 0 : red));
3129          real_green = ((green>0x0ffff) ? 0x0ffff : ((green<0) ? 0 : green));
3130          real_blue = ((blue>0x0ffff) ? 0x0ffff : ((blue<0) ? 0 : blue));
3131 
3132          pColor->red = (unsigned int)real_red;
3133          pColor->green = (unsigned int)real_green;
3134          pColor->blue = (unsigned int)real_blue;
3135       } else {
3136          pColor->red = (unsigned int)red;
3137          pColor->green = (unsigned int)green;
3138          pColor->blue = (unsigned int)blue;
3139       }
3140    }
3141 }
3142 
ChangeHue()3143 void ChangeHue()
3144 {
3145    char *szFrom=NULL, *szFromAngle=NULL, *szTo=NULL, *szToAngle=NULL;
3146    char szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1], szValue[MAXSTRING+1];
3147    double sVal=(double)0, dVal=(double)0;
3148    int vVal;
3149    XColor xcolor;
3150 
3151    if (!CheckSelectionForImageProc(CMDID_CHANGEHUE)) return;
3152    if (TrueColorTransPixelCheck(topSel->obj, CMDID_CHANGEHUE)) return;
3153 
3154    *szSpec = '\0';
3155    Dialog(TgLoadString(STID_ENTER_VAL_FOR_CHANGE_HUE), NULL, szSpec);
3156    UtilTrimBlanks(szSpec);
3157    if (*szSpec == '\0') return;
3158 
3159    strcpy(szSpecCopy, szSpec);
3160    if ((szFrom=strtok(szSpec, " ,\t\n\r")) == NULL ||
3161          (szFromAngle=strtok(NULL, " ,\t\n\r")) == NULL ||
3162          (szTo=strtok(NULL, " ,\t\n\r")) == NULL ||
3163          (szToAngle=strtok(NULL, " ,\t\n\r")) == NULL) {
3164       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_4_VAL),
3165             szSpecCopy);
3166       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3167       return;
3168    }
3169    if (!TgifParseColor(szFrom, &xcolor)) {
3170       sprintf(gszMsgBox, TgLoadString(STID_GIVEN_IS_NOT_A_VALID_COLOR), szFrom);
3171       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3172       return;
3173    }
3174    RGBtoHSV(((int)xcolor.red), ((int)xcolor.green), ((int)xcolor.blue),
3175          &gnFromHue, &sVal, &vVal);
3176    if (!TgifParseColor(szTo, &xcolor)) {
3177       sprintf(gszMsgBox, TgLoadString(STID_GIVEN_IS_NOT_A_VALID_COLOR), szTo);
3178       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3179       return;
3180    }
3181    RGBtoHSV(((int)xcolor.red), ((int)xcolor.green), ((int)xcolor.blue),
3182          &gnToHue, &sVal, &vVal);
3183 
3184    strcpy(szValue, szFromAngle);
3185    if (sscanf(szValue, "%lf", &dVal) != 1) {
3186       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_A_VAL),
3187             szFromAngle);
3188       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3189       return;
3190    }
3191    if (dVal > (double)180.0) dVal = (double)180.0;
3192    if (dVal < (double)(-180.0)) dVal = (double)(-180.0);
3193    gfFromAngle = (float)dVal;
3194    gfFromStart = ((double)gnFromHue)-gfFromAngle;
3195    gfFromEnd = ((double)gnFromHue)+gfFromAngle;
3196 
3197    strcpy(szValue, szToAngle);
3198    if (sscanf(szValue, "%lf", &dVal) != 1) {
3199       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_A_VAL), szToAngle);
3200       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3201       return;
3202    }
3203    if (dVal > (double)180.0) dVal = (double)180.0;
3204    if (dVal < (double)(-180.0)) dVal = (double)(-180.0);
3205    gfToAngle = (float)dVal;
3206    gfToStart = ((double)gnToHue)-gfToAngle;
3207    gfToEnd = ((double)gnToHue)+gfToAngle;
3208 
3209    DoImageProc((NLFN*)ChangeToChangeHue);
3210 }
3211 
3212 /* ----------------------- ContrastEnhance ----------------------- */
3213 
3214 static float gfContrastFactor=1.0;
3215 
3216 static
ChangeToContrastEnhance(nColorIndex,pColor)3217 void ChangeToContrastEnhance(nColorIndex, pColor)
3218    int nColorIndex;
3219    XColor *pColor;
3220 {
3221    struct XPmRec *xpm_ptr=topObj->detail.xpm;
3222 
3223    if (DoPpm6(xpm_ptr)) {
3224       int pixel=nColorIndex;
3225       FILE *fp=(FILE*)pColor;
3226       uint32_t pix=(uint32_t)(unsigned int)pixel;
3227       unsigned int r=0, g=0, b=0;
3228       double dr=(double)0, dg=(double)0, db=(double)0;
3229       unsigned char buf[3];
3230 
3231       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
3232       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
3233       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
3234       dr = ((double)r) / gTrueColorInfo.dr_maxval;
3235       dg = ((double)g) / gTrueColorInfo.dg_maxval;
3236       db = ((double)b) / gTrueColorInfo.db_maxval;
3237 
3238       dr = ((dr-0.5) * ((double)gfContrastFactor) + 0.5) * 256.0;
3239       dg = ((dg-0.5) * ((double)gfContrastFactor) + 0.5) * 256.0;
3240       db = ((db-0.5) * ((double)gfContrastFactor) + 0.5) * 256.0;
3241 
3242       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
3243       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
3244       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
3245       if (r > 255) r = 255;
3246       if (g > 255) g = 255;
3247       if (b > 255) b = 255;
3248       buf[0] = (unsigned char)r;
3249       buf[1] = (unsigned char)g;
3250       buf[2] = (unsigned char)b;
3251       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
3252    } else {
3253       int red=(int)tgifColors[nColorIndex].red;
3254       int green=(int)tgifColors[nColorIndex].green;
3255       int blue=(int)tgifColors[nColorIndex].blue;
3256       int ival, real_red, real_green, real_blue;
3257       float fval;
3258 
3259       fval = ((float)(red-0x8000))*gfContrastFactor + ((float)0x8000);
3260       ival = round(fval);
3261       real_red = ((ival>0x0ffff) ? 0x0ffff : ((ival<0) ? 0 : ival));
3262       fval = ((float)(green-0x8000))*gfContrastFactor + ((float)0x8000);
3263       ival = round(fval);
3264       real_green = ((ival>0x0ffff) ? 0x0ffff : ((ival<0) ? 0 : ival));
3265       fval = ((float)(blue-0x8000))*gfContrastFactor + ((float)0x8000);
3266       ival = round(fval);
3267       real_blue = ((ival>0x0ffff) ? 0x0ffff : ((ival<0) ? 0 : ival));
3268 
3269       pColor->red = (unsigned int)real_red;
3270       pColor->green = (unsigned int)real_green;
3271       pColor->blue = (unsigned int)real_blue;
3272    }
3273 }
3274 
ContrastEnhance()3275 void ContrastEnhance()
3276 {
3277    char *c_ptr, szValue[MAXSTRING+1];
3278    char szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1];
3279    float fVal;
3280 
3281    if (!CheckSelectionForImageProc(CMDID_CONTRASTENHANCE)) return;
3282    if (TrueColorTransPixelCheck(topSel->obj, CMDID_CONTRASTENHANCE)) return;
3283 
3284    *szSpec = '\0';
3285    Dialog(TgLoadString(STID_ENTER_VAL_FOR_CONTRAST_ENH),
3286          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
3287    UtilTrimBlanks(szSpec);
3288    if (*szSpec == '\0') return;
3289 
3290    strcpy(szSpecCopy, szSpec);
3291    if ((c_ptr=strtok(szSpec, " ,\t\n\r")) == NULL) return;
3292    strcpy(szValue, c_ptr);
3293    if (strcmp(szValue, "1.0") == 0 || strcmp(szValue, "1") == 0 ||
3294          strcmp(szValue, "1.") == 0) {
3295       return;
3296    } else if (sscanf(szValue, "%f", &fVal) != 1) {
3297       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_A_VAL),
3298             szSpecCopy);
3299       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3300       return;
3301    } else if (fVal < (float)0.0) {
3302       sprintf(gszMsgBox, TgLoadString(STID_GIVEN_NEG_VAL_NOT_ALLOWED),
3303             szSpecCopy);
3304       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3305       return;
3306    }
3307    gfContrastFactor = fVal;
3308 
3309    DoImageProc((NLFN*)ChangeToContrastEnhance);
3310 }
3311 
3312 /* ----------------------- ColorBalance ----------------------- */
3313 
3314 static float gfRedBalanceFactor=1.0;
3315 static float gfGreenBalanceFactor=1.0;
3316 static float gfBlueBalanceFactor=1.0;
3317 
3318 static
ChangeToColorBalance(nColorIndex,pColor)3319 void ChangeToColorBalance(nColorIndex, pColor)
3320    int nColorIndex;
3321    XColor *pColor;
3322 {
3323    struct XPmRec *xpm_ptr=topObj->detail.xpm;
3324 
3325    if (DoPpm6(xpm_ptr)) {
3326       int pixel=nColorIndex;
3327       FILE *fp=(FILE*)pColor;
3328       uint32_t pix=(uint32_t)(unsigned int)pixel;
3329       unsigned int r=0, g=0, b=0;
3330       double dr=(double)0, dg=(double)0, db=(double)0;
3331       unsigned char buf[3];
3332 
3333       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
3334       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
3335       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
3336       dr = ((double)r) / gTrueColorInfo.dr_maxval;
3337       dg = ((double)g) / gTrueColorInfo.dg_maxval;
3338       db = ((double)b) / gTrueColorInfo.db_maxval;
3339 
3340       dr = dr * gfRedBalanceFactor * ((double)256);
3341       dg = dg * gfGreenBalanceFactor * ((double)256);
3342       db = db * gfBlueBalanceFactor * ((double)256);
3343 
3344       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
3345       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
3346       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
3347       if (r > 255) r = 255;
3348       if (g > 255) g = 255;
3349       if (b > 255) b = 255;
3350       buf[0] = (unsigned char)r;
3351       buf[1] = (unsigned char)g;
3352       buf[2] = (unsigned char)b;
3353       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
3354    } else {
3355       int red=(int)tgifColors[nColorIndex].red;
3356       int green=(int)tgifColors[nColorIndex].green;
3357       int blue=(int)tgifColors[nColorIndex].blue;
3358       int ival, real_red, real_green, real_blue;
3359       float fval;
3360 
3361       fval = ((float)red) * gfRedBalanceFactor;
3362       ival = round(fval);
3363       real_red = ((ival>0x0ffff) ? 0x0ffff : ((ival<0) ? 0 : ival));
3364       fval = ((float)green) * gfGreenBalanceFactor;
3365       ival = round(fval);
3366       real_green = ((ival>0x0ffff) ? 0x0ffff : ((ival<0) ? 0 : ival));
3367       fval = ((float)blue) * gfBlueBalanceFactor;
3368       ival = round(fval);
3369       real_blue = ((ival>0x0ffff) ? 0x0ffff : ((ival<0) ? 0 : ival));
3370 
3371       pColor->red = (unsigned int)real_red;
3372       pColor->green = (unsigned int)real_green;
3373       pColor->blue = (unsigned int)real_blue;
3374    }
3375 }
3376 
ColorBalance()3377 void ColorBalance()
3378 {
3379    char *c_ptr, szRed[MAXSTRING+1], szGreen[MAXSTRING+1], szBlue[MAXSTRING+1];
3380    char szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1];
3381 
3382    if (!CheckSelectionForImageProc(CMDID_COLORBALANCE)) return;
3383    if (TrueColorTransPixelCheck(topSel->obj, CMDID_COLORBALANCE)) return;
3384 
3385    *szSpec = '\0';
3386    Dialog(TgLoadString(STID_ENTER_VAL_FOR_COLOR_BAL),
3387          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
3388    UtilTrimBlanks(szSpec);
3389    if (*szSpec == '\0') return;
3390 
3391    strcpy(szSpecCopy, szSpec);
3392    *szRed = *szGreen = *szBlue = '\0';
3393    if ((c_ptr=strtok(szSpec, " ,\t\n\r")) != NULL) {
3394       strcpy(szRed, c_ptr);
3395       if ((c_ptr=strtok(NULL, " ,\t\n\r")) != NULL) {
3396          strcpy(szGreen, c_ptr);
3397          if ((c_ptr=strtok(NULL, " ,\t\n\r")) != NULL) {
3398             strcpy(szBlue, c_ptr);
3399          }
3400       }
3401    }
3402    if (*szRed == '\0' || *szGreen == '\0' || *szBlue == '\0' ||
3403          sscanf(szRed, "%f", &gfRedBalanceFactor) != 1 ||
3404          sscanf(szGreen, "%f", &gfGreenBalanceFactor) != 1 ||
3405          sscanf(szBlue, "%f", &gfBlueBalanceFactor) != 1) {
3406       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_3_VAL),
3407             szSpecCopy);
3408       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3409       return;
3410    } else if (gfRedBalanceFactor < (float)0.0 ||
3411          gfGreenBalanceFactor < (float)0.0 ||
3412          gfBlueBalanceFactor < (float)0.0) {
3413       sprintf(gszMsgBox, TgLoadString(STID_GIVEN_NEG_VAL_NOT_ALLOWED),
3414             szSpecCopy);
3415       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3416       return;
3417    }
3418 
3419    DoImageProc((NLFN*)ChangeToColorBalance);
3420 }
3421 
3422 /* ----------------------- Gamma ----------------------- */
3423 
3424 static float gfOneOverGamma=1.0;
3425 
3426 static
ChangeToGamma(nColorIndex,pColor)3427 void ChangeToGamma(nColorIndex, pColor)
3428    int nColorIndex;
3429    XColor *pColor;
3430 {
3431    struct XPmRec *xpm_ptr=topObj->detail.xpm;
3432 
3433    if (DoPpm6(xpm_ptr)) {
3434       int pixel=nColorIndex;
3435       FILE *fp=(FILE*)pColor;
3436       uint32_t pix=(uint32_t)(unsigned int)pixel;
3437       unsigned int r=0, g=0, b=0;
3438       double dr=(double)0, dg=(double)0, db=(double)0;
3439       unsigned char buf[3];
3440 
3441       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
3442       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
3443       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
3444       dr = ((double)r) / gTrueColorInfo.dr_maxval;
3445       dg = ((double)g) / gTrueColorInfo.dg_maxval;
3446       db = ((double)b) / gTrueColorInfo.db_maxval;
3447 
3448       dr = pow(dr, (double)gfOneOverGamma) * ((double)256);
3449       dg = pow(dg, (double)gfOneOverGamma) * ((double)256);
3450       db = pow(db, (double)gfOneOverGamma) * ((double)256);
3451 
3452       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
3453       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
3454       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
3455       if (r > 255) r = 255;
3456       if (g > 255) g = 255;
3457       if (b > 255) b = 255;
3458       buf[0] = (unsigned char)r;
3459       buf[1] = (unsigned char)g;
3460       buf[2] = (unsigned char)b;
3461       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
3462    } else {
3463       double red=((double)tgifColors[nColorIndex].red)/((double)0x0ffff);
3464       double green=((double)tgifColors[nColorIndex].green)/((double)0x0ffff);
3465       double blue=((double)tgifColors[nColorIndex].blue)/((double)0x0ffff);
3466       int ival, real_red, real_green, real_blue;
3467       double dval;
3468 
3469       dval = pow(red, (double)gfOneOverGamma) * ((double)0x010000);
3470       ival = round(dval);
3471       real_red = ((ival>0x0ffff) ? 0x0ffff : ((ival<0) ? 0 : ival));
3472       dval = pow(green, (double)gfOneOverGamma) * ((double)0x010000);
3473       ival = round(dval);
3474       real_green = ((ival>0x0ffff) ? 0x0ffff : ((ival<0) ? 0 : ival));
3475       dval = pow(blue, (double)gfOneOverGamma) * ((double)0x010000);
3476       ival = round(dval);
3477       real_blue = ((ival>0x0ffff) ? 0x0ffff : ((ival<0) ? 0 : ival));
3478 
3479       pColor->red = (unsigned int)real_red;
3480       pColor->green = (unsigned int)real_green;
3481       pColor->blue = (unsigned int)real_blue;
3482    }
3483 }
3484 
Gamma(buf)3485 void Gamma(buf)
3486    char *buf;
3487 {
3488    char *c_ptr, szValue[MAXSTRING+1];
3489    char szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1];
3490    float gamma=0.0;
3491 
3492    if (!CheckSelectionForImageProc(CMDID_GAMMA)) return;
3493    if (TrueColorTransPixelCheck(topSel->obj, CMDID_GAMMA)) return;
3494 
3495    if (buf != NULL) {
3496       strcpy(szSpec, buf);
3497    } else {
3498       *szSpec = '\0';
3499       Dialog(TgLoadString(STID_ENTER_VAL_FOR_GAMMA),
3500             TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
3501    }
3502    UtilTrimBlanks(szSpec);
3503    if (*szSpec == '\0') return;
3504 
3505    strcpy(szSpecCopy, szSpec);
3506    *szValue = '\0';
3507    if ((c_ptr=strtok(szSpec, " ,\t\n\r")) != NULL) {
3508       strcpy(szValue, c_ptr);
3509    }
3510    if (*szValue == '\0' || sscanf(szValue, "%f", &gamma) != 1) {
3511       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_A_VAL),
3512             szSpecCopy);
3513       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3514       return;
3515    } else if (gamma < (float)INT_TOL) {
3516       sprintf(gszMsgBox, TgLoadString(STID_GIVEN_NEG_VAL_NOT_ALLOWED),
3517             szSpecCopy);
3518       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3519       return;
3520    }
3521    gfOneOverGamma = (float)1.0 / gamma;
3522 
3523    DoImageProc((NLFN*)ChangeToGamma);
3524 }
3525 
3526 /* ----------------------- Convolution Required ----------------------- */
3527 
3528 /* ----------------------- EdgeDetect ----------------------- */
3529 
3530 #ifdef NOT_DEFINED
3531 static
3532 int tx=50, ty=16;
3533 #endif /* NOT_DEFINED */
3534 
3535 static
ConvolveToEdgeDetect(x,y)3536 int ConvolveToEdgeDetect(x, y)
3537    int x, y;
3538    /*
3539     *               [ -1  0 +1 ]                [ -1 -2 -1 ]
3540     * horizontally: [ -2  0 +2 ]    vertically: [  0  0  0 ]
3541     *               [ -2  0 +1 ]                [ +1 +2 +1 ]
3542     */
3543 {
3544    if (gConvExtraInfo.fp != NULL) {
3545       XColor **xcolors=gConvExtraInfo.xcolors;
3546       FILE *fp=gConvExtraInfo.fp;
3547       int w=gConvExtraInfo.image_w, h=gConvExtraInfo.image_h;
3548       unsigned char buf[3];
3549 
3550       if (x == 0 || x == w-1 || y == 0 || y == h-1) {
3551          buf[0] = (unsigned char)xcolors[y][x].red;
3552          buf[1] = (unsigned char)xcolors[y][x].green;
3553          buf[2] = (unsigned char)xcolors[y][x].blue;
3554       } else {
3555          unsigned int r=0, g=0, b=0;
3556          double rx=0, gx=0, bx=0, ry=0, gy=0, by=0;
3557          double ddr, ddg, ddb;
3558 
3559          /*
3560           * Thinning is not implemented!
3561           */
3562          rx = (double)(
3563                ((int)xcolors[y][x+1].red) +
3564                ((int)xcolors[y][x+1].red) +
3565                ((int)xcolors[y-1][x+1].red) +
3566                ((int)xcolors[y+1][x+1].red) -
3567                ((int)xcolors[y][x-1].red) -
3568                ((int)xcolors[y][x-1].red) -
3569                ((int)xcolors[y-1][x-1].red) -
3570                ((int)xcolors[y+1][x-1].red));
3571          gx = (double)(
3572                ((int)xcolors[y][x+1].green) +
3573                ((int)xcolors[y][x+1].green) +
3574                ((int)xcolors[y-1][x+1].green) +
3575                ((int)xcolors[y+1][x+1].green) -
3576                ((int)xcolors[y][x-1].green) -
3577                ((int)xcolors[y][x-1].green) -
3578                ((int)xcolors[y-1][x-1].green) -
3579                ((int)xcolors[y+1][x-1].green));
3580          bx = (double)(
3581                ((int)xcolors[y][x+1].blue) +
3582                ((int)xcolors[y][x+1].blue) +
3583                ((int)xcolors[y-1][x+1].blue) +
3584                ((int)xcolors[y+1][x+1].blue) -
3585                ((int)xcolors[y][x-1].blue) -
3586                ((int)xcolors[y][x-1].blue) -
3587                ((int)xcolors[y-1][x-1].blue) -
3588                ((int)xcolors[y+1][x-1].blue));
3589 
3590 #ifdef NOT_DEFINED
3591 if (x == tx && y == ty) {
3592    fprintf(stderr, "x = %1d, y = %1d\n", x, y);
3593    fprintf(stderr, "rx = %g, gx = %g, bx = %g\n", rx, gx, bx);
3594    fflush(stderr);
3595    fprintf(stderr, "\n");
3596 }
3597 #endif /* NOT_DEFINED */
3598 
3599          ry = (double)(
3600                ((int)xcolors[y+1][x].red) +
3601                ((int)xcolors[y+1][x].red) +
3602                ((int)xcolors[y+1][x-1].red) +
3603                ((int)xcolors[y+1][x+1].red) -
3604                ((int)xcolors[y-1][x].red) -
3605                ((int)xcolors[y-1][x].red) -
3606                ((int)xcolors[y-1][x-1].red) -
3607                ((int)xcolors[y-1][x+1].red));
3608          gy = (double)(
3609                ((int)xcolors[y+1][x].green) +
3610                ((int)xcolors[y+1][x].green) +
3611                ((int)xcolors[y+1][x-1].green) +
3612                ((int)xcolors[y+1][x+1].green) -
3613                ((int)xcolors[y-1][x].green) -
3614                ((int)xcolors[y-1][x].green) -
3615                ((int)xcolors[y-1][x-1].green) -
3616                ((int)xcolors[y-1][x+1].green));
3617          by = (double)(
3618                ((int)xcolors[y+1][x].blue) +
3619                ((int)xcolors[y+1][x].blue) +
3620                ((int)xcolors[y+1][x-1].blue) +
3621                ((int)xcolors[y+1][x+1].blue) -
3622                ((int)xcolors[y-1][x].blue) -
3623                ((int)xcolors[y-1][x].blue) -
3624                ((int)xcolors[y-1][x-1].blue) -
3625                ((int)xcolors[y-1][x+1].blue));
3626 
3627          if (rx == 0 && ry == 0 && gx == 0 && gy == 0 && bx == 0 && by == 0) {
3628             buf[0] = (unsigned char)255;
3629             buf[1] = (unsigned char)255;
3630             buf[2] = (unsigned char)255;
3631          } else {
3632             ddr = sqrt(rx*rx + ry*ry);
3633             ddg = sqrt(gx*gx + gy*gy);
3634             ddb = sqrt(bx*bx + by*by);
3635             r = (unsigned int)ddr;
3636             g = (unsigned int)ddg;
3637             b = (unsigned int)ddb;
3638             if (r > 255) r = 255;
3639             if (g > 255) g = 255;
3640             if (b > 255) b = 255;
3641             r = 255 - r;
3642             g = 255 - g;
3643             b = 255 - b;
3644 
3645             buf[0] = (unsigned char)r;
3646             buf[1] = (unsigned char)g;
3647             buf[2] = (unsigned char)b;
3648          }
3649       }
3650       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
3651 
3652       return TRUE;
3653    } else {
3654       XColor xcolor;
3655       long red, green, blue;
3656 
3657       if (x == 0 || x == gnImageW-1 || y == 0 || y == gnImageH-1) {
3658          return GetOrAllocHistogramIndex(&tgifColors[gnOrigImageIndex[y][x]]);
3659       }
3660       memset(&xcolor, 0, sizeof(XColor));
3661       red = (((((long)tgifColors[gnOrigImageIndex[y][x]].red)<<2) -
3662             ((long)tgifColors[gnOrigImageIndex[y][x-1]].red) -
3663             ((long)tgifColors[gnOrigImageIndex[y][x+1]].red) -
3664             ((long)tgifColors[gnOrigImageIndex[y-1][x]].red) -
3665             ((long)tgifColors[gnOrigImageIndex[y+1][x]].red))>>2);
3666       green = (((((long)tgifColors[gnOrigImageIndex[y][x]].green)<<2) -
3667             ((long)tgifColors[gnOrigImageIndex[y][x-1]].green) -
3668             ((long)tgifColors[gnOrigImageIndex[y][x+1]].green) -
3669             ((long)tgifColors[gnOrigImageIndex[y-1][x]].green) -
3670             ((long)tgifColors[gnOrigImageIndex[y+1][x]].green))>>2);
3671       blue = (((((long)tgifColors[gnOrigImageIndex[y][x]].blue)<<2) -
3672             ((long)tgifColors[gnOrigImageIndex[y][x-1]].blue) -
3673             ((long)tgifColors[gnOrigImageIndex[y][x+1]].blue) -
3674             ((long)tgifColors[gnOrigImageIndex[y-1][x]].blue) -
3675             ((long)tgifColors[gnOrigImageIndex[y+1][x]].blue))>>2);
3676       xcolor.red = (red > 0L ?
3677             (red > 0x0000ffff ? 0x0ffff : (unsigned int)(red&0xffff)) : 0);
3678       xcolor.green = (green > 0L ?
3679             (green > 0x0000ffff ? 0x0ffff : (unsigned int)(green&0xffff)) : 0);
3680       xcolor.blue = (blue > 0L ?
3681             (blue > 0x0000ffff ? 0x0ffff : (unsigned int)(blue&0xffff)) : 0);
3682       return GetOrAllocHistogramIndex(&xcolor);
3683    }
3684 }
3685 
EdgeDetect()3686 void EdgeDetect()
3687 {
3688    if (!CheckSelectionForImageProc(CMDID_EDGEDETECT)) {
3689       return;
3690    }
3691    if (topSel->obj->detail.xpm->image_w < 2 ||
3692          topSel->obj->detail.xpm->image_h < 2) {
3693       MsgBox(TgLoadString(STID_SEL_TOO_THIN_FLAT_FOR_EDGE), TOOL_NAME, INFO_MB);
3694       return;
3695    }
3696    gpConvolveFunc = (NLFN*)ConvolveToEdgeDetect;
3697    gpConvolveCmdID = CMDID_EDGEDETECT;
3698    gnConvolving = TRUE;
3699    DoImageProc(NULL);
3700    gnConvolving = FALSE;
3701    gpConvolveFunc = NULL;
3702    gpConvolveCmdID = (-1);
3703 }
3704 
3705 /* ----------------------- Emboss ----------------------- */
3706 
3707 static
ConvolveToEmboss(x,y)3708 int ConvolveToEmboss(x, y)
3709    int x, y;
3710 {
3711    if (gConvExtraInfo.fp != NULL) {
3712       XColor **xcolors=gConvExtraInfo.xcolors;
3713       FILE *fp=gConvExtraInfo.fp;
3714       int w=gConvExtraInfo.image_w, h=gConvExtraInfo.image_h;
3715       unsigned char buf[3];
3716 
3717       if (x == 0 || x == w-1 || y == 0 || y == h-1) {
3718          double dgray=(double)0;
3719          int gray=0;
3720 
3721          dgray = 0.299*((double)xcolors[y][x].red) +
3722                0.587*((double)xcolors[y][x].green) +
3723                0.144*((double)xcolors[y][x].blue);
3724          gray = (int)round(dgray);
3725          if (gray < 0) gray = 0;
3726          if (gray > 255) gray = 255;
3727          buf[0] = buf[1] = buf[2] = (unsigned char)(unsigned int)gray;
3728       } else {
3729          double d_lt_gray=(double)0, d_rb_gray=(double)0, dgray=(double)0;
3730          int gray=0;
3731 
3732          d_lt_gray = 0.299*((double)xcolors[y-1][x-1].red) +
3733                0.587*((double)xcolors[y-1][x-1].green) +
3734                0.144*((double)xcolors[y-1][x-1].blue);
3735          d_rb_gray = 0.299*((double)xcolors[y+1][x+1].red) +
3736                0.587*((double)xcolors[y+1][x+1].green) +
3737                0.144*((double)xcolors[y+1][x+1].blue);
3738          dgray = 128.0 + ((d_rb_gray - d_lt_gray) / 2.0);
3739          gray = (int)round(dgray);
3740          if (gray < 0) gray = 0;
3741          if (gray > 255) gray = 255;
3742          buf[0] = buf[1] = buf[2] = (unsigned char)(unsigned int)gray;
3743       }
3744       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
3745 
3746       return TRUE;
3747    } else {
3748       XColor xcolor;
3749 
3750       memset(&xcolor, 0, sizeof(XColor));
3751       if (x == 0 || x == gnImageW-1 || y == 0 || y == gnImageH-1) {
3752          float gray=0.299*((float)tgifColors[gnOrigImageIndex[y][x]].red) +
3753                0.587*((float)tgifColors[gnOrigImageIndex[y][x]].green) +
3754                0.144*((float)tgifColors[gnOrigImageIndex[y][x]].blue);
3755 
3756          xcolor.red = xcolor.green = xcolor.blue = (unsigned short)gray;
3757          return GetOrAllocHistogramIndex(&xcolor);
3758       } else {
3759          float lt_gray, rb_gray;
3760          int val;
3761 
3762          lt_gray=0.299*((float)tgifColors[gnOrigImageIndex[y-1][x-1]].red) +
3763                0.587*((float)tgifColors[gnOrigImageIndex[y-1][x-1]].green) +
3764                0.144*((float)tgifColors[gnOrigImageIndex[y-1][x-1]].blue);
3765          rb_gray=0.299*((float)tgifColors[gnOrigImageIndex[y+1][x+1]].red) +
3766                0.587*((float)tgifColors[gnOrigImageIndex[y+1][x+1]].green) +
3767                0.144*((float)tgifColors[gnOrigImageIndex[y+1][x+1]].blue);
3768          val = 0x7fff +
3769                (int)((rb_gray - lt_gray) / 2.0);
3770          val = ((val>0x0ffff) ? 0x0ffff : ((val<0) ? 0 : val));
3771          xcolor.red = xcolor.green = xcolor.blue = (unsigned short)val;
3772          return GetOrAllocHistogramIndex(&xcolor);
3773       }
3774    }
3775 }
3776 
Emboss()3777 void Emboss()
3778 {
3779    if (!CheckSelectionForImageProc(CMDID_EMBOSS)) return;
3780 
3781    if (topSel->obj->detail.xpm->image_w < 2 ||
3782          topSel->obj->detail.xpm->image_h < 2) {
3783       MsgBox(TgLoadString(STID_SEL_TOO_THIN_FLAT_FOR_EMBOSS), TOOL_NAME,
3784             INFO_MB);
3785       return;
3786    }
3787    gpConvolveFunc = (NLFN*)ConvolveToEmboss;
3788    gpConvolveCmdID = CMDID_EMBOSS;
3789    gnConvolving = TRUE;
3790    DoImageProc(NULL);
3791    gnConvolving = FALSE;
3792    gpConvolveFunc = NULL;
3793    gpConvolveCmdID = (-1);
3794 }
3795 
3796 /* ----------------------- ReduceColors ----------------------- */
3797 
3798 static
ConvolveToReduceColors(x,y)3799 int ConvolveToReduceColors(x, y)
3800    int x, y;
3801 {
3802    return GetOrAllocHistogramIndex(&tgifColors[gnOrigImageIndex[y][x]]);
3803 }
3804 
ReduceColors()3805 void ReduceColors()
3806 {
3807    char *c_ptr, szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1];
3808    int colors_to_reduce_to=0;
3809    struct XPmRec *xpm_ptr=NULL;
3810 
3811    if (!CheckSelectionForImageProc(CMDID_REDUCECOLORS)) {
3812       return;
3813    }
3814    sprintf(gszMsgBox, TgLoadString(STID_ENTER_NUM_COLORS_TO_REDUCE_TO),
3815          topSel->obj->detail.xpm->ncolors);
3816    *szSpec = '\0';
3817    Dialog(gszMsgBox, TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
3818    UtilTrimBlanks(szSpec);
3819    if (*szSpec == '\0') return;
3820 
3821    xpm_ptr = topSel->obj->detail.xpm;
3822    strcpy(szSpecCopy, szSpec);
3823    if ((c_ptr=strtok(szSpec, " ,\t\n\r")) == NULL) return;
3824    colors_to_reduce_to = atoi(c_ptr);
3825    if (DoPpm6(xpm_ptr)) {
3826       if (colors_to_reduce_to < 2 ||
3827             colors_to_reduce_to > 256) {
3828          sprintf(gszMsgBox, TgLoadString(STID_NUM_COLORS_BETWEEN_2_N_GIVEN),
3829                szSpecCopy, 256);
3830          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3831          return;
3832       }
3833    } else {
3834       if (colors_to_reduce_to < 2 ||
3835             colors_to_reduce_to > topSel->obj->detail.xpm->ncolors) {
3836          sprintf(gszMsgBox, TgLoadString(STID_NUM_COLORS_BETWEEN_2_N_GIVEN),
3837                szSpecCopy, topSel->obj->detail.xpm->ncolors);
3838          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3839          return;
3840       }
3841    }
3842    gnUserSpecifiedLevels = colors_to_reduce_to;
3843    gpConvolveFunc = (NLFN*)ConvolveToReduceColors;
3844    gpConvolveCmdID = CMDID_REDUCECOLORS;
3845    gnConvolving = TRUE;
3846    DoImageProc(NULL);
3847    gnConvolving = FALSE;
3848    gpConvolveFunc = NULL;
3849    gpConvolveCmdID = (-1);
3850    gnUserSpecifiedLevels = (-1);
3851 }
3852 
3853 /* ----------------------- ReduceToPixmapColors ----------------------- */
3854 
3855 static struct XPmRec gXPmTarget;
3856 static XColor *gTargetColors=NULL;
3857 static int *gTargetColorValid=NULL;
3858 static int *gnObjectColorsToTargetColorMapping=NULL;
3859 static int *gnTgifColorsToObjectColorMapping=NULL;
3860 
3861 static int gnFloyd=FALSE;
3862 static int **gnImageTargetColor=NULL;
3863 static XImage *gpTargetImage=NULL, *gpTargetBitmapImage=NULL;
3864 
3865 static
FreeTargetColors(ncolors)3866 void FreeTargetColors(ncolors)
3867    int ncolors;
3868 {
3869    if (gTargetColors != NULL) {
3870       free(gTargetColors);
3871       gTargetColors = NULL;
3872    }
3873    if (gTargetColorValid != NULL) {
3874       free(gTargetColorValid);
3875       gTargetColorValid = NULL;
3876    }
3877 }
3878 
3879 static
AllocTargetColors(ncolors)3880 int AllocTargetColors(ncolors)
3881    int ncolors;
3882 {
3883    int i=0;
3884 
3885    gTargetColors = (XColor*)malloc(ncolors*sizeof(XColor));
3886    if (gTargetColors == NULL) FailAllocMessage();
3887    memset(gTargetColors, 0, ncolors*sizeof(XColor));
3888 
3889    gTargetColorValid = (int*)malloc(ncolors*sizeof(int));
3890    if (gTargetColorValid == NULL) FailAllocMessage();
3891    for (i=0; i < ncolors; i++) {
3892       gTargetColorValid[i] = TRUE;
3893    }
3894    return TRUE;
3895 }
3896 
3897 static
GetClosestColorIndex(red_bits,green_bits,blue_bits,red,green,blue,target_ncolors)3898 int GetClosestColorIndex(red_bits, green_bits, blue_bits, red, green, blue,
3899       target_ncolors)
3900    int red_bits, green_bits, blue_bits, red, green, blue, target_ncolors;
3901 {
3902    int min_index=0;
3903    unsigned long dr=(double)0, dg=(double)0, db=(double)0, min_dist=(double)0;
3904 
3905    if (gXPmTarget.color_str != NULL ||
3906          gXPmTarget.userdata == (void*)CMDID_REDUCETOMOBILEWEBCOLORS) {
3907       /* ReduceToPixmapColors() or ReduceToMobileWebSafeColors() */
3908       int j=0;
3909 
3910       for (j=0; j < target_ncolors; j++) {
3911          if (gTargetColorValid[j]) {
3912             dr = (unsigned long)abs(red-(int)(gTargetColors[j].red));
3913             dg = (unsigned long)abs(green-(int)(gTargetColors[j].green));
3914             db = (unsigned long)abs(blue-(int)(gTargetColors[j].blue));
3915             dr >>= 4; dg >>= 4; db >>= 4;
3916             break;
3917          }
3918       }
3919       min_index = j;
3920       min_dist = dr*dr + dg*dg + db*db;
3921       for (j++; j < target_ncolors; j++) {
3922          if (gTargetColorValid[j]) {
3923             unsigned long new_dist;
3924 
3925             dr = (unsigned long)abs(red-(int)(gTargetColors[j].red));
3926             dg = (unsigned long)abs(green-(int)(gTargetColors[j].green));
3927             db = (unsigned long)abs(blue-(int)(gTargetColors[j].blue));
3928             dr >>= 4; dg >>= 4; db >>= 4;
3929             new_dist = dr*dr + dg*dg + db*db;
3930 
3931             if (new_dist < min_dist) {
3932                min_index = j;
3933                min_dist = new_dist;
3934             }
3935          }
3936       }
3937    } else if (gXPmTarget.userdata == (void*)CMDID_REDUCETOMOBILEWEBCOLORS) {
3938       /* ReduceToDefaultColors() or DefaultErrorDiffuse() */
3939       int k, j, i;
3940       int red_max=(1<<red_bits);
3941       int green_max=(1<<green_bits);
3942       int blue_max=(1<<blue_bits);
3943       int red_index=(red >> (16-red_bits));
3944       int green_index=(green >> (16-green_bits));
3945       int blue_index=(blue >> (16-blue_bits));
3946 
3947       min_index = 0;
3948       dr = (unsigned long)abs(red-(int)(gTargetColors[min_index].red));
3949       dg = (unsigned long)abs(green-(int)(gTargetColors[min_index].green));
3950       db = (unsigned long)abs(blue-(int)(gTargetColors[min_index].blue));
3951       dr >>= 4; dg >>= 4; db >>= 4;
3952 
3953       min_dist = dr*dr + dg*dg + db*db;
3954       for (i=red_index-1; i <= red_index+1; i++) {
3955          if (i >= 0 && i < red_max) {
3956             for (j=green_index-1; j <= green_index+1; j++) {
3957                if (j >= 0 && j < green_max) {
3958                   for (k=blue_index-1; k <= blue_index+1; k++) {
3959                      if (k >= 0 && k < blue_max) {
3960                         if (i!=red_index || j!=green_index || k!=blue_index) {
3961                            int index=(((i << green_bits) + j) << blue_bits) + k;
3962                            unsigned long new_dist;
3963 
3964                            dr = (unsigned long)abs(red -
3965                                  (int)(gTargetColors[index].red));
3966                            dg = (unsigned long)abs(green -
3967                                  (int)(gTargetColors[index].green));
3968                            db = (unsigned long)abs(blue -
3969                                  (int)(gTargetColors[index].blue));
3970                            dr >>= 4; dg >>= 4; db >>= 4;
3971                            new_dist = dr*dr + dg*dg + db*db;
3972                            if (new_dist < min_dist) {
3973                               min_index = index;
3974                               min_dist = new_dist;
3975                            }
3976                         }
3977                      }
3978                   }
3979                }
3980             }
3981          }
3982       }
3983    } else {
3984       /* ReduceToDefaultColors() or DefaultErrorDiffuse() */
3985       int k, j, i;
3986       int red_max=(1<<red_bits);
3987       int green_max=(1<<green_bits);
3988       int blue_max=(1<<blue_bits);
3989       int red_index=(red >> (16-red_bits));
3990       int green_index=(green >> (16-green_bits));
3991       int blue_index=(blue >> (16-blue_bits));
3992 
3993       min_index = (((red_index << green_bits) + green_index) << blue_bits) +
3994             blue_index;
3995       dr = (unsigned long)abs(red-(int)(gTargetColors[min_index].red));
3996       dg = (unsigned long)abs(green-(int)(gTargetColors[min_index].green));
3997       db = (unsigned long)abs(blue-(int)(gTargetColors[min_index].blue));
3998       dr >>= 4; dg >>= 4; db >>= 4;
3999 
4000       min_dist = dr*dr + dg*dg + db*db;
4001       for (i=red_index-1; i <= red_index+1; i++) {
4002          if (i >= 0 && i < red_max) {
4003             for (j=green_index-1; j <= green_index+1; j++) {
4004                if (j >= 0 && j < green_max) {
4005                   for (k=blue_index-1; k <= blue_index+1; k++) {
4006                      if (k >= 0 && k < blue_max) {
4007                         if (i!=red_index || j!=green_index || k!=blue_index) {
4008                            int index=(((i << green_bits) + j) << blue_bits) + k;
4009                            unsigned long new_dist;
4010 
4011                            dr = (unsigned long)abs(red -
4012                                  (int)(gTargetColors[index].red));
4013                            dg = (unsigned long)abs(green -
4014                                  (int)(gTargetColors[index].green));
4015                            db = (unsigned long)abs(blue -
4016                                  (int)(gTargetColors[index].blue));
4017                            dr >>= 4; dg >>= 4; db >>= 4;
4018                            new_dist = dr*dr + dg*dg + db*db;
4019                            if (new_dist < min_dist) {
4020                               min_index = index;
4021                               min_dist = new_dist;
4022                            }
4023                         }
4024                      }
4025                   }
4026                }
4027             }
4028          }
4029       }
4030    }
4031    return min_index;
4032 }
4033 static
ConvolveToErrorDiffuse(x,y)4034 int ConvolveToErrorDiffuse(x, y)
4035    int x, y;
4036 {
4037    int targetcolor_index=gnImageTargetColor[y][x];
4038 
4039    if (gConvExtraInfo.fp != NULL) {
4040       FILE *fp=gConvExtraInfo.fp;
4041       double dr=(double)0, dg=(double)0, db=(double)0;
4042       unsigned char buf[3];
4043       unsigned int r=(unsigned int)(gTargetColors[targetcolor_index].red);
4044       unsigned int g=(unsigned int)(gTargetColors[targetcolor_index].green);
4045       unsigned int b=(unsigned int)(gTargetColors[targetcolor_index].blue);
4046 
4047       dr = ((double)r) / maxRGB * ((double)255);
4048       dg = ((double)g) / maxRGB * ((double)255);
4049       db = ((double)b) / maxRGB * ((double)255);
4050       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
4051       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
4052       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
4053       if (r > 255) r = 255;
4054       if (g > 255) g = 255;
4055       if (b > 255) b = 255;
4056       buf[0] = (unsigned char)r;
4057       buf[1] = (unsigned char)g;
4058       buf[2] = (unsigned char)b;
4059       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
4060 
4061       return TRUE;
4062    } else {
4063       return GetOrAllocHistogramIndex(&gTargetColors[targetcolor_index]);
4064    }
4065 }
4066 
4067 static
ConvolveToReduceToPixmapColors(x,y)4068 int ConvolveToReduceToPixmapColors(x, y)
4069    int x, y;
4070 {
4071    if (gConvExtraInfo.fp != NULL) {
4072       int index=0, pixel=XGetPixel(gConvExtraInfo.image,x,y);
4073       FILE *fp=gConvExtraInfo.fp;
4074       uint32_t pix=(uint32_t)(unsigned int)pixel;
4075       unsigned int r=0, g=0, b=0;
4076       double dr=(double)0, dg=(double)0, db=(double)0;
4077       unsigned char buf[3];
4078 
4079       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
4080       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
4081       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
4082       dr = ((double)r) / gTrueColorInfo.dr_maxval * ((double)maxRGB);
4083       dg = ((double)g) / gTrueColorInfo.dg_maxval * ((double)maxRGB);
4084       db = ((double)b) / gTrueColorInfo.db_maxval * ((double)maxRGB);
4085 
4086       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
4087       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
4088       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
4089       if (r > maxRGB) r = maxRGB;
4090       if (g > maxRGB) g = maxRGB;
4091       if (b > maxRGB) b = maxRGB;
4092 
4093       index = GetClosestColorIndex(gDefErrorDiffuseLevel.red,
4094             gDefErrorDiffuseLevel.green, gDefErrorDiffuseLevel.blue,
4095             (int)r, (int)g, (int)b, gnUserSpecifiedLevels);
4096 
4097       r = (unsigned int)(gTargetColors[index].red);
4098       g = (unsigned int)(gTargetColors[index].green);
4099       b = (unsigned int)(gTargetColors[index].blue);
4100 
4101       dr = ((double)r) / maxRGB * ((double)255);
4102       dg = ((double)g) / maxRGB * ((double)255);
4103       db = ((double)b) / maxRGB * ((double)255);
4104       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
4105       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
4106       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
4107       if (r > 255) r = 255;
4108       if (g > 255) g = 255;
4109       if (b > 255) b = 255;
4110       buf[0] = (unsigned char)r;
4111       buf[1] = (unsigned char)g;
4112       buf[2] = (unsigned char)b;
4113       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
4114 
4115       return TRUE;
4116    } else {
4117       int tgifcolor_index=gnOrigImageIndex[y][x]; /* index into tgifColors */
4118       int objcolor_index=gnTgifColorsToObjectColorMapping[tgifcolor_index];
4119       int targetcolor_index=gnObjectColorsToTargetColorMapping[objcolor_index];
4120 
4121       return GetOrAllocHistogramIndex(&gTargetColors[targetcolor_index]);
4122    }
4123 }
4124 
4125 static
GetIntensity(index,max_levels)4126 int GetIntensity(index, max_levels)
4127    int index, max_levels; /* 0 < max_levels <= 255 */
4128    /*
4129     * May be it should be like the following:
4130     *
4131     * int ival=(int)(((double)index)/((double)max_levels)*((double)maxRGB));
4132     *
4133     * return (ival < 0 ? 0 : (ival > maxRGB ? maxRGB : ival));
4134     */
4135 {
4136    int ival=(int)(((double)index)/((double)max_levels)*((double)255));
4137 
4138    return ((ival < 0 ? 0 : (ival > 0xff ? 0xff : ival))<<8);
4139 }
4140 
4141 #define FS_SCALE 0x400
4142 #define HALF_FS_SCALE 0x200
4143 
4144 static
DoPrepareForErrorDiffuse(xpm_ptr)4145 int DoPrepareForErrorDiffuse(xpm_ptr)
4146    struct XPmRec *xpm_ptr;
4147 {
4148    int j, i, target_ncolors=gXPmTarget.ncolors, fs_forward=TRUE;
4149    int image_w=xpm_ptr->image_w, image_h=xpm_ptr->image_h;
4150    int red_bits=0, green_bits=0, blue_bits=0;
4151    long *this_r_err, *this_g_err, *this_b_err, *tmp_err;
4152    long *next_r_err, *next_g_err, *next_b_err;
4153    ProgressInfo pi;
4154 
4155    if (!CreatePixelToIndexMapping()) return FALSE;
4156    if (gXPmTarget.color_str != NULL) {
4157       for (i=0; i < target_ncolors; i++) {
4158          if (!TgifParseColor(gXPmTarget.color_str[i], &gTargetColors[i])) {
4159             gTargetColorValid[i] = FALSE;
4160          }
4161       }
4162    } else {
4163       red_bits = gDefErrorDiffuseLevel.red;
4164       green_bits = gDefErrorDiffuseLevel.green;
4165       blue_bits = gDefErrorDiffuseLevel.blue;
4166    }
4167    if (!GetXPmImages(xpm_ptr, &gpTargetImage, &gpTargetBitmapImage)) {
4168       return FALSE;
4169    }
4170    gnImageTargetColor = (int**)malloc(image_h*sizeof(int*));
4171    if (gnImageTargetColor == NULL) return FailAllocMessage();
4172    for (i=0; i < image_h; i++) {
4173       if ((gnImageTargetColor[i]=(int*)malloc(image_w*sizeof(int))) == NULL) {
4174          for (j=0; j < i; j++) free(gnImageTargetColor[j]);
4175          free(gnImageTargetColor);
4176          gnImageTargetColor = NULL;
4177          return FailAllocMessage();
4178       }
4179    }
4180    this_r_err = (long*)malloc((image_w+2)*sizeof(long));
4181    this_g_err = (long*)malloc((image_w+2)*sizeof(long));
4182    this_b_err = (long*)malloc((image_w+2)*sizeof(long));
4183    next_r_err = (long*)malloc((image_w+2)*sizeof(long));
4184    next_g_err = (long*)malloc((image_w+2)*sizeof(long));
4185    next_b_err = (long*)malloc((image_w+2)*sizeof(long));
4186    if (this_r_err==NULL || this_g_err==NULL || this_b_err==NULL ||
4187          next_r_err==NULL || next_g_err==NULL || next_b_err==NULL) {
4188       if (this_r_err != NULL) free(this_r_err);
4189       if (this_g_err != NULL) free(this_g_err);
4190       if (this_b_err != NULL) free(this_b_err);
4191       if (next_r_err != NULL) free(next_r_err);
4192       if (next_g_err != NULL) free(next_g_err);
4193       if (next_b_err != NULL) free(next_b_err);
4194       return FALSE;
4195    }
4196    srand(0);
4197    for (j=0; j < image_w+2; j++) {
4198       this_r_err[j] = (rand() % FS_SCALE - HALF_FS_SCALE) << 4;
4199       this_g_err[j] = (rand() % FS_SCALE - HALF_FS_SCALE) << 4;
4200       this_b_err[j] = (rand() % FS_SCALE - HALF_FS_SCALE) << 4;
4201    }
4202 
4203    BeginProgress(&pi, image_h);
4204    for (i=0; i < image_h; i++) {
4205       int col, limitcol;
4206 
4207       UpdateProgress(&pi, i);
4208       /*--------------------------------------------------------*/
4209       /* the error diffusion code is adapted from 'ppmquant.c', */
4210       /*       which is part of the netpbm package.             */
4211       /*                                                        */
4212       /*       Copyright (C) 1989, 1991 by Jef Poskanzer        */
4213       /*--------------------------------------------------------*/
4214       col = (fs_forward ? 0 : image_w-1);
4215       limitcol = (fs_forward ? image_w : (-1));
4216       for (j=0; j < image_w+2; j++) {
4217          next_r_err[j] = next_g_err[j] = next_b_err[j] = 0L;
4218       }
4219       do {
4220          int red, green, blue, err, min_index=0;
4221          int pixel=XGetPixel(gpTargetImage, col, i);
4222 
4223          if (DoPpm6(xpm_ptr)) {
4224             uint32_t pix=(uint32_t)(unsigned int)pixel;
4225             unsigned int r=0, g=0, b=0;
4226             double dr=(double)0, dg=(double)0, db=(double)0;
4227 
4228             r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
4229             g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
4230             b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
4231             dr = ((double)r) / gTrueColorInfo.dr_maxval * ((double)65535);
4232             dg = ((double)g) / gTrueColorInfo.dg_maxval * ((double)65535);
4233             db = ((double)b) / gTrueColorInfo.db_maxval * ((double)65535);
4234 
4235             r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
4236             g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
4237             b = (db < ((double)0)) ? 0 : ((unsigned int)db);
4238             if (r > 65535) r = 65535;
4239             if (g > 65535) g = 65535;
4240             if (b > 65535) b = 65535;
4241 
4242             red = (int)(r) + this_r_err[col+1];
4243             green = (int)(g) + this_g_err[col+1];
4244             blue = (int)(b) + this_b_err[col+1];
4245             if (red < 0) red = 0; if (red > 65535) red = 65535;
4246             if (green < 0) green = 0; if (green > 65535) green = 65535;
4247             if (blue < 0) blue = 0; if (blue > 65535) blue = 65535;
4248 
4249             min_index = GetClosestColorIndex(gDefErrorDiffuseLevel.red,
4250                   gDefErrorDiffuseLevel.green, gDefErrorDiffuseLevel.blue,
4251                   (int)red, (int)green, (int)blue, target_ncolors);
4252          } else {
4253             int index=GetIndexOfPixel(pixel);
4254 
4255             red = (int)(tgifColors[index].red) + this_r_err[col+1];
4256             green = (int)(tgifColors[index].green) + this_g_err[col+1];
4257             blue = (int)(tgifColors[index].blue) + this_b_err[col+1];
4258             if (red < 0) red = 0; if (red > 65535) red = 65535;
4259             if (green < 0) green = 0; if (green > 65535) green = 65535;
4260             if (blue < 0) blue = 0; if (blue > 65535) blue = 65535;
4261 
4262             min_index = GetClosestColorIndex(red_bits, green_bits, blue_bits,
4263                   red, green, blue, target_ncolors);
4264          }
4265          gnImageTargetColor[i][col] = min_index;
4266          if (fs_forward) {
4267             err = (red - gTargetColors[min_index].red); /* * FS_SCALE; */
4268             this_r_err[col+2] += (err*7) >> 4;
4269             next_r_err[col  ] += (err*3) >> 4;
4270             next_r_err[col+1] += (err*5) >> 4;
4271             next_r_err[col+2] += (err  ) >> 4;
4272             err = (green - gTargetColors[min_index].green); /* * FS_SCALE; */
4273             this_g_err[col+2] += (err*7) >> 4;
4274             next_g_err[col  ] += (err*3) >> 4;
4275             next_g_err[col+1] += (err*5) >> 4;
4276             next_g_err[col+2] += (err  ) >> 4;
4277             err = (blue - gTargetColors[min_index].blue); /* * FS_SCALE; */
4278             this_b_err[col+2] += (err*7) >> 4;
4279             next_b_err[col  ] += (err*3) >> 4;
4280             next_b_err[col+1] += (err*5) >> 4;
4281             next_b_err[col+2] += (err  ) >> 4;
4282 
4283             col++;
4284          } else {
4285             err = (red - gTargetColors[min_index].red); /* * FS_SCALE; */
4286             this_r_err[col  ] += (err*7) >> 4;
4287             next_r_err[col+2] += (err*3) >> 4;
4288             next_r_err[col+1] += (err*5) >> 4;
4289             next_r_err[col  ] += (err  ) >> 4;
4290             err = (green - gTargetColors[min_index].green); /* * FS_SCALE; */
4291             this_g_err[col  ] += (err*7) >> 4;
4292             next_g_err[col+2] += (err*3) >> 4;
4293             next_g_err[col+1] += (err*5) >> 4;
4294             next_g_err[col  ] += (err  ) >> 4;
4295             err = (blue - gTargetColors[min_index].blue); /* * FS_SCALE; */
4296             this_b_err[col  ] += (err*7) >> 4;
4297             next_b_err[col+2] += (err*3) >> 4;
4298             next_b_err[col+1] += (err*5) >> 4;
4299             next_b_err[col  ] += (err  ) >> 4;
4300 
4301             col--;
4302          }
4303       } while (col != limitcol);
4304       tmp_err = this_r_err; this_r_err = next_r_err; next_r_err = tmp_err;
4305       tmp_err = this_g_err; this_g_err = next_g_err; next_g_err = tmp_err;
4306       tmp_err = this_b_err; this_b_err = next_b_err; next_b_err = tmp_err;
4307       fs_forward = !fs_forward;
4308    }
4309    free(this_r_err); free(this_g_err); free(this_b_err);
4310    free(next_r_err); free(next_g_err); free(next_b_err);
4311 
4312    return TRUE;
4313 }
4314 
4315 static
PrepareForErrorDiffuse(xpm_ptr)4316 int PrepareForErrorDiffuse(xpm_ptr)
4317    struct XPmRec *xpm_ptr;
4318 {
4319    int rc;
4320 
4321    SaveStatusStrings();
4322    SetWatchCursor(drawWindow);
4323    SetWatchCursor(mainWindow);
4324    rc = DoPrepareForErrorDiffuse(xpm_ptr);
4325    SetDefaultCursor(mainWindow);
4326    ShowCursor();
4327    RestoreStatusStrings();
4328    return rc;
4329 }
4330 
4331 static
MapTargetColors(xpm_ptr)4332 int MapTargetColors(xpm_ptr)
4333    struct XPmRec *xpm_ptr;
4334 {
4335    int i, target_ncolors=gXPmTarget.ncolors;
4336    int red_bits=0, green_bits=0, blue_bits=0;
4337 
4338    if (gXPmTarget.color_str != NULL) {
4339       for (i=0; i < target_ncolors; i++) {
4340          if (!TgifParseColor(gXPmTarget.color_str[i], &gTargetColors[i])) {
4341             gTargetColorValid[i] = FALSE;
4342          }
4343       }
4344    } else {
4345       red_bits = gDefErrorDiffuseLevel.red;
4346       green_bits = gDefErrorDiffuseLevel.green;
4347       blue_bits = gDefErrorDiffuseLevel.blue;
4348    }
4349    gnTgifColorsToObjectColorMapping = (int*)malloc(maxColors*sizeof(int));
4350    if (gnTgifColorsToObjectColorMapping == NULL) {
4351       FailAllocMessage();
4352       return FALSE;
4353    }
4354    memset(gnTgifColorsToObjectColorMapping, (-1), maxColors*sizeof(int));
4355 
4356    for (i=0; i < maxColors; i++) {
4357       int cur_pixel=colorPixels[i], ncolors=xpm_ptr->ncolors;
4358       int found_index, *pixels=xpm_ptr->pixels;
4359       int red, green, blue, min_index;
4360 
4361       for (found_index=0; found_index < ncolors; found_index++) {
4362          if (pixels[found_index] == cur_pixel) {
4363             break;
4364          }
4365       }
4366       if (found_index >= ncolors) {
4367          continue;
4368       }
4369       gnTgifColorsToObjectColorMapping[i] = found_index;
4370 
4371       red = (int)(tgifColors[i].red);
4372       green = (int)(tgifColors[i].green);
4373       blue = (int)(tgifColors[i].blue);
4374 
4375       min_index = GetClosestColorIndex(red_bits, green_bits, blue_bits,
4376             red, green, blue, target_ncolors);
4377 
4378       gnObjectColorsToTargetColorMapping[found_index] = min_index;
4379    }
4380    return TRUE;
4381 }
4382 
ReduceToPixmapColors()4383 void ReduceToPixmapColors()
4384 {
4385    int i, ok=TRUE, rc, short_name=FALSE;
4386    char xpm_fname[MAXPATHLENGTH+1], *rest=NULL, tmp_fname[MAXPATHLENGTH+1];
4387    XEvent ev;
4388    int ncolors=0, chars_per_pixel=0, first_pixel_is_bg=0, remote_file=FALSE;
4389    char *color_char=NULL, **color_str=NULL, *xpm_data=NULL;
4390    struct XPmRec *xpm_ptr=NULL;
4391 
4392    if (!CheckSelectionForImageProc(CMDID_REDUCETOPIXMAPCOLORS)) {
4393       return;
4394    }
4395    importingFile = TRUE;
4396    *xpm_fname = *tmp_fname = '\0';
4397    gnObjectColorsToTargetColorMapping = NULL;
4398    if (importFromLibrary) {
4399       char name[MAXSTRING+1], path[MAXSTRING+1];
4400 
4401       if (SelectFromLibrary(TgLoadString(STID_SEL_XPM_FILE_FOR_RED_COLORS),
4402             XPM_FILE_EXT, name, path) == INVALID) {
4403          ok = FALSE;
4404       } else {
4405          sprintf(xpm_fname, "%s%c%s", path, DIR_SEP, name);
4406       }
4407    } else if (SelectFileNameToImport(
4408          TgLoadString(STID_SEL_XPM_FILE_FOR_RED_COLORS), XPM_FILE_EXT,
4409          xpm_fname) == INVALID) {
4410       ok = FALSE;
4411    } else if (FileIsRemote(xpm_fname)) {
4412       int is_html=FALSE;
4413 
4414       if (!DownloadRemoteFile(xpm_fname, NULL, NULL, &is_html, tmp_fname, NULL,
4415            0) || *tmp_fname == '\0') {
4416          ok = FALSE;
4417       } else {
4418          remote_file = TRUE;
4419       }
4420    }
4421    importingFile = FALSE;
4422    if (!ok) {
4423       if (remote_file) unlink(tmp_fname);
4424       return;
4425    }
4426    XSync(mainDisplay, False);
4427    if (XCheckMaskEvent(mainDisplay, ExposureMask, &ev)) {
4428       ExposeEventHandler(&ev, TRUE);
4429    }
4430    xpm_ptr = topSel->obj->detail.xpm;
4431    if (DoPpm6(xpm_ptr)) {
4432       gnObjectColorsToTargetColorMapping = NULL;
4433    } else {
4434       gnObjectColorsToTargetColorMapping =
4435             (int*)malloc(xpm_ptr->ncolors*sizeof(int));
4436       if (gnObjectColorsToTargetColorMapping == NULL) {
4437          FailAllocMessage();
4438          if (remote_file) unlink(tmp_fname);
4439          return;
4440       }
4441       memset(gnObjectColorsToTargetColorMapping, 0,
4442             xpm_ptr->ncolors*sizeof(int));
4443    }
4444    SetWatchCursor(drawWindow);
4445    SetWatchCursor(mainWindow);
4446    rc = MyReadPixmapFile(remote_file ? tmp_fname : xpm_fname, NULL, NULL, NULL,
4447          NULL, NULL, NULL, NULL, NULL, &ncolors, &chars_per_pixel,
4448          &first_pixel_is_bg, &color_char, &color_str, NULL, &xpm_data);
4449    SetDefaultCursor(mainWindow);
4450    ShowCursor();
4451 
4452    if (remote_file) {
4453       short_name = FALSE;
4454    } else {
4455       if ((short_name=IsPrefix(bootDir, xpm_fname, &rest))) ++rest;
4456    }
4457    if (rc != BitmapSuccess) {
4458       sprintf(gszMsgBox, TgLoadString(STID_INVALID_GIVEN_XPM_FILE),
4459             (short_name ? rest : xpm_fname));
4460       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4461       if (gnObjectColorsToTargetColorMapping != NULL) {
4462          free(gnObjectColorsToTargetColorMapping);
4463          gnObjectColorsToTargetColorMapping = NULL;
4464       }
4465       if (remote_file) unlink(tmp_fname);
4466       return;
4467    }
4468    gXPmTarget.ncolors = ncolors;
4469    gXPmTarget.chars_per_pixel = chars_per_pixel;
4470    gXPmTarget.first_pixel_is_bg = first_pixel_is_bg;
4471    gXPmTarget.color_char = color_char;
4472    gXPmTarget.color_str = color_str;
4473    gXPmTarget.data = xpm_data;
4474    gXPmTarget.userdata = (void*)CMDID_REDUCETOPIXMAPCOLORS;
4475 
4476    if (ncolors <= 0) {
4477       sprintf(gszMsgBox, TgLoadString(STID_INVALID_GIVEN_XPM_FILE),
4478             (short_name ? rest : xpm_fname));
4479       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4480    } else {
4481       int image_h=xpm_ptr->image_h;
4482 
4483       rc = MsgBox(TgLoadString(STID_Q_FS_ERROR_DIFFUSE), TOOL_NAME, YNC_MB);
4484       switch (rc) {
4485       case MB_ID_YES: gnFloyd = TRUE; break;
4486       case MB_ID_NO: gnFloyd = FALSE; break;
4487       case MB_ID_CANCEL:
4488          if (gnObjectColorsToTargetColorMapping != NULL) {
4489             free(gnObjectColorsToTargetColorMapping);
4490             gnObjectColorsToTargetColorMapping = NULL;
4491          }
4492          if (remote_file) unlink(tmp_fname);
4493          return;
4494       }
4495       AllocTargetColors(ncolors);
4496       if (gnFloyd) {
4497          if (DoPpm6(xpm_ptr)) {
4498             if (!InitTrueColorInfo(xpm_ptr->image, &gTrueColorInfo,
4499                   xpm_ptr->image_w)) {
4500                sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
4501                      xpm_ptr->image_w, xpm_ptr->image_h);
4502                MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4503                if (gnObjectColorsToTargetColorMapping != NULL) {
4504                   free(gnObjectColorsToTargetColorMapping);
4505                   gnObjectColorsToTargetColorMapping = NULL;
4506                }
4507                if (remote_file) unlink(tmp_fname);
4508                return;
4509             }
4510          }
4511          rc = PrepareForErrorDiffuse(xpm_ptr);
4512          CleanUpIndexOfPixel();
4513          if (gpTargetImage != NULL) XDestroyImage(gpTargetImage);
4514          if (gpTargetBitmapImage != NULL) XDestroyImage(gpTargetBitmapImage);
4515          gpTargetImage = gpTargetBitmapImage = NULL;
4516       } else {
4517          if (DoPpm6(xpm_ptr)) {
4518             if (gXPmTarget.color_str != NULL) {
4519                for (i=0; i < ncolors; i++) {
4520                   if (!TgifParseColor(gXPmTarget.color_str[i],
4521                         &gTargetColors[i])) {
4522                      gTargetColorValid[i] = FALSE;
4523                   }
4524                }
4525             }
4526             rc = TRUE;
4527          } else {
4528             rc = MapTargetColors(xpm_ptr);
4529          }
4530       }
4531       if (rc) {
4532          gnUserSpecifiedLevels = ncolors;
4533          if (gnFloyd) {
4534             gpConvolveFunc = (NLFN*)ConvolveToErrorDiffuse;
4535             gpConvolveCmdID = CMDID_REDUCETOPIXMAPCOLORS;
4536          } else {
4537             gpConvolveFunc = (NLFN*)ConvolveToReduceToPixmapColors;
4538             gpConvolveCmdID = CMDID_REDUCETOPIXMAPCOLORS;
4539          }
4540          gnConvolving = TRUE;
4541          DoImageProc(NULL);
4542          gnConvolving = FALSE;
4543          gpConvolveFunc = NULL;
4544          gpConvolveCmdID = (-1);
4545          gnUserSpecifiedLevels = (-1);
4546       }
4547       FreeTargetColors();
4548       if (gnFloyd) {
4549          if (gnImageTargetColor != NULL) {
4550             for (i=0; i < image_h; i++) free(gnImageTargetColor[i]);
4551             free(gnImageTargetColor);
4552             gnImageTargetColor = NULL;
4553          }
4554       }
4555       gnFloyd = FALSE;
4556    }
4557    if (gXPmTarget.color_char != NULL) free(gXPmTarget.color_char);
4558    if (gXPmTarget.color_str != NULL) {
4559       for (i=0; i < gXPmTarget.ncolors; i++) free(gXPmTarget.color_str[i]);
4560       free(gXPmTarget.color_str);
4561    }
4562    if (gXPmTarget.data != NULL) free(gXPmTarget.data);
4563    memset(&gXPmTarget, 0, sizeof(struct XPmRec));
4564 
4565    if (gnObjectColorsToTargetColorMapping != NULL) {
4566       free(gnObjectColorsToTargetColorMapping);
4567       gnObjectColorsToTargetColorMapping = NULL;
4568    }
4569    if (gnTgifColorsToObjectColorMapping != NULL) {
4570       free(gnTgifColorsToObjectColorMapping);
4571       gnTgifColorsToObjectColorMapping = NULL;
4572    }
4573    if (remote_file) unlink(tmp_fname);
4574 }
4575 
4576 /* ---------------------- ReduceToMobileWebSafeColors ---------------------- */
4577 
4578 static
GetMobileWebSafeIntensity(index)4579 int GetMobileWebSafeIntensity(index)
4580    int index;
4581 {
4582    switch (index) {
4583    case 0: return 0x00000;
4584    case 1: return 0x03300;
4585    case 2: return 0x06600;
4586    case 3: return 0x09900;
4587    case 4: return 0x0cc00;
4588    case 5: return 0x0ff00;
4589    }
4590    TgAssert(FALSE, "invalid parameter passed to GetMobileWebSafeIntensity()",
4591          NULL);
4592    return (-1);
4593 }
4594 
ReduceToMobileWebSafeColors()4595 void ReduceToMobileWebSafeColors()
4596 {
4597    int i, red=0, rc=FALSE, ncolors=0, image_h=0;
4598    struct XPmRec *xpm_ptr=NULL;
4599 
4600    if (!CheckSelectionForImageProc(CMDID_REDUCETOMOBILEWEBCOLORS)) {
4601       return;
4602    }
4603    xpm_ptr = topSel->obj->detail.xpm;
4604    image_h = xpm_ptr->image_h;
4605 
4606    if (DoPpm6(xpm_ptr)) {
4607       gnObjectColorsToTargetColorMapping = NULL;
4608    } else {
4609       gnObjectColorsToTargetColorMapping =
4610             (int*)malloc(xpm_ptr->ncolors*sizeof(int));
4611       if (gnObjectColorsToTargetColorMapping == NULL) {
4612          FailAllocMessage();
4613          return;
4614       }
4615       memset(gnObjectColorsToTargetColorMapping, 0,
4616             xpm_ptr->ncolors*sizeof(int));
4617    }
4618    ncolors = 6*6*6;
4619 
4620    memset(&gXPmTarget, 0, sizeof(struct XPmRec));
4621    gXPmTarget.ncolors = ncolors;
4622    gXPmTarget.userdata = (void*)CMDID_REDUCETOMOBILEWEBCOLORS;
4623 
4624    rc = MsgBox(TgLoadString(STID_Q_FS_ERROR_DIFFUSE), TOOL_NAME, YNC_MB);
4625    switch (rc) {
4626    case MB_ID_YES: gnFloyd = TRUE; break;
4627    case MB_ID_NO: gnFloyd = FALSE; break;
4628    case MB_ID_CANCEL:
4629       if (gnObjectColorsToTargetColorMapping != NULL) {
4630          free(gnObjectColorsToTargetColorMapping);
4631          gnObjectColorsToTargetColorMapping = NULL;
4632       }
4633       return;
4634    }
4635    AllocTargetColors(ncolors);
4636    i = 0;
4637    for (red=0; red < 6; red++) {
4638       int red_intensity=GetMobileWebSafeIntensity(red), green;
4639 
4640       for (green=0; green < 6; green++) {
4641          int green_intensity=GetMobileWebSafeIntensity(green), blue;
4642 
4643          for (blue=0; blue < 6; blue++) {
4644             int blue_intensity=GetMobileWebSafeIntensity(blue);
4645 
4646             gTargetColors[i].red = red_intensity;
4647             gTargetColors[i].green = green_intensity;
4648             gTargetColors[i].blue = blue_intensity;
4649             i++;
4650          }
4651       }
4652    }
4653    if (gnFloyd) {
4654       if (DoPpm6(xpm_ptr)) {
4655          if (!InitTrueColorInfo(xpm_ptr->image, &gTrueColorInfo,
4656                xpm_ptr->image_w)) {
4657             sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
4658                   xpm_ptr->image_w, xpm_ptr->image_h);
4659             MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4660             if (gnObjectColorsToTargetColorMapping != NULL) {
4661                free(gnObjectColorsToTargetColorMapping);
4662                gnObjectColorsToTargetColorMapping = NULL;
4663             }
4664             return;
4665          }
4666       }
4667       rc = PrepareForErrorDiffuse(xpm_ptr);
4668       CleanUpIndexOfPixel();
4669       if (gpTargetImage != NULL) XDestroyImage(gpTargetImage);
4670       if (gpTargetBitmapImage != NULL) XDestroyImage(gpTargetBitmapImage);
4671       gpTargetImage = gpTargetBitmapImage = NULL;
4672    } else {
4673       if (DoPpm6(xpm_ptr)) {
4674          rc = TRUE;
4675       } else {
4676          rc = MapTargetColors(xpm_ptr);
4677       }
4678    }
4679    if (rc) {
4680       gnUserSpecifiedLevels = ncolors;
4681       if (gnFloyd) {
4682          gpConvolveFunc = (NLFN*)ConvolveToErrorDiffuse;
4683          gpConvolveCmdID = CMDID_REDUCETOMOBILEWEBCOLORS;
4684       } else {
4685          gpConvolveFunc = (NLFN*)ConvolveToReduceToPixmapColors;
4686          gpConvolveCmdID = CMDID_REDUCETOMOBILEWEBCOLORS;
4687       }
4688       gnConvolving = TRUE;
4689       DoImageProc(NULL);
4690       gnConvolving = FALSE;
4691       gpConvolveFunc = NULL;
4692       gpConvolveCmdID = (-1);
4693       gnUserSpecifiedLevels = (-1);
4694    }
4695    FreeTargetColors();
4696    if (gnFloyd) {
4697       if (gnImageTargetColor != NULL) {
4698          for (i=0; i < image_h; i++) free(gnImageTargetColor[i]);
4699          free(gnImageTargetColor);
4700          gnImageTargetColor = NULL;
4701       }
4702    }
4703    memset(&gXPmTarget, 0, sizeof(struct XPmRec));
4704 
4705    if (gnObjectColorsToTargetColorMapping != NULL) {
4706       free(gnObjectColorsToTargetColorMapping);
4707       gnObjectColorsToTargetColorMapping = NULL;
4708    }
4709    if (gnTgifColorsToObjectColorMapping != NULL) {
4710       free(gnTgifColorsToObjectColorMapping);
4711       gnTgifColorsToObjectColorMapping = NULL;
4712    }
4713 }
4714 
4715 /* ----------------------- SetDefaultColorLevels ----------------------- */
4716 
4717 #define PDCL_OK 0
4718 #define PDCL_TOO_LARGE 1
4719 #define PDCL_TOO_SMALL 2
4720 #define PDCL_BAD_FORMAT 3
4721 
4722 static
ParseDefaultColorLevels(buf,p_xcolor)4723 int ParseDefaultColorLevels(buf, p_xcolor)
4724    char *buf;
4725    XColor *p_xcolor;
4726 {
4727    char *red_ptr, *green_ptr, *blue_ptr;
4728 
4729    if ((red_ptr=strtok(buf, " ,:\t\n\r")) != NULL &&
4730          (green_ptr=strtok(NULL, " ,:\t\n\r")) != NULL &&
4731          (blue_ptr=strtok(NULL, " ,:\t\n\r")) != NULL) {
4732       int red, green, blue;
4733 
4734       red = atoi(red_ptr);
4735       green = atoi(green_ptr);
4736       blue = atoi(blue_ptr);
4737       if (red+green+blue > 8) {
4738          return PDCL_TOO_LARGE;
4739       } else if (red <= 0 || green <= 0 || blue <= 0) {
4740          return PDCL_TOO_SMALL;
4741       } else {
4742          p_xcolor->red = red;
4743          p_xcolor->green = green;
4744          p_xcolor->blue = blue;
4745          return PDCL_OK;
4746       }
4747    }
4748    return PDCL_BAD_FORMAT;
4749 }
4750 
SetDefaultColorLevels()4751 void SetDefaultColorLevels()
4752 {
4753    char szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1];
4754    XColor xcolor;
4755 
4756    sprintf(gszMsgBox, TgLoadString(STID_ENTER_NUM_BITS_IN_RGB),
4757          (int)gDefErrorDiffuseLevel.red, (int)gDefErrorDiffuseLevel.green,
4758          (int)gDefErrorDiffuseLevel.blue);
4759    *szSpec = '\0';
4760    Dialog(gszMsgBox, TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
4761    UtilTrimBlanks(szSpec);
4762    if (*szSpec == '\0') return;
4763 
4764    strcpy(szSpecCopy, szSpec);
4765    switch (ParseDefaultColorLevels(szSpecCopy, &xcolor)) {
4766    case PDCL_OK:
4767       gDefErrorDiffuseLevel.red = xcolor.red;
4768       gDefErrorDiffuseLevel.green = xcolor.green;
4769       gDefErrorDiffuseLevel.blue = xcolor.blue;
4770       Msg(TgLoadString(STID_RGB_LEVELS_CHANGED_TO));
4771       break;
4772    case PDCL_TOO_LARGE:
4773       sprintf(gszMsgBox, TgLoadString(STID_BAD_VAL_SUM_RGB_LEVEL), szSpec);
4774       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4775       break;
4776    case PDCL_TOO_SMALL:
4777       sprintf(gszMsgBox, TgLoadString(STID_BAD_VAL_GT_0_RGB_LEVEL), szSpec);
4778       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4779       break;
4780    case PDCL_BAD_FORMAT:
4781       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_3_VAL), szSpec);
4782       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4783       break;
4784    }
4785 }
4786 
4787 /* ----------------------- ReduceToDefaultColors ----------------------- */
4788 
ReduceToDefaultColors()4789 void ReduceToDefaultColors()
4790 {
4791    int i, red=0, rc=FALSE, ncolors=0, image_h=0;
4792    int red_bits, green_bits, blue_bits, red_levels, green_levels, blue_levels;
4793    struct XPmRec *xpm_ptr=NULL;
4794 
4795    if (!CheckSelectionForImageProc(CMDID_DEFAULTERRORDIFFUSE)) {
4796       return;
4797    }
4798    xpm_ptr = topSel->obj->detail.xpm;
4799    image_h = xpm_ptr->image_h;
4800 
4801    if (DoPpm6(xpm_ptr)) {
4802       gnObjectColorsToTargetColorMapping = NULL;
4803    } else {
4804       gnObjectColorsToTargetColorMapping =
4805             (int*)malloc(xpm_ptr->ncolors*sizeof(int));
4806       if (gnObjectColorsToTargetColorMapping == NULL) {
4807          FailAllocMessage();
4808          return;
4809       }
4810       memset(gnObjectColorsToTargetColorMapping, 0,
4811             xpm_ptr->ncolors*sizeof(int));
4812    }
4813    red_bits = gDefErrorDiffuseLevel.red;
4814    green_bits = gDefErrorDiffuseLevel.green;
4815    blue_bits = gDefErrorDiffuseLevel.blue;
4816    sprintf(gszMsgBox, TgLoadString(STID_RGB_LEVELS_ARE),
4817          red_bits, green_bits, blue_bits);
4818    Msg(gszMsgBox);
4819    red_levels = (1 << red_bits);
4820    green_levels = (1 << green_bits);
4821    blue_levels = (1 << blue_bits);
4822    ncolors = (1 << (red_bits+green_bits+blue_bits));
4823    if (ncolors > 0x100) ncolors = 0x100;
4824 
4825    memset(&gXPmTarget, 0, sizeof(struct XPmRec));
4826    gXPmTarget.ncolors = ncolors;
4827    gXPmTarget.userdata = (void*)CMDID_DEFAULTERRORDIFFUSE;
4828 
4829    AllocTargetColors(ncolors);
4830    i = 0;
4831    for (red=0; red < red_levels; red++) {
4832       int red_intensity=GetIntensity(red, red_levels-1), green;
4833 
4834       for (green=0; green < green_levels; green++) {
4835          int green_intensity=GetIntensity(green, green_levels-1), blue;
4836 
4837          for (blue=0; blue < blue_levels; blue++) {
4838             int blue_intensity=GetIntensity(blue, blue_levels-1);
4839 
4840             gTargetColors[i].red = red_intensity;
4841             gTargetColors[i].green = green_intensity;
4842             gTargetColors[i].blue = blue_intensity;
4843             i++;
4844          }
4845       }
4846    }
4847    if (DoPpm6(xpm_ptr)) {
4848       rc = TRUE;
4849    } else {
4850       rc = MapTargetColors(xpm_ptr);
4851    }
4852    if (rc) {
4853       gnUserSpecifiedLevels = ncolors;
4854       gpConvolveFunc = (NLFN*)ConvolveToReduceToPixmapColors;
4855       gpConvolveCmdID = CMDID_DEFAULTERRORDIFFUSE;
4856       gnConvolving = TRUE;
4857       DoImageProc(NULL);
4858       gnConvolving = FALSE;
4859       gpConvolveFunc = NULL;
4860       gpConvolveCmdID = (-1);
4861       gnUserSpecifiedLevels = (-1);
4862    }
4863    FreeTargetColors();
4864    memset(&gXPmTarget, 0, sizeof(struct XPmRec));
4865 
4866    if (gnObjectColorsToTargetColorMapping != NULL) {
4867       free(gnObjectColorsToTargetColorMapping);
4868       gnObjectColorsToTargetColorMapping = NULL;
4869    }
4870    if (gnTgifColorsToObjectColorMapping != NULL) {
4871       free(gnTgifColorsToObjectColorMapping);
4872       gnTgifColorsToObjectColorMapping = NULL;
4873    }
4874 }
4875 
4876 /* ----------------------- DefaultErrorDiffuse ----------------------- */
4877 
DefaultErrorDiffuse()4878 void DefaultErrorDiffuse()
4879 {
4880    int i, red, rc, ncolors, image_h;
4881    int red_bits, green_bits, blue_bits, red_levels, green_levels, blue_levels;
4882    struct XPmRec *xpm_ptr=NULL;
4883 
4884    if (!CheckSelectionForImageProc(CMDID_DEFAULTERRORDIFFUSE)) {
4885       return;
4886    }
4887    xpm_ptr = topSel->obj->detail.xpm;
4888    image_h = xpm_ptr->image_h;
4889 
4890    red_bits = gDefErrorDiffuseLevel.red;
4891    green_bits = gDefErrorDiffuseLevel.green;
4892    blue_bits = gDefErrorDiffuseLevel.blue;
4893    sprintf(gszMsgBox, TgLoadString(STID_RGB_LEVELS_ARE),
4894          red_bits, green_bits, blue_bits);
4895    Msg(gszMsgBox);
4896    red_levels = (1 << red_bits);
4897    green_levels = (1 << green_bits);
4898    blue_levels = (1 << blue_bits);
4899    ncolors = (1 << (red_bits+green_bits+blue_bits));
4900    if (ncolors > 0x100) ncolors = 0x100;
4901 
4902    memset(&gXPmTarget, 0, sizeof(struct XPmRec));
4903    gXPmTarget.ncolors = ncolors;
4904    gXPmTarget.userdata = (void*)CMDID_DEFAULTERRORDIFFUSE;
4905 
4906    gnFloyd = TRUE;
4907 
4908    AllocTargetColors(ncolors);
4909    i = 0;
4910    for (red=0; red < red_levels; red++) {
4911       int red_intensity=GetIntensity(red, red_levels-1), green;
4912 
4913       for (green=0; green < green_levels; green++) {
4914          int green_intensity=GetIntensity(green, green_levels-1), blue;
4915 
4916          for (blue=0; blue < blue_levels; blue++) {
4917             int blue_intensity=GetIntensity(blue, blue_levels-1);
4918 
4919             gTargetColors[i].red = red_intensity;
4920             gTargetColors[i].green = green_intensity;
4921             gTargetColors[i].blue = blue_intensity;
4922             i++;
4923          }
4924       }
4925    }
4926    if (DoPpm6(xpm_ptr)) {
4927       if (!InitTrueColorInfo(xpm_ptr->image, &gTrueColorInfo,
4928             xpm_ptr->image_w)) {
4929          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
4930                xpm_ptr->image_w, xpm_ptr->image_h);
4931          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4932          return;
4933       }
4934    }
4935    rc = PrepareForErrorDiffuse(xpm_ptr);
4936 
4937    CleanUpIndexOfPixel();
4938    if (gpTargetImage != NULL) XDestroyImage(gpTargetImage);
4939    if (gpTargetBitmapImage != NULL) XDestroyImage(gpTargetBitmapImage);
4940    gpTargetImage = gpTargetBitmapImage = NULL;
4941 
4942    if (rc) {
4943       gnUserSpecifiedLevels = ncolors;
4944       gpConvolveFunc = (NLFN*)ConvolveToErrorDiffuse;
4945       gpConvolveCmdID = CMDID_DEFAULTERRORDIFFUSE;
4946       gnConvolving = TRUE;
4947       DoImageProc(NULL);
4948       gnConvolving = FALSE;
4949       gpConvolveFunc = NULL;
4950       gpConvolveCmdID = (-1);
4951       gnUserSpecifiedLevels = (-1);
4952    }
4953    FreeTargetColors();
4954    if (gnImageTargetColor != NULL) {
4955       for (i=0; i < image_h; i++) free(gnImageTargetColor[i]);
4956       free(gnImageTargetColor);
4957       gnImageTargetColor = NULL;
4958    }
4959    gnFloyd = FALSE;
4960    memset(&gXPmTarget, 0, sizeof(struct XPmRec));
4961 }
4962 
4963 /* ----------------------- Spread ----------------------- */
4964 
4965 static int gnAmountToSpread=0;
4966 static int **gnSpreadImageIndex=NULL;
4967 
4968 static
ConvolveToSpread(x,y)4969 int ConvolveToSpread(x, y)
4970    int x, y;
4971 {
4972    if (gConvExtraInfo.fp != NULL) {
4973       XColor **xcolors=gConvExtraInfo.xcolors;
4974       FILE *fp=gConvExtraInfo.fp;
4975       int w=gConvExtraInfo.image_w, h=gConvExtraInfo.image_h;
4976       unsigned char buf[3];
4977 
4978       if (x == 0 && y == 0) {
4979          int r=0;
4980 
4981          srand(0);
4982          for (r=0; r < h; r++) {
4983             int c=0;
4984 
4985             for (c=0; c < w; c++) {
4986                int dx=(rand() % gnAmountToSpread) - (gnAmountToSpread>>1);
4987                int dy=(rand() % gnAmountToSpread) - (gnAmountToSpread>>1);
4988                int tmp_y=r+dy, tmp_x=c+dx;
4989 
4990                if (tmp_x >= 0 && tmp_x < w && tmp_y >= 0 && tmp_y < h) {
4991                   XColor tmp_xcolor;
4992 
4993                   memcpy(&tmp_xcolor, &xcolors[r][c], sizeof(XColor));
4994                   memcpy(&xcolors[r][c], &xcolors[tmp_y][tmp_x],
4995                         sizeof(XColor));
4996                   memcpy(&xcolors[tmp_y][tmp_x], &tmp_xcolor, sizeof(XColor));
4997                }
4998             }
4999          }
5000       }
5001       buf[0] = (unsigned char)xcolors[y][x].red;
5002       buf[1] = (unsigned char)xcolors[y][x].green;
5003       buf[2] = (unsigned char)xcolors[y][x].blue;
5004       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
5005 
5006       return TRUE;
5007    } else {
5008       return GetOrAllocHistogramIndex(&tgifColors[gnSpreadImageIndex[y][x]]);
5009    }
5010 }
5011 
5012 static
CleanUpSpreadData()5013 void CleanUpSpreadData()
5014 {
5015    int i, image_h=topSel->obj->detail.xpm->image_h;
5016 
5017    if (gnSpreadImageIndex != NULL) {
5018       for (i=0; i < image_h; i++) free(gnSpreadImageIndex[i]);
5019       free(gnSpreadImageIndex);
5020       gnSpreadImageIndex = NULL;
5021    }
5022 }
5023 
5024 static
ComputeSpreadData()5025 int ComputeSpreadData()
5026 {
5027    struct XPmRec *xpm_ptr=topSel->obj->detail.xpm;
5028    int i, image_w=xpm_ptr->image_w, image_h=xpm_ptr->image_h;
5029    ProgressInfo pi;
5030    Pixmap pixmap=xpm_ptr->pixmap;
5031    XImage *image=XGetImage(mainDisplay, pixmap, 0, 0, image_w, image_h,
5032          AllPlanes, ZPixmap);
5033 
5034    if (image == NULL) {
5035       FailAllocMessage();
5036       return FALSE;
5037    }
5038    if (!CreatePixelToIndexMapping()) {
5039       XDestroyImage(image);
5040       return FALSE;
5041    }
5042    gnSpreadImageIndex = (int**)malloc(image_h*sizeof(int*));
5043    if (gnSpreadImageIndex == NULL) {
5044       FailAllocMessage();
5045       XDestroyImage(image);
5046       CleanUpIndexOfPixel();
5047       return FALSE;
5048    }
5049    gnAmountToSpread++;
5050    BeginProgress(&pi, image_h);
5051    for (i=0; i < image_h; i++) {
5052       int j;
5053 
5054       UpdateProgress(&pi, i);
5055       gnSpreadImageIndex[i] = (int*)malloc(image_w*sizeof(int));
5056       if (gnSpreadImageIndex[i] == NULL) {
5057          FailAllocMessage();
5058          for (j=0; j < i; j++) free(gnSpreadImageIndex[j]);
5059          free(gnSpreadImageIndex);
5060          gnSpreadImageIndex = NULL;
5061          XDestroyImage(image);
5062          CleanUpIndexOfPixel();
5063          return FALSE;
5064       }
5065       for (j=0; j < image_w; j++) {
5066          gnSpreadImageIndex[i][j] = GetIndexOfPixel(XGetPixel(image,j,i));
5067       }
5068    }
5069    BeginProgress(&pi, image_h);
5070    srand(0);
5071    for (i=0; i < image_h; i++) {
5072       int j;
5073 
5074       UpdateProgress(&pi, i);
5075       for (j=0; j < image_w; j++) {
5076          int dx=(rand() % gnAmountToSpread) - (gnAmountToSpread>>1);
5077          int dy=(rand() % gnAmountToSpread) - (gnAmountToSpread>>1);
5078          int y=i+dy, x=j+dx, tmp_index;
5079 
5080          if (x >= 0 && x < image_w && y >= 0 && y < image_h) {
5081             tmp_index = gnSpreadImageIndex[i][j];
5082             gnSpreadImageIndex[i][j] = gnSpreadImageIndex[y][x];
5083             gnSpreadImageIndex[y][x] = tmp_index;
5084          }
5085       }
5086    }
5087    XDestroyImage(image);
5088    CleanUpIndexOfPixel();
5089 
5090    return TRUE;
5091 }
5092 
Spread()5093 void Spread()
5094 {
5095    char *c_ptr=NULL, szSpec[MAXSTRING+1], szSpecCopy[MAXSTRING+1];
5096 
5097    if (!CheckSelectionForImageProc(CMDID_SPREAD)) {
5098       return;
5099    }
5100    *szSpec = '\0';
5101    Dialog(TgLoadString(STID_ENTER_INT_AMT_TO_SPREAD),
5102          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
5103    UtilTrimBlanks(szSpec);
5104    if (*szSpec == '\0') return;
5105 
5106    strcpy(szSpecCopy, szSpec);
5107    if ((c_ptr=strtok(szSpec, " ,\t\n\r")) == NULL) return;
5108    gnAmountToSpread = atoi(c_ptr);
5109    if (gnAmountToSpread <= 0) {
5110       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), szSpecCopy);
5111       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5112       return;
5113    }
5114    if (!DoPpm6(topSel->obj->detail.xpm)) {
5115       if (!ComputeSpreadData()) {
5116          return;
5117       }
5118    }
5119    gpConvolveFunc = (NLFN*)ConvolveToSpread;
5120    gpConvolveCmdID = CMDID_SPREAD;
5121    gnConvolving = TRUE;
5122    DoImageProc(NULL);
5123    gnConvolving = FALSE;
5124    gpConvolveFunc = NULL;
5125    gpConvolveCmdID = (-1);
5126 
5127    CleanUpSpreadData();
5128 }
5129 
5130 /* ----------------------- Sharpen ----------------------- */
5131 
5132 static
ConvolveToSharpen(x,y)5133 int ConvolveToSharpen(x, y)
5134    int x, y;
5135    /*
5136     * [  0 -1  0 ]
5137     * [ -1  5 -1 ]
5138     * [  0 -1  0 ]
5139     */
5140 {
5141    if (gConvExtraInfo.fp != NULL) {
5142       XColor **xcolors=gConvExtraInfo.xcolors;
5143       FILE *fp=gConvExtraInfo.fp;
5144       int w=gConvExtraInfo.image_w, h=gConvExtraInfo.image_h;
5145       unsigned char buf[3];
5146 
5147       if (x == 0 || x == w-1 || y == 0 || y == h-1) {
5148          buf[0] = (unsigned char)xcolors[y][x].red;
5149          buf[1] = (unsigned char)xcolors[y][x].green;
5150          buf[2] = (unsigned char)xcolors[y][x].blue;
5151       } else {
5152          int r=0, g=0, b=0;
5153 
5154          r = (int)(
5155                (((int)xcolors[y][x].red)<<3) -
5156                ((int)xcolors[y][x-1].red) -
5157                ((int)xcolors[y][x+1].red) -
5158                ((int)xcolors[y-1][x].red) -
5159                ((int)xcolors[y+1][x].red));
5160          g = (int)(
5161                (((int)xcolors[y][x].green)<<3) -
5162                ((int)xcolors[y][x-1].green) -
5163                ((int)xcolors[y][x+1].green) -
5164                ((int)xcolors[y-1][x].green) -
5165                ((int)xcolors[y+1][x].green));
5166          b = (int)(
5167                (((int)xcolors[y][x].blue)<<3) -
5168                ((int)xcolors[y][x-1].blue) -
5169                ((int)xcolors[y][x+1].blue) -
5170                ((int)xcolors[y-1][x].blue) -
5171                ((int)xcolors[y+1][x].blue));
5172          r >>= 2;
5173          g >>= 2;
5174          b >>= 2;
5175          if (r < 0) r = 0;
5176          if (g < 0) g = 0;
5177          if (b < 0) b = 0;
5178          if (r > 255) r = 255;
5179          if (g > 255) g = 255;
5180          if (b > 255) b = 255;
5181 
5182          buf[0] = (unsigned char)(unsigned int)r;
5183          buf[1] = (unsigned char)(unsigned int)g;
5184          buf[2] = (unsigned char)(unsigned int)b;
5185       }
5186       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
5187 
5188       return TRUE;
5189    } else {
5190       register long red, green, blue;
5191       XColor xcolor, *color_ptr;
5192 
5193       if (x == 0 || x == gnImageW-1 || y == 0 || y == gnImageH-1) {
5194          return GetOrAllocHistogramIndex(&tgifColors[gnOrigImageIndex[y][x]]);
5195       }
5196       color_ptr = (&tgifColors[gnOrigImageIndex[y][x]]);
5197       red = ((long)color_ptr->red) << 3;
5198       green = ((long)color_ptr->green) << 3;
5199       blue = ((long)color_ptr->blue) << 3;
5200       color_ptr = (&tgifColors[gnOrigImageIndex[y][x-1]]);
5201       red -= ((long)color_ptr->red);
5202       green -= ((long)color_ptr->green);
5203       blue -= ((long)color_ptr->blue);
5204       color_ptr = (&tgifColors[gnOrigImageIndex[y][x+1]]);
5205       red -= ((long)color_ptr->red);
5206       green -= ((long)color_ptr->green);
5207       blue -= ((long)color_ptr->blue);
5208       color_ptr = (&tgifColors[gnOrigImageIndex[y-1][x]]);
5209       red -= ((long)color_ptr->red);
5210       green -= ((long)color_ptr->green);
5211       blue -= ((long)color_ptr->blue);
5212       color_ptr = (&tgifColors[gnOrigImageIndex[y+1][x]]);
5213       red = (red - ((long)color_ptr->red)) >> 2;
5214       green = (green - ((long)color_ptr->green)) >> 2;
5215       blue = (blue - ((long)color_ptr->blue)) >> 2;
5216 
5217       memset(&xcolor, 0, sizeof(XColor));
5218       xcolor.red = (red > 0L ?
5219             (red > 0x0000ffff ? 0x0ffff : (unsigned int)(red&0xffff)) : 0);
5220       xcolor.green = (green > 0L ?
5221             (green > 0x0000ffff ? 0x0ffff : (unsigned int)(green&0xffff)) : 0);
5222       xcolor.blue = (blue > 0L ?
5223             (blue > 0x0000ffff ? 0x0ffff : (unsigned int)(blue&0xffff)) : 0);
5224       return GetOrAllocHistogramIndex(&xcolor);
5225    }
5226 }
5227 
Sharpen()5228 void Sharpen()
5229 {
5230    if (!CheckSelectionForImageProc(CMDID_SHARPEN)) return;
5231    if (topSel->obj->detail.xpm->image_w < 2 ||
5232          topSel->obj->detail.xpm->image_h < 2) {
5233       MsgBox(TgLoadString(STID_SEL_TOO_THIN_FLAT_FOR_SHARPEN), TOOL_NAME,
5234             INFO_MB);
5235       return;
5236    }
5237    gpConvolveFunc = (NLFN*)ConvolveToSharpen;
5238    gpConvolveCmdID = CMDID_SHARPEN;
5239    gnConvolving = TRUE;
5240    DoImageProc(NULL);
5241    gnConvolving = FALSE;
5242    gpConvolveFunc = NULL;
5243    gpConvolveCmdID = (-1);
5244 }
5245 
5246 /* ----------------------- Blur3, Blur5, Blur7 ----------------------- */
5247 
5248 static int gnBlurSize=1;
5249 
5250 static
ConvolveToBlur(x,y)5251 int ConvolveToBlur(x, y)
5252    int x, y;
5253    /*
5254     *                 [ 1/25 1/25 1/25 ]
5255     * [ 1/9 1/9 1/9 ] [ 1/25 1/25 1/25 ]
5256     * [ 1/9 1/9 1/9 ] [ 1/25 1/25 1/25 ] ...
5257     * [ 1/9 1/9 1/9 ] [ 1/25 1/25 1/25 ]
5258     *                 [ 1/25 1/25 1/25 ]
5259     */
5260 {
5261    if (gConvExtraInfo.fp != NULL) {
5262       XColor **xcolors=gConvExtraInfo.xcolors;
5263       FILE *fp=gConvExtraInfo.fp;
5264       int w=gConvExtraInfo.image_w, h=gConvExtraInfo.image_h;
5265       unsigned char buf[3];
5266 
5267       unsigned int r=0, g=0, b=0;
5268       double dr=(double)0, dg=(double)0, db=(double)0;
5269       int x_index=0, num_cells=0;
5270 
5271       for (x_index=x-gnBlurSize; x_index <= x+gnBlurSize; x_index++) {
5272          int y_index=0;
5273 
5274          if (x_index >= 0 && x_index < w) {
5275             for (y_index=y-gnBlurSize; y_index <= y+gnBlurSize; y_index++) {
5276                if (y_index >= 0 && y_index < h) {
5277                   r += ((int)xcolors[y_index][x_index].red);
5278                   g += ((int)xcolors[y_index][x_index].green);
5279                   b += ((int)xcolors[y_index][x_index].blue);
5280                   num_cells++;
5281                }
5282             }
5283          }
5284       }
5285       dr = ((double)r) / ((double)num_cells);
5286       dg = ((double)g) / ((double)num_cells);
5287       db = ((double)b) / ((double)num_cells);
5288       r = (unsigned int)round(dr);
5289       g = (unsigned int)round(dg);
5290       b = (unsigned int)round(db);
5291       if (r > 255) r = 255;
5292       if (g > 255) g = 255;
5293       if (b > 255) b = 255;
5294 
5295       buf[0] = (unsigned char)r;
5296       buf[1] = (unsigned char)g;
5297       buf[2] = (unsigned char)b;
5298 
5299       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
5300 
5301       return TRUE;
5302    } else {
5303       register long red=0L, green=0L, blue=0L;
5304       XColor xcolor;
5305       int x_index, array_size=((gnBlurSize<<1)+1);
5306 
5307       if (x < gnBlurSize || x >= gnImageW-gnBlurSize ||
5308             y < gnBlurSize || y >= gnImageH-gnBlurSize) {
5309          return GetOrAllocHistogramIndex(&tgifColors[gnOrigImageIndex[y][x]]);
5310       }
5311       for (x_index=x-gnBlurSize; x_index <= x+gnBlurSize; x_index++) {
5312          int y_index;
5313 
5314          for (y_index=y-gnBlurSize; y_index <= y+gnBlurSize; y_index++) {
5315             register XColor *color_ptr;
5316 
5317             color_ptr = (&tgifColors[gnOrigImageIndex[y_index][x_index]]);
5318             red += ((long)color_ptr->red);
5319             green += ((long)color_ptr->green);
5320             blue += ((long)color_ptr->blue);
5321          }
5322       }
5323       array_size *= array_size;
5324       red /= array_size;
5325       green /= array_size;
5326       blue /= array_size;
5327 
5328       memset(&xcolor, 0, sizeof(XColor));
5329       xcolor.red = (red > 0L ?
5330             (red > 0x0000ffff ? 0x0ffff : (unsigned int)(red&0xffff)) : 0);
5331       xcolor.green = (green > 0L ?
5332             (green > 0x0000ffff ? 0x0ffff : (unsigned int)(green&0xffff)) : 0);
5333       xcolor.blue = (blue > 0L ?
5334             (blue > 0x0000ffff ? 0x0ffff : (unsigned int)(blue&0xffff)) : 0);
5335       return GetOrAllocHistogramIndex(&xcolor);
5336    }
5337 }
5338 
5339 static
Blur(nSize)5340 void Blur(nSize)
5341    int nSize;
5342 {
5343    int cmdid=CMDID_BLUR3;
5344 
5345    switch (nSize) {
5346    case 3: cmdid=CMDID_BLUR3; break;
5347    case 5: cmdid=CMDID_BLUR5; break;
5348    case 7: cmdid=CMDID_BLUR7; break;
5349    }
5350    gnBlurSize = ((nSize-1)>>1);
5351    if (!CheckSelectionForImageProc(cmdid)) return;
5352    if (topSel->obj->detail.xpm->image_w <= gnBlurSize ||
5353          topSel->obj->detail.xpm->image_h <= gnBlurSize) {
5354       sprintf(gszMsgBox, TgLoadString(STID_SEL_TOO_THIN_FLAT_FOR_BLUR),
5355             nSize, nSize);
5356       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5357       return;
5358    }
5359    gpConvolveFunc = (NLFN*)ConvolveToBlur;
5360    gpConvolveCmdID = CMDID_BLUR3;
5361    gnConvolving = TRUE;
5362    DoImageProc(NULL);
5363    gnConvolving = FALSE;
5364    gpConvolveFunc = NULL;
5365    gpConvolveCmdID = (-1);
5366 }
5367 
Blur3()5368 void Blur3() { Blur(3); }
Blur5()5369 void Blur5() { Blur(5); }
Blur7()5370 void Blur7() { Blur(7); }
5371 
5372 /* ----------------------- AlphaCombine ----------------------- */
5373 
5374 static
PointInRect(x,y,p_bbox)5375 int PointInRect(x, y, p_bbox)
5376    int  x, y;
5377    struct BBRec *p_bbox;
5378 {
5379    return (x>=p_bbox->ltx && y>=p_bbox->lty && x<p_bbox->rbx && y<p_bbox->rby);
5380 }
5381 
5382 #define INSIDE_NONE  0x0
5383 #define INSIDE_FG    0x1
5384 #define INSIDE_BG    0x2
5385 #define INSIDE_ALPHA 0x4
5386 
5387 static
ConvolveToAlphaCombine(x,y)5388 int ConvolveToAlphaCombine(x, y)
5389    int x, y;
5390 {
5391    XColor fg_xcolor, bg_xcolor, alpha_xcolor, xcolor;
5392    int inside_flag=INSIDE_NONE, pixel;
5393    double alpha=(double)0, beta=(double)0;
5394    long red=0L, green=0L, blue=0L;
5395 
5396    if (PointInRect(x, y, &gAlphaCombineBBox)) inside_flag |= INSIDE_ALPHA;
5397    if (PointInRect(x, y, &gFgCombineBBox)) inside_flag |= INSIDE_FG;
5398    if (PointInRect(x, y, &gBgCombineBBox)) inside_flag |= INSIDE_BG;
5399 
5400    if (gConvExtraInfo.fp != NULL) {
5401       FILE *fp=gConvExtraInfo.fp;
5402       XColor **xcolors=NULL;
5403       int image_x=0, image_y=0;
5404       unsigned int r=0, g=0, b=0;
5405       unsigned char buf[3];
5406 
5407       switch (inside_flag) {
5408       case 0x0:
5409       case 0x4: /* INSIDE_ALPHA */
5410          buf[0] = (unsigned char)gConvExtraInfo.my_bg_xcolor.red;
5411          buf[1] = (unsigned char)gConvExtraInfo.my_bg_xcolor.green;
5412          buf[2] = (unsigned char)gConvExtraInfo.my_bg_xcolor.blue;
5413          break;
5414       case 0x1: /* INSIDE_FG */
5415       case 0x3: /* INSIDE_BG | INSIDE_FG */
5416       case 0x5: /* INSIDE_ALPHA | INSIDE_FG */
5417          xcolors = gConvExtraInfo.xcolors;
5418          image_x = x-gFgCombineBBox.ltx;
5419          image_y = y-gFgCombineBBox.lty;
5420          buf[0] = (unsigned char)xcolors[image_y][image_x].red;
5421          buf[1] = (unsigned char)xcolors[image_y][image_x].green;
5422          buf[2] = (unsigned char)xcolors[image_y][image_x].blue;
5423          break;
5424       case 0x2: /* INSIDE_BG */
5425       case 0x6: /* INSIDE_ALPHA | INSIDE_BG */
5426          xcolors = gConvExtraInfo.bg_xcolors;
5427          image_x = x-gBgCombineBBox.ltx;
5428          image_y = y-gBgCombineBBox.lty;
5429          buf[0] = (unsigned char)xcolors[image_y][image_x].red;
5430          buf[1] = (unsigned char)xcolors[image_y][image_x].green;
5431          buf[2] = (unsigned char)xcolors[image_y][image_x].blue;
5432          break;
5433       case 0x7: /* INSIDE_ALPHA | INSIDE_BG | INSIDE_FG */
5434          xcolors = gConvExtraInfo.xcolors;
5435          image_x = x-gFgCombineBBox.ltx;
5436          image_y = y-gFgCombineBBox.lty;
5437          memcpy(&fg_xcolor, &xcolors[image_y][image_x], sizeof(XColor));
5438          xcolors = gConvExtraInfo.bg_xcolors;
5439          image_x = x-gBgCombineBBox.ltx;
5440          image_y = y-gBgCombineBBox.lty;
5441          memcpy(&bg_xcolor, &xcolors[image_y][image_x], sizeof(XColor));
5442          xcolors = gConvExtraInfo.alpha_xcolors;
5443          image_x = x-gAlphaCombineBBox.ltx;
5444          image_y = y-gAlphaCombineBBox.lty;
5445          memcpy(&alpha_xcolor, &xcolors[image_y][image_x], sizeof(XColor));
5446          alpha = (0.299*((double)alpha_xcolor.red) +
5447                0.587*((double)alpha_xcolor.green) +
5448                0.114*((double)alpha_xcolor.blue)) / 255.0;
5449          beta = ((double)1) - alpha;
5450          r = ((double)(fg_xcolor.red)) * alpha +
5451                ((double)(bg_xcolor.red)) * beta;
5452          g = ((double)(fg_xcolor.green)) * alpha +
5453                ((double)(bg_xcolor.green)) * beta;
5454          b = ((double)(fg_xcolor.blue)) * alpha +
5455                ((double)(bg_xcolor.blue)) * beta;
5456          if (r < 0) r = 0;
5457          if (g < 0) g = 0;
5458          if (b < 0) b = 0;
5459          if (r > 255) r = 255;
5460          if (g > 255) g = 255;
5461          if (b > 255) b = 255;
5462          buf[0] = (unsigned char)r;
5463          buf[1] = (unsigned char)g;
5464          buf[2] = (unsigned char)b;
5465          break;
5466       }
5467       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
5468 
5469       return TRUE;
5470    } else {
5471       switch (inside_flag) {
5472       case 0x0:
5473       case 0x4: /* INSIDE_ALPHA */
5474          if (myFileBgColorStr == NULL) {
5475             return GetOrAllocHistogramIndex(&myBgColor);
5476          } else {
5477             return GetOrAllocHistogramIndex(&myFileBgColor);
5478          }
5479       case 0x1:
5480       case 0x3: /* INSIDE_BG | INSIDE_FG */
5481       case 0x5: /* INSIDE_ALPHA | INSIDE_FG */
5482          /* INSIDE_FG */
5483          pixel = XGetPixel(gpFgImage, x-gFgCombineBBox.ltx,
5484                y-gFgCombineBBox.lty);
5485          return GetOrAllocHistogramIndex(&tgifColors[GetIndexOfPixel(pixel)]);
5486       case 0x2: /* INSIDE_BG */
5487       case 0x6: /* INSIDE_ALPHA | INSIDE_BG */
5488          pixel = XGetPixel(gpBgImage, x-gBgCombineBBox.ltx,
5489                y-gBgCombineBBox.lty);
5490          return GetOrAllocHistogramIndex(&tgifColors[GetIndexOfPixel(pixel)]);
5491       case 0x7:
5492          /* INSIDE_ALPHA | INSIDE_BG | INSIDE_FG */
5493          pixel = XGetPixel(gpFgImage, x-gFgCombineBBox.ltx,
5494                y-gFgCombineBBox.lty);
5495          memcpy(&fg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5496                sizeof(XColor));
5497          pixel = XGetPixel(gpBgImage, x-gBgCombineBBox.ltx,
5498                y-gBgCombineBBox.lty);
5499          memcpy(&bg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5500                sizeof(XColor));
5501          pixel = XGetPixel(gpAlphaImage, x-gAlphaCombineBBox.ltx,
5502                y-gAlphaCombineBBox.lty);
5503          memcpy(&alpha_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5504                sizeof(XColor));
5505          alpha = ((double)(0.299*((double)alpha_xcolor.red) +
5506                0.587*((double)alpha_xcolor.green) +
5507                0.114*((double)alpha_xcolor.blue))) / 65536.0;
5508          beta = 1.0 - alpha;
5509          red = (long)(((double)fg_xcolor.red)*alpha +
5510                ((double)bg_xcolor.red)*beta);
5511          green = (long)(((double)fg_xcolor.green)*alpha +
5512                ((double)bg_xcolor.green)*beta);
5513          blue = (long)(((double)fg_xcolor.blue)*alpha +
5514                ((double)bg_xcolor.blue)*beta);
5515          break;
5516       }
5517       memset(&xcolor, 0, sizeof(XColor));
5518       xcolor.red = (red > 0L ?
5519             (red > 0x0000ffff ? 0x0ffff : (unsigned int)(red&0xffff)) : 0);
5520       xcolor.green = (green > 0L ?
5521             (green > 0x0000ffff ? 0x0ffff : (unsigned int)(green&0xffff)) : 0);
5522       xcolor.blue = (blue > 0L ?
5523             (blue > 0x0000ffff ? 0x0ffff : (unsigned int)(blue&0xffff)) : 0);
5524       return GetOrAllocHistogramIndex(&xcolor);
5525    }
5526 }
5527 
5528 static
OffsetRect(p_bbox,dx,dy)5529 void OffsetRect(p_bbox, dx, dy)
5530    struct BBRec *p_bbox;
5531    int dx, dy;
5532 {
5533    p_bbox->ltx += dx; p_bbox->lty += dy;
5534    p_bbox->rbx += dx; p_bbox->rby += dy;
5535 }
5536 
5537 static
CleanUpAlphaCombine()5538 void CleanUpAlphaCombine()
5539 {
5540    if (gpFgImage != NULL) XDestroyImage(gpFgImage);
5541    if (gpBgImage != NULL) XDestroyImage(gpBgImage);
5542    if (gpAlphaImage != NULL) XDestroyImage(gpAlphaImage);
5543    if (gpFgBitmapImage != NULL) XDestroyImage(gpFgBitmapImage);
5544    if (gpBgBitmapImage != NULL) XDestroyImage(gpBgBitmapImage);
5545    if (gpAlphaBitmapImage != NULL) XDestroyImage(gpAlphaBitmapImage);
5546    gpFgImage = gpBgImage = gpAlphaImage =
5547          gpFgBitmapImage = gpBgBitmapImage = gpAlphaBitmapImage = NULL;
5548    gpFgObj = gpBgObj = gpAlphaObj = NULL;
5549 }
5550 
5551 static
PrepareForAlphaCombine()5552 int PrepareForAlphaCombine()
5553 {
5554    memcpy(&gFgCombineBBox, &gpFgObj->obbox, sizeof(struct BBRec));
5555    memcpy(&gBgCombineBBox, &gpBgObj->obbox, sizeof(struct BBRec));
5556    if (gpAlphaObj != NULL) {
5557       memcpy(&gAlphaCombineBBox, &gpAlphaObj->obbox, sizeof(struct BBRec));
5558    }
5559    UnionRect(&gFgCombineBBox, &gBgCombineBBox, &gTotalCombineBBox);
5560    if (gpAlphaObj != NULL) {
5561       UnionRect(&gAlphaCombineBBox, &gTotalCombineBBox, &gTotalCombineBBox);
5562    }
5563    gnCombineW = gTotalCombineBBox.rbx - gTotalCombineBBox.ltx;
5564    gnCombineH = gTotalCombineBBox.rby - gTotalCombineBBox.lty;
5565    OffsetRect(&gFgCombineBBox, -gTotalCombineBBox.ltx, -gTotalCombineBBox.lty);
5566    OffsetRect(&gBgCombineBBox, -gTotalCombineBBox.ltx, -gTotalCombineBBox.lty);
5567    if (gpAlphaObj != NULL) {
5568       OffsetRect(&gAlphaCombineBBox,
5569             -gTotalCombineBBox.ltx, -gTotalCombineBBox.lty);
5570    }
5571    gpAlphaImage = gpAlphaBitmapImage = NULL;
5572    if (!GetXPmImages(gpFgObj->detail.xpm, &gpFgImage, &gpFgBitmapImage) ||
5573          !GetXPmImages(gpBgObj->detail.xpm, &gpBgImage, &gpBgBitmapImage) ||
5574          (gpAlphaObj != NULL && !GetXPmImages(gpAlphaObj->detail.xpm,
5575          &gpAlphaImage, &gpAlphaBitmapImage))) {
5576       return FALSE;
5577    }
5578    return TRUE;
5579 }
5580 
AlphaCombine()5581 void AlphaCombine()
5582 {
5583    char szBuf[MAXSTRING+1];
5584    int num_objs=0;
5585 
5586    strcpy(szBuf, GetImageProcName(CMDID_ALPHACOMBINE));
5587    gpFgObj = gpBgObj = gpAlphaObj = NULL;
5588    if (curChoice == NOTHING && numObjSelected == 3) {
5589       struct SelRec *sel_ptr;
5590       struct ObjRec *obj_ptr;
5591       int ok=TRUE;
5592 
5593       for (obj_ptr=topObj; ok && obj_ptr != NULL; obj_ptr=obj_ptr->next) {
5594          for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
5595             if (obj_ptr == sel_ptr->obj) {
5596                if (obj_ptr->type == OBJ_XPM) {
5597                   struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
5598                   int image_w=obj_ptr->obbox.rbx-obj_ptr->obbox.ltx;
5599                   int image_h=obj_ptr->obbox.rby-obj_ptr->obbox.lty;
5600 
5601                   if (obj_ptr->ctm != NULL || xpm_ptr->image_w != image_w ||
5602                         xpm_ptr->image_h != image_h) {
5603                      char szBuf1[MAXSTRING+1];
5604 
5605                      strcpy(szBuf1,
5606                            GetImageProcName(CMDID_REGENERATEIMAGE));
5607                      sprintf(gszMsgBox,
5608                            TgLoadString(STID_IMAGE_PROC_CANT_USE_XFORMED),
5609                            szBuf, szBuf1);
5610                      MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5611                      return;
5612                   }
5613                   switch (num_objs++) {
5614                   case 0: gpFgObj = obj_ptr; break;
5615                   case 1: gpAlphaObj = obj_ptr; break;
5616                   case 2: gpBgObj = obj_ptr; break;
5617                   }
5618                } else {
5619                   ok = FALSE;
5620                   break;
5621                }
5622             }
5623          }
5624       }
5625       if (!ok) num_objs = 0;
5626    }
5627    if (num_objs != 3) {
5628       sprintf(gszMsgBox, TgLoadString(STID_SEL_3_XPM_FOR_IMAGEPROC_CMD), szBuf);
5629       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5630       return;
5631    }
5632    if (PrepareForAlphaCombine()) {
5633       gnCombining = TRUE;
5634       gpConvolveFunc = (NLFN*)ConvolveToAlphaCombine;
5635       gpConvolveCmdID = CMDID_ALPHACOMBINE;
5636       gnConvolving = TRUE;
5637       DoImageProc(NULL);
5638       gnConvolving = FALSE;
5639       gpConvolveFunc = NULL;
5640       gpConvolveCmdID = (-1);
5641       gnCombining = FALSE;
5642    }
5643    CleanUpAlphaCombine();
5644 }
5645 
5646 /* ----------------------- Subtract ----------------------- */
5647 
5648 static
ConvolveToSubtract(x,y)5649 int ConvolveToSubtract(x, y)
5650    int x, y;
5651 {
5652    XColor fg_xcolor, bg_xcolor, xcolor;
5653    int inside_flag=INSIDE_NONE, pixel;
5654    long red=0L, green=0L, blue=0L;
5655 
5656    if (PointInRect(x, y, &gFgCombineBBox)) inside_flag |= INSIDE_FG;
5657    if (PointInRect(x, y, &gBgCombineBBox)) inside_flag |= INSIDE_BG;
5658 
5659    if (gConvExtraInfo.fp != NULL) {
5660       FILE *fp=gConvExtraInfo.fp;
5661       XColor **xcolors=NULL;
5662       int r=0, g=0, b=0, image_x=0, image_y=0;
5663       unsigned char buf[3];
5664 
5665       switch (inside_flag) {
5666       case 0x0:
5667          buf[0] = (unsigned char)gConvExtraInfo.my_bg_xcolor.red;
5668          buf[1] = (unsigned char)gConvExtraInfo.my_bg_xcolor.green;
5669          buf[2] = (unsigned char)gConvExtraInfo.my_bg_xcolor.blue;
5670          break;
5671       case 0x1:
5672          /* INSIDE_FG */
5673          xcolors = gConvExtraInfo.xcolors;
5674          image_x = x-gFgCombineBBox.ltx;
5675          image_y = y-gFgCombineBBox.lty;
5676          buf[0] = (unsigned char)xcolors[image_y][image_x].red;
5677          buf[1] = (unsigned char)xcolors[image_y][image_x].green;
5678          buf[2] = (unsigned char)xcolors[image_y][image_x].blue;
5679          break;
5680       case 0x2:
5681          /* INSIDE_BG */
5682          xcolors = gConvExtraInfo.bg_xcolors;
5683          image_x = x-gBgCombineBBox.ltx;
5684          image_y = y-gBgCombineBBox.lty;
5685          buf[0] = (unsigned char)xcolors[image_y][image_x].red;
5686          buf[1] = (unsigned char)xcolors[image_y][image_x].green;
5687          buf[2] = (unsigned char)xcolors[image_y][image_x].blue;
5688          break;
5689       case 0x3:
5690          /* INSIDE_BG | INSIDE_FG */
5691          xcolors = gConvExtraInfo.xcolors;
5692          image_x = x-gFgCombineBBox.ltx;
5693          image_y = y-gFgCombineBBox.lty;
5694          memcpy(&fg_xcolor, &xcolors[image_y][image_x], sizeof(XColor));
5695          xcolors = gConvExtraInfo.bg_xcolors;
5696          image_x = x-gBgCombineBBox.ltx;
5697          image_y = y-gBgCombineBBox.lty;
5698          memcpy(&bg_xcolor, &xcolors[image_y][image_x], sizeof(XColor));
5699          r = ((int)fg_xcolor.red) - ((int)bg_xcolor.red);
5700          g = ((int)fg_xcolor.green) - ((int)bg_xcolor.green);
5701          b = ((int)fg_xcolor.blue) - ((int)bg_xcolor.blue);
5702          if (r < 0) r = 0;
5703          if (g < 0) g = 0;
5704          if (b < 0) b = 0;
5705          if (r > 255) r = 255;
5706          if (g > 255) g = 255;
5707          if (b > 255) b = 255;
5708          buf[0] = (unsigned char)r;
5709          buf[1] = (unsigned char)g;
5710          buf[2] = (unsigned char)b;
5711          break;
5712       }
5713       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
5714 
5715       return TRUE;
5716    } else {
5717       switch (inside_flag) {
5718       case 0x0:
5719          if (myFileBgColorStr == NULL) {
5720             return GetOrAllocHistogramIndex(&myBgColor);
5721          } else {
5722             return GetOrAllocHistogramIndex(&myFileBgColor);
5723          }
5724       case 0x1:
5725          /* INSIDE_FG */
5726          pixel = XGetPixel(gpFgImage, x-gFgCombineBBox.ltx,
5727                y-gFgCombineBBox.lty);
5728          memcpy(&fg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5729                sizeof(XColor));
5730          red = (long)fg_xcolor.red;
5731          green = (long)fg_xcolor.green;
5732          blue = (long)fg_xcolor.blue;
5733          break;
5734       case 0x2:
5735          /* INSIDE_BG */
5736          pixel = XGetPixel(gpBgImage, x-gBgCombineBBox.ltx,
5737                y-gBgCombineBBox.lty);
5738          memcpy(&bg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5739                sizeof(XColor));
5740          red = (long)bg_xcolor.red;
5741          green = (long)bg_xcolor.green;
5742          blue = (long)bg_xcolor.blue;
5743          break;
5744       case 0x3:
5745          /* INSIDE_BG | INSIDE_FG */
5746          pixel = XGetPixel(gpFgImage, x-gFgCombineBBox.ltx,
5747                y-gFgCombineBBox.lty);
5748          memcpy(&fg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5749                sizeof(XColor));
5750          pixel = XGetPixel(gpBgImage, x-gBgCombineBBox.ltx,
5751                y-gBgCombineBBox.lty);
5752          memcpy(&bg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5753                sizeof(XColor));
5754          red = ((long)fg_xcolor.red) - ((long)bg_xcolor.red);
5755          green = ((long)fg_xcolor.green) - ((long)bg_xcolor.green);
5756          blue = ((long)fg_xcolor.blue) - ((long)bg_xcolor.blue);
5757          break;
5758       }
5759       memset(&xcolor, 0, sizeof(XColor));
5760       xcolor.red = (red > 0L ?
5761             (red > 0x0000ffff ? 0x0ffff : (unsigned int)(red&0xffff)) : 0);
5762       xcolor.green = (green > 0L ?
5763             (green > 0x0000ffff ? 0x0ffff : (unsigned int)(green&0xffff)) : 0);
5764       xcolor.blue = (blue > 0L ?
5765             (blue > 0x0000ffff ? 0x0ffff : (unsigned int)(blue&0xffff)) : 0);
5766       return GetOrAllocHistogramIndex(&xcolor);
5767    }
5768 }
5769 
Subtract()5770 void Subtract()
5771 {
5772    char szBuf[MAXSTRING+1];
5773    int num_objs=0;
5774 
5775    strcpy(szBuf, GetImageProcName(CMDID_SUBTRACT));
5776    gpFgObj = gpBgObj = gpAlphaObj = NULL;
5777    if (curChoice == NOTHING && numObjSelected == 2) {
5778       struct SelRec *sel_ptr;
5779       struct ObjRec *obj_ptr;
5780       int ok=TRUE;
5781 
5782       for (obj_ptr=topObj; ok && obj_ptr != NULL; obj_ptr=obj_ptr->next) {
5783          for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
5784             if (obj_ptr == sel_ptr->obj) {
5785                if (obj_ptr->type == OBJ_XPM) {
5786                   struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
5787                   int image_w=obj_ptr->obbox.rbx-obj_ptr->obbox.ltx;
5788                   int image_h=obj_ptr->obbox.rby-obj_ptr->obbox.lty;
5789 
5790                   if (obj_ptr->ctm != NULL || xpm_ptr->image_w != image_w ||
5791                         xpm_ptr->image_h != image_h) {
5792                      char szBuf1[MAXSTRING+1];
5793 
5794                      strcpy(szBuf1,
5795                            GetImageProcName(CMDID_REGENERATEIMAGE));
5796                      sprintf(gszMsgBox,
5797                            TgLoadString(STID_IMAGE_PROC_CANT_USE_XFORMED),
5798                            szBuf, szBuf1);
5799                      MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5800                      return;
5801                   }
5802                   switch (num_objs++) {
5803                   case 0: gpFgObj = obj_ptr; break;
5804                   case 1: gpBgObj = obj_ptr; break;
5805                   }
5806                } else {
5807                   ok = FALSE;
5808                   break;
5809                }
5810             }
5811          }
5812       }
5813       if (!ok) num_objs = 0;
5814    }
5815    if (num_objs != 2) {
5816       sprintf(gszMsgBox, TgLoadString(STID_SEL_2_XPM_FOR_IMAGEPROC_CMD), szBuf);
5817       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5818       return;
5819    }
5820    if (PrepareForAlphaCombine()) {
5821       gnCombining = TRUE;
5822       gpConvolveFunc = (NLFN*)ConvolveToSubtract;
5823       gpConvolveCmdID = CMDID_SUBTRACT;
5824       gnConvolving = TRUE;
5825       DoImageProc(NULL);
5826       gnConvolving = FALSE;
5827       gpConvolveFunc = NULL;
5828       gpConvolveCmdID = (-1);
5829       gnCombining = FALSE;
5830    }
5831    CleanUpAlphaCombine();
5832 }
5833 
5834 /* ----------------------- XorColors ----------------------- */
5835 
5836 static
ConvolveToXorColors(x,y)5837 int ConvolveToXorColors(x, y)
5838    int x, y;
5839 {
5840    XColor fg_xcolor, bg_xcolor, xcolor;
5841    int inside_flag=INSIDE_NONE, pixel=0;
5842 
5843    memset(&xcolor, 0, sizeof(XColor));
5844 
5845    if (PointInRect(x, y, &gFgCombineBBox)) inside_flag |= INSIDE_FG;
5846    if (PointInRect(x, y, &gBgCombineBBox)) inside_flag |= INSIDE_BG;
5847 
5848    if (gConvExtraInfo.fp != NULL) {
5849       FILE *fp=gConvExtraInfo.fp;
5850       XColor **xcolors=NULL;
5851       int image_x=0, image_y=0;
5852       unsigned int r=0, g=0, b=0;
5853       unsigned char buf[3];
5854 
5855       switch (inside_flag) {
5856       case 0x0:
5857          buf[0] = (unsigned char)gConvExtraInfo.my_bg_xcolor.red;
5858          buf[1] = (unsigned char)gConvExtraInfo.my_bg_xcolor.green;
5859          buf[2] = (unsigned char)gConvExtraInfo.my_bg_xcolor.blue;
5860          break;
5861       case 0x1:
5862          /* INSIDE_FG */
5863          xcolors = gConvExtraInfo.xcolors;
5864          image_x = x-gFgCombineBBox.ltx;
5865          image_y = y-gFgCombineBBox.lty;
5866          buf[0] = (unsigned char)xcolors[image_y][image_x].red;
5867          buf[1] = (unsigned char)xcolors[image_y][image_x].green;
5868          buf[2] = (unsigned char)xcolors[image_y][image_x].blue;
5869          break;
5870       case 0x2:
5871          /* INSIDE_BG */
5872          xcolors = gConvExtraInfo.bg_xcolors;
5873          image_x = x-gBgCombineBBox.ltx;
5874          image_y = y-gBgCombineBBox.lty;
5875          buf[0] = (unsigned char)xcolors[image_y][image_x].red;
5876          buf[1] = (unsigned char)xcolors[image_y][image_x].green;
5877          buf[2] = (unsigned char)xcolors[image_y][image_x].blue;
5878          break;
5879       case 0x3:
5880          /* INSIDE_BG | INSIDE_FG */
5881          xcolors = gConvExtraInfo.xcolors;
5882          image_x = x-gFgCombineBBox.ltx;
5883          image_y = y-gFgCombineBBox.lty;
5884          memcpy(&fg_xcolor, &xcolors[image_y][image_x], sizeof(XColor));
5885          xcolors = gConvExtraInfo.bg_xcolors;
5886          image_x = x-gBgCombineBBox.ltx;
5887          image_y = y-gBgCombineBBox.lty;
5888          memcpy(&bg_xcolor, &xcolors[image_y][image_x], sizeof(XColor));
5889          r = (unsigned int)((fg_xcolor.red) ^ (bg_xcolor.red));
5890          g = (unsigned int)((fg_xcolor.green) ^ (bg_xcolor.green));
5891          b = (unsigned int)((fg_xcolor.blue) ^ (bg_xcolor.blue));
5892          if (r < 0) r = 0;
5893          if (g < 0) g = 0;
5894          if (b < 0) b = 0;
5895          if (r > 255) r = 255;
5896          if (g > 255) g = 255;
5897          if (b > 255) b = 255;
5898          buf[0] = (unsigned char)r;
5899          buf[1] = (unsigned char)g;
5900          buf[2] = (unsigned char)b;
5901          break;
5902       }
5903       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
5904 
5905       return TRUE;
5906    } else {
5907       switch (inside_flag) {
5908       case 0x0:
5909          if (myFileBgColorStr == NULL) {
5910             return GetOrAllocHistogramIndex(&myBgColor);
5911          } else {
5912             return GetOrAllocHistogramIndex(&myFileBgColor);
5913          }
5914       case 0x1:
5915          /* INSIDE_FG */
5916          pixel = XGetPixel(gpFgImage, x-gFgCombineBBox.ltx,
5917                y-gFgCombineBBox.lty);
5918          memcpy(&fg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5919                sizeof(XColor));
5920          xcolor.red = fg_xcolor.red;
5921          xcolor.green = fg_xcolor.green;
5922          xcolor.blue = fg_xcolor.blue;
5923          break;
5924       case 0x2:
5925          /* INSIDE_BG */
5926          pixel = XGetPixel(gpBgImage, x-gBgCombineBBox.ltx,
5927                y-gBgCombineBBox.lty);
5928          memcpy(&bg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5929                sizeof(XColor));
5930          xcolor.red = bg_xcolor.red;
5931          xcolor.green = bg_xcolor.green;
5932          xcolor.blue = bg_xcolor.blue;
5933          break;
5934       case 0x3:
5935          /* INSIDE_BG | INSIDE_FG */
5936          pixel = XGetPixel(gpFgImage, x-gFgCombineBBox.ltx,
5937                y-gFgCombineBBox.lty);
5938          memcpy(&fg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5939                sizeof(XColor));
5940          pixel = XGetPixel(gpBgImage, x-gBgCombineBBox.ltx,
5941                y-gBgCombineBBox.lty);
5942          memcpy(&bg_xcolor, &tgifColors[GetIndexOfPixel(pixel)],
5943                sizeof(XColor));
5944          xcolor.red = (fg_xcolor.red) ^ (bg_xcolor.red);
5945          xcolor.green = (fg_xcolor.green) ^ (bg_xcolor.green);
5946          xcolor.blue = (fg_xcolor.blue) ^ (bg_xcolor.blue);
5947          break;
5948       }
5949       return GetOrAllocHistogramIndex(&xcolor);
5950    }
5951 }
5952 
XorColors()5953 void XorColors()
5954 {
5955    char szBuf[MAXSTRING+1];
5956    int num_objs=0;
5957 
5958    strcpy(szBuf, GetImageProcName(CMDID_XORCOLORS));
5959    gpFgObj = gpBgObj = gpAlphaObj = NULL;
5960    if (curChoice == NOTHING && numObjSelected == 2) {
5961       struct SelRec *sel_ptr;
5962       struct ObjRec *obj_ptr;
5963       int ok=TRUE;
5964 
5965       for (obj_ptr=topObj; ok && obj_ptr != NULL; obj_ptr=obj_ptr->next) {
5966          for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
5967             if (obj_ptr == sel_ptr->obj) {
5968                if (obj_ptr->type == OBJ_XPM) {
5969                   struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
5970                   int image_w=obj_ptr->obbox.rbx-obj_ptr->obbox.ltx;
5971                   int image_h=obj_ptr->obbox.rby-obj_ptr->obbox.lty;
5972 
5973                   if (obj_ptr->ctm != NULL || xpm_ptr->image_w != image_w ||
5974                         xpm_ptr->image_h != image_h) {
5975                      char szBuf1[MAXSTRING+1];
5976 
5977                      strcpy(szBuf1,
5978                            GetImageProcName(CMDID_REGENERATEIMAGE));
5979                      sprintf(gszMsgBox,
5980                            TgLoadString(STID_IMAGE_PROC_CANT_USE_XFORMED),
5981                            szBuf, szBuf1);
5982                      MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5983                      return;
5984                   }
5985                   switch (num_objs++) {
5986                   case 0: gpFgObj = obj_ptr; break;
5987                   case 1: gpBgObj = obj_ptr; break;
5988                   }
5989                } else {
5990                   ok = FALSE;
5991                   break;
5992                }
5993             }
5994          }
5995       }
5996       if (!ok) num_objs = 0;
5997    }
5998    if (num_objs != 2) {
5999       sprintf(gszMsgBox, TgLoadString(STID_SEL_2_XPM_FOR_IMAGEPROC_CMD), szBuf);
6000       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6001       return;
6002    }
6003    if (PrepareForAlphaCombine()) {
6004       gnCombining = TRUE;
6005       gpConvolveFunc = (NLFN*)ConvolveToXorColors;
6006       gpConvolveCmdID = CMDID_XORCOLORS;
6007       gnConvolving = TRUE;
6008       DoImageProc(NULL);
6009       gnConvolving = FALSE;
6010       gpConvolveFunc = NULL;
6011       gpConvolveCmdID = (-1);
6012       gnCombining = FALSE;
6013    }
6014    CleanUpAlphaCombine();
6015 }
6016 
6017 /* ======================= Image Warping ======================= */
6018 
6019 /* ----------------------- VectorWarp ----------------------- */
6020 
6021 typedef struct RevWarpXformRec {
6022    /*
6023     * x = X + x_coef ( X + w - f(x)W ) ( Y + h - g(y)H )
6024     * y = Y + y_coef ( X + w - f(x)W ) ( Y + h - g(y)H )
6025     *
6026     *                  [ 00 | 10 ]
6027     * where f(x)g(y) = [ ---+--- ]
6028     *                  [ 01 | 11 ]
6029     *
6030     * X and Y are in the warped domain and x and y are in the original domain
6031     *
6032     * x_coef = -dw / ( ( w + dw - f(x)W ) ( h + dh - g(y)H ) )
6033     * y_coef = -dh / ( ( w + dw - f(x)W ) ( h + dh - g(y)H ) )
6034     *
6035     * Nevertheless, this structure is not used!
6036     */
6037    double x_coef, y_coef;
6038 } RevWarpXform;
6039 
6040 static IntPoint gaTrapMapPt[5];
6041 static TrapMapSeg gaTrapMapSeg[4];
6042 static int gaTrapMapLeaf[4];
6043 
6044 static TrapMapSpec gLeftTrapMapSpec[] = {
6045    /*
6046     * In-order specification of the trapezoidal map for Cx < Tx.
6047     */
6048    { TRAP_NODE_PT, TRAP_PT_C },
6049    { TRAP_NODE_SEG, TRAP_SEG_L },
6050    { TRAP_NODE_LEAF, TRAP_LEAF_LT },
6051    { TRAP_NODE_LEAF, TRAP_LEAF_LB },
6052    { TRAP_NODE_PT, TRAP_PT_T },
6053    { TRAP_NODE_SEG, TRAP_SEG_T },
6054    { TRAP_NODE_LEAF, TRAP_LEAF_LT },
6055    { TRAP_NODE_SEG, TRAP_SEG_R },
6056    { TRAP_NODE_LEAF, TRAP_LEAF_RT },
6057    { TRAP_NODE_SEG, TRAP_SEG_B },
6058    { TRAP_NODE_LEAF, TRAP_LEAF_RB },
6059    { TRAP_NODE_LEAF, TRAP_LEAF_LB },
6060    { TRAP_NODE_SEG, TRAP_SEG_R },
6061    { TRAP_NODE_LEAF, TRAP_LEAF_RT },
6062    { TRAP_NODE_LEAF, TRAP_LEAF_RB },
6063    { -1, -1 }
6064 };
6065 
6066 static TrapMapSpec gRightTrapMapSpec[] = {
6067    /*
6068     * In-order specification of the trapezoidal map for Cx > Tx.
6069     */
6070    { TRAP_NODE_PT, TRAP_PT_T },
6071    { TRAP_NODE_SEG, TRAP_SEG_L },
6072    { TRAP_NODE_LEAF, TRAP_LEAF_LT },
6073    { TRAP_NODE_LEAF, TRAP_LEAF_LB },
6074    { TRAP_NODE_PT, TRAP_PT_C },
6075    { TRAP_NODE_SEG, TRAP_SEG_T },
6076    { TRAP_NODE_LEAF, TRAP_LEAF_RT },
6077    { TRAP_NODE_SEG, TRAP_SEG_L },
6078    { TRAP_NODE_LEAF, TRAP_LEAF_LT },
6079    { TRAP_NODE_SEG, TRAP_SEG_B },
6080    { TRAP_NODE_LEAF, TRAP_LEAF_LB },
6081    { TRAP_NODE_LEAF, TRAP_LEAF_RB },
6082    { TRAP_NODE_SEG, TRAP_SEG_R },
6083    { TRAP_NODE_LEAF, TRAP_LEAF_RT },
6084    { TRAP_NODE_LEAF, TRAP_LEAF_RB },
6085    { -1, -1 }
6086 };
6087 
6088 static TrapMapSpec gCenterTrapMapSpec[] = {
6089    /*
6090     * In-order specification of the trapezoidal map for Cx == Tx.
6091     */
6092    { TRAP_NODE_PT, TRAP_PT_T },
6093    { TRAP_NODE_SEG, TRAP_SEG_L },
6094    { TRAP_NODE_LEAF, TRAP_LEAF_LT },
6095    { TRAP_NODE_LEAF, TRAP_LEAF_LB },
6096    { TRAP_NODE_SEG, TRAP_SEG_R },
6097    { TRAP_NODE_LEAF, TRAP_LEAF_RT },
6098    { TRAP_NODE_LEAF, TRAP_LEAF_RB },
6099    { -1, -1 }
6100 };
6101 
6102 static TrapMap *gpTrapMap=NULL;
6103 static TrapMapSpec *gpTrapMapSpec=NULL;
6104 static int **gpSegYIntersects=NULL;
6105 static int **gpExtraYIntersects=NULL;
6106 
6107 static
CleanTrapMapNode(pTrapMapNode)6108 void CleanTrapMapNode(pTrapMapNode)
6109    TrapMap *pTrapMapNode;
6110 {
6111    switch (pTrapMapNode->type) {
6112    case TRAP_NODE_LEAF: break;
6113    case TRAP_NODE_PT:
6114       if (pTrapMapNode->detail.pt.left != NULL) {
6115          CleanTrapMapNode(pTrapMapNode->detail.pt.left);
6116       }
6117       if (pTrapMapNode->detail.pt.right != NULL) {
6118          CleanTrapMapNode(pTrapMapNode->detail.pt.right);
6119       }
6120       break;
6121    case TRAP_NODE_SEG:
6122       if (pTrapMapNode->detail.seg.above != NULL) {
6123          CleanTrapMapNode(pTrapMapNode->detail.seg.above);
6124       }
6125       if (pTrapMapNode->detail.seg.below != NULL) {
6126          CleanTrapMapNode(pTrapMapNode->detail.seg.below);
6127       }
6128       break;
6129    }
6130    free(pTrapMapNode);
6131 }
6132 
6133 static
CleanTrapMap()6134 void CleanTrapMap()
6135 {
6136    if (gpTrapMap != NULL) {
6137       CleanTrapMapNode(gpTrapMap);
6138    }
6139    gpTrapMap = NULL;
6140    if (gpSegYIntersects != NULL) {
6141       int i;
6142 
6143       for (i=0; i < 4; i++) {
6144          if (gpSegYIntersects[i] != NULL) {
6145             free(gpSegYIntersects[i]);
6146          }
6147       }
6148       free(gpSegYIntersects);
6149       gpSegYIntersects = NULL;
6150    }
6151    if (gpExtraYIntersects != NULL) {
6152       int i;
6153 
6154       for (i=0; i < 4; i++) {
6155          if (gpExtraYIntersects[i] != NULL) {
6156             free(gpExtraYIntersects[i]);
6157          }
6158       }
6159       free(gpExtraYIntersects);
6160       gpExtraYIntersects = NULL;
6161    }
6162 }
6163 
6164 static
SetTrapMapLeaf(pTrapMapSpecRoot,pnIndex,pTrapMapNode,nWhich)6165 int SetTrapMapLeaf(pTrapMapSpecRoot, pnIndex, pTrapMapNode, nWhich)
6166    TrapMapSpec *pTrapMapSpecRoot;
6167    int *pnIndex;
6168    TrapMap *pTrapMapNode;
6169    int nWhich;
6170 {
6171    pTrapMapNode->type = TRAP_NODE_LEAF;
6172    pTrapMapNode->detail.leaf.which = nWhich;
6173    pTrapMapNode->detail.leaf.data = (&gaTrapMapLeaf[nWhich]);
6174    return TRUE;
6175 }
6176 
6177 static
SetTrapMapPt(pTrapMapSpecRoot,pnIndex,pTrapMapNode,nWhich)6178 int SetTrapMapPt(pTrapMapSpecRoot, pnIndex, pTrapMapNode, nWhich)
6179    TrapMapSpec *pTrapMapSpecRoot;
6180    int *pnIndex;
6181    TrapMap *pTrapMapNode;
6182    int nWhich;
6183 {
6184    pTrapMapNode->type = TRAP_NODE_PT;
6185    pTrapMapNode->detail.pt.which = nWhich;
6186    pTrapMapNode->detail.pt.data = (&gaTrapMapPt[nWhich]);
6187    pTrapMapNode->detail.pt.left = (TrapMap*)malloc(sizeof(TrapMap));
6188    pTrapMapNode->detail.pt.right = (TrapMap*)malloc(sizeof(TrapMap));
6189    if (pTrapMapNode->detail.pt.left == NULL ||
6190          pTrapMapNode->detail.pt.right == NULL) {
6191       FailAllocMessage();
6192    }
6193    memset(pTrapMapNode->detail.pt.left, 0, sizeof(TrapMap));
6194    memset(pTrapMapNode->detail.pt.right, 0, sizeof(TrapMap));
6195    (*pnIndex)++;
6196    if (!BuildTrapMapNode(pTrapMapSpecRoot, pnIndex,
6197          pTrapMapNode->detail.pt.left)) {
6198       return FALSE;
6199    }
6200    (*pnIndex)++;
6201    if (!BuildTrapMapNode(pTrapMapSpecRoot, pnIndex,
6202          pTrapMapNode->detail.pt.right)) {
6203       return FALSE;
6204    }
6205    return TRUE;
6206 }
6207 
6208 static
SetTrapMapSeg(pTrapMapSpecRoot,pnIndex,pTrapMapNode,nWhich)6209 int SetTrapMapSeg(pTrapMapSpecRoot, pnIndex, pTrapMapNode, nWhich)
6210    TrapMapSpec *pTrapMapSpecRoot;
6211    int *pnIndex;
6212    TrapMap *pTrapMapNode;
6213    int nWhich;
6214 {
6215    pTrapMapNode->type = TRAP_NODE_SEG;
6216    pTrapMapNode->detail.seg.which = nWhich;
6217    pTrapMapNode->detail.seg.data = (&gaTrapMapSeg[nWhich]);
6218    pTrapMapNode->detail.seg.above = (TrapMap*)malloc(sizeof(TrapMap));
6219    pTrapMapNode->detail.seg.below = (TrapMap*)malloc(sizeof(TrapMap));
6220    if (pTrapMapNode->detail.seg.above == NULL ||
6221          pTrapMapNode->detail.seg.below == NULL) {
6222       FailAllocMessage();
6223    }
6224    memset(pTrapMapNode->detail.seg.above, 0, sizeof(TrapMap));
6225    memset(pTrapMapNode->detail.seg.below, 0, sizeof(TrapMap));
6226    (*pnIndex)++;
6227    if (!BuildTrapMapNode(pTrapMapSpecRoot, pnIndex,
6228          pTrapMapNode->detail.seg.above)) {
6229       return FALSE;
6230    }
6231    (*pnIndex)++;
6232    if (!BuildTrapMapNode(pTrapMapSpecRoot, pnIndex,
6233          pTrapMapNode->detail.seg.below)) {
6234       return FALSE;
6235    }
6236    return TRUE;
6237 }
6238 
BuildTrapMapNode(pTrapMapSpecRoot,pnIndex,pTrapMapNode)6239 int BuildTrapMapNode(pTrapMapSpecRoot, pnIndex, pTrapMapNode)
6240    TrapMapSpec *pTrapMapSpecRoot;
6241    int *pnIndex;
6242    TrapMap *pTrapMapNode;
6243 {
6244    TrapMapSpec *pTrapMapSpec=(&pTrapMapSpecRoot[*pnIndex]);
6245 
6246    switch (pTrapMapSpec->type) {
6247    case TRAP_NODE_LEAF:
6248       return SetTrapMapLeaf(pTrapMapSpecRoot, pnIndex, pTrapMapNode,
6249             pTrapMapSpec->which);
6250    case TRAP_NODE_PT:
6251       return SetTrapMapPt(pTrapMapSpecRoot, pnIndex, pTrapMapNode,
6252             pTrapMapSpec->which);
6253    case TRAP_NODE_SEG:
6254       return SetTrapMapSeg(pTrapMapSpecRoot, pnIndex, pTrapMapNode,
6255             pTrapMapSpec->which);
6256    default: break;
6257    }
6258    return TRUE;
6259 }
6260 
6261 static
BuildTrapMap(pTrapMapSpec)6262 int BuildTrapMap(pTrapMapSpec)
6263    TrapMapSpec *pTrapMapSpec;
6264 {
6265    int nIndex=0;
6266 
6267    gpTrapMap = (TrapMap*)malloc(sizeof(TrapMap));
6268    if (gpTrapMap == NULL) FailAllocMessage();
6269    memset(gpTrapMap, 0, sizeof(TrapMap));
6270    if (BuildTrapMapNode(pTrapMapSpec, &nIndex, gpTrapMap)) {
6271       return TRUE;
6272    }
6273    CleanTrapMap();
6274    return FALSE;
6275 }
6276 
6277 /*
6278  * L, T, R, B, C are points.  Left subtree are to the left of the point.
6279  * sL, sT, sR, sB are line segments.  Left subtree are above the line segment.
6280  * lt, rt, lb, rb are the quardrants (leaves of the tree).
6281  *
6282  * If Cx == Tx && Cy == Ly: Do not warp.
6283  *
6284  * If Cx < Tx:
6285  *
6286  *      +-------T-------+                C
6287  *      |               |               / \
6288  *      |     sT        |              /   \
6289  *      |               |             /     \
6290  *      |    C   sR     |            sL      T
6291  *      L sL            R           /  \    / \
6292  *      |               |          lt  lb  /   \
6293  *      |     sB        |                 /     \
6294  *      |               |                sT     sR
6295  *      |               |               /  \   /  \
6296  *      +-------B-------+              lt  sR rt  rb
6297  *                                        /  \
6298  *                                       rt   sB
6299  *                                           /  \
6300  *                                          rb  lb
6301  * If Cx > Tx:
6302  *
6303  *      +-------T-------+                T
6304  *      |               |               / \
6305  *      |        sT     |              /   \
6306  *      |               |             /     \
6307  *      |     sL   C    |            sL      C
6308  *      L            sR R           /  \    / \
6309  *      |               |          lt  lb  /   \
6310  *      |        sB     |                 /     \
6311  *      |               |                sT     sR
6312  *      |               |               /  \   /  \
6313  *      +-------B-------+              rt  sL rt  rb
6314  *                                        /  \
6315  *                                       lt   sB
6316  *                                           /  \
6317  *                                          lb  rb
6318  * If Cx == Tx:
6319  *
6320  *      +-------T-------+                T
6321  *      |               |               / \
6322  *      |       sT      |              /   \
6323  *      |               |             /     \
6324  *      |  sL   C   sR  |            sL      sR
6325  *      L               R           /  \    /  \
6326  *      |               |          lt  lb  rt  rb
6327  *      |       sB      |
6328  *      |               |
6329  *      |               |
6330  *      +-------B-------+
6331  */
6332 
6333 static int **gnVectorWarpImageSrcIndex=NULL;
6334 static int **gnVectorWarpImageDestIndex=NULL;
6335 
6336 static
ConvolveToVectorWarp(x,y)6337 int ConvolveToVectorWarp(x, y)
6338    int x, y;
6339 {
6340    if (gConvExtraInfo.fp != NULL) {
6341       XColor **xcolors=gConvExtraInfo.xcolors;
6342       FILE *fp=gConvExtraInfo.fp;
6343       unsigned char buf[3];
6344 
6345       buf[0] = (unsigned char)xcolors[y][x].red;
6346       buf[1] = (unsigned char)xcolors[y][x].green;
6347       buf[2] = (unsigned char)xcolors[y][x].blue;
6348 
6349       if ((int)fwrite(buf, sizeof(char), 3, fp) <= 0) writeFileFailed = TRUE;
6350 
6351       return TRUE;
6352    } else {
6353       return GetOrAllocHistogramIndex(
6354             &tgifColors[gnVectorWarpImageDestIndex[y][x]]);
6355    }
6356 }
6357 
6358 static
CleanUpVectorWarpData()6359 void CleanUpVectorWarpData()
6360 {
6361    int i, image_h=topSel->obj->detail.xpm->image_h;
6362 
6363    if (gnVectorWarpImageSrcIndex != NULL) {
6364       for (i=0; i < image_h; i++) {
6365          if (gnVectorWarpImageSrcIndex[i] != NULL) {
6366             free(gnVectorWarpImageSrcIndex[i]);
6367          }
6368       }
6369       free(gnVectorWarpImageSrcIndex);
6370       gnVectorWarpImageSrcIndex = NULL;
6371    }
6372    if (gnVectorWarpImageDestIndex != NULL) {
6373       for (i=0; i < image_h; i++) {
6374          if (gnVectorWarpImageDestIndex[i] != NULL) {
6375             free(gnVectorWarpImageDestIndex[i]);
6376          }
6377       }
6378       free(gnVectorWarpImageDestIndex);
6379       gnVectorWarpImageDestIndex = NULL;
6380    }
6381 }
6382 
6383 static
SetTrapMapSegValue(nSegIndex,nPtIndex,w)6384 void SetTrapMapSegValue(nSegIndex, nPtIndex, w)
6385    int nSegIndex, nPtIndex, w;
6386 {
6387    int i;
6388    double dx=(double)0.0, dy, m, b;
6389 
6390    dy = (double)(gaTrapMapPt[TRAP_PT_C].y - gaTrapMapPt[nPtIndex].y);
6391    if (gaTrapMapPt[TRAP_PT_C].x == gaTrapMapPt[nPtIndex].x) {
6392       dx = (double)0.0;
6393       m = (double)0.0;
6394    } else {
6395       dx = (double)(gaTrapMapPt[TRAP_PT_C].x - gaTrapMapPt[nPtIndex].x);
6396       m = ((double)dy) / ((double)dx);
6397    }
6398    b = ((double)(gaTrapMapPt[TRAP_PT_C].y)) -
6399          m * ((double)(gaTrapMapPt[TRAP_PT_C].x));
6400    gaTrapMapSeg[nSegIndex].m = m;
6401    gaTrapMapSeg[nSegIndex].b = b;
6402    for (i=0; i < w; i++) {
6403       double y=m*((double)(i))+b;
6404 
6405       gpSegYIntersects[nSegIndex][i] = round(y);
6406    }
6407 }
6408 
6409 static
SetTrapMapExtraValue(nLeafIndex,end_x,end_y,w)6410 void SetTrapMapExtraValue(nLeafIndex, end_x, end_y, w)
6411    int nLeafIndex, end_x, end_y, w;
6412 {
6413    int i;
6414    double dx=(double)0.0, dy, m, b;
6415 
6416    dy = (double)(gaTrapMapPt[TRAP_PT_C].y - end_y);
6417    if (gaTrapMapPt[TRAP_PT_C].x == end_x) {
6418       dx = (double)0.0;
6419       m = (double)0.0;
6420    } else {
6421       dx = (double)(gaTrapMapPt[TRAP_PT_C].x - end_x);
6422       m = ((double)dy) / ((double)dx);
6423    }
6424    b = ((double)(gaTrapMapPt[TRAP_PT_C].y)) -
6425          m * ((double)(gaTrapMapPt[TRAP_PT_C].x));
6426    for (i=0; i < w; i++) {
6427       double y=m*((double)(i))+b;
6428 
6429       gpExtraYIntersects[nLeafIndex][i] = round(y);
6430    }
6431 }
6432 
6433 static
CheckVectorWarpCoords(pFromPt,pToPt,dx,dy,image_w,image_h,pBBox)6434 int CheckVectorWarpCoords(pFromPt, pToPt, dx, dy, image_w, image_h, pBBox)
6435    IntPoint *pFromPt, *pToPt;
6436    int dx, dy, image_w, image_h;
6437    struct BBRec *pBBox;
6438 {
6439    IntPoint to_pt;
6440    double ddx, ddy, d;
6441    int i, diam, ltx, lty, rbx, rby, w, h;
6442    char szBuf[MAXSTRING+1];
6443 
6444    strcpy(szBuf, GetImageProcName(CMDID_VECTORWARP));
6445    if (pFromPt->x < topSel->obj->obbox.ltx ||
6446          pFromPt->y < topSel->obj->obbox.lty ||
6447          pFromPt->x >= topSel->obj->obbox.rbx ||
6448          pFromPt->y >= topSel->obj->obbox.rby) {
6449       MsgBox(TgLoadString(STID_PICK_PT_WITHIN_IMAGE_BOUND), TOOL_NAME, INFO_MB);
6450       return FALSE;
6451    } else if (image_w <= 4 || image_h <= 4) {
6452       sprintf(gszMsgBox, TgLoadString(STID_IMG_TOO_SMALL_IMAGEPROC_CMD), szBuf);
6453       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6454       return FALSE;
6455    }
6456    to_pt.x = pFromPt->x+dx;
6457    to_pt.y = pFromPt->y+dy;
6458    if (to_pt.x < topSel->obj->obbox.ltx ||
6459          to_pt.y < topSel->obj->obbox.lty ||
6460          to_pt.x >= topSel->obj->obbox.rbx ||
6461          to_pt.y >= topSel->obj->obbox.rby) {
6462       if (dx == 0) {
6463          if (dy > 0) {
6464             to_pt.y = topSel->obj->obbox.rby-1;
6465          } else {
6466             to_pt.y = topSel->obj->obbox.lty;
6467          }
6468       } else {
6469          double m, b, x_intersect, y_intersect;
6470          int intersect=CORNER_TOP;
6471 
6472          m = ((double)dy) / ((double)dx);
6473          b = ((double)(pFromPt->y)) - m * ((double)(pFromPt->x));
6474          if (dx < 0) {
6475             y_intersect = m * ((double)(topSel->obj->obbox.ltx)) + b;
6476             if (y_intersect < topSel->obj->obbox.lty) {
6477                intersect = CORNER_TOP;
6478             } else if (y_intersect > topSel->obj->obbox.rby-1) {
6479                intersect = CORNER_BOTTOM;
6480             } else {
6481                intersect = CORNER_LEFT;
6482             }
6483          } else {
6484             y_intersect = m * ((double)(topSel->obj->obbox.rbx-1)) + b;
6485             if (y_intersect < topSel->obj->obbox.lty) {
6486                intersect = CORNER_TOP;
6487             } else if (y_intersect > topSel->obj->obbox.rby-1) {
6488                intersect = CORNER_BOTTOM;
6489             } else {
6490                intersect = CORNER_RIGHT;
6491             }
6492          }
6493          switch (intersect) {
6494          case CORNER_TOP:
6495             /* intersects the obbox at the top */
6496             to_pt.y = topSel->obj->obbox.lty;
6497             x_intersect = (((double)(to_pt.y)) - b) / m;
6498             to_pt.x = round(x_intersect);
6499             if (to_pt.x < topSel->obj->obbox.ltx) {
6500                to_pt.x = topSel->obj->obbox.ltx;
6501             } else if (to_pt.x >= topSel->obj->obbox.rbx) {
6502                to_pt.x = topSel->obj->obbox.rbx-1;
6503             }
6504             break;
6505          case CORNER_BOTTOM:
6506             /* intersects the obbox at the bottom */
6507             to_pt.y = topSel->obj->obbox.rby;
6508             x_intersect = (((double)(to_pt.y)) - b) / m;
6509             to_pt.x = round(x_intersect);
6510             if (to_pt.x < topSel->obj->obbox.ltx) {
6511                to_pt.x = topSel->obj->obbox.ltx;
6512             } else if (to_pt.x >= topSel->obj->obbox.rbx) {
6513                to_pt.x = topSel->obj->obbox.rbx-1;
6514             }
6515             break;
6516          case CORNER_LEFT:
6517             /* intersects the obbox at the left */
6518             to_pt.x = topSel->obj->obbox.lty;
6519             y_intersect = (m * ((double)(to_pt.x))) + b;
6520             to_pt.y = round(y_intersect);
6521             if (to_pt.y < topSel->obj->obbox.lty) {
6522                to_pt.y = topSel->obj->obbox.lty;
6523             } else if (to_pt.y >= topSel->obj->obbox.rby) {
6524                to_pt.y = topSel->obj->obbox.rby-1;
6525             }
6526             break;
6527          case CORNER_RIGHT:
6528             /* intersects the obbox at the right */
6529             to_pt.x = topSel->obj->obbox.rby;
6530             y_intersect = (m * ((double)(to_pt.x))) + b;
6531             to_pt.y = round(y_intersect);
6532             if (to_pt.y < topSel->obj->obbox.lty) {
6533                to_pt.y = topSel->obj->obbox.lty;
6534             } else if (to_pt.y >= topSel->obj->obbox.rby) {
6535                to_pt.y = topSel->obj->obbox.rby-1;
6536             }
6537             break;
6538          }
6539       }
6540    }
6541    ddx = (double)(to_pt.x - pFromPt->x);
6542    ddy = (double)(to_pt.y - pFromPt->y);
6543    d = ((double)gfVectorWarpSoftness)*(double)sqrt((double)(ddx*ddx+ddy*ddy));
6544    diam = round(d);
6545    ltx = pFromPt->x-diam - topSel->obj->obbox.ltx;
6546    lty = pFromPt->y-diam - topSel->obj->obbox.lty;
6547    rbx = pFromPt->x+diam - topSel->obj->obbox.ltx;
6548    rby = pFromPt->y+diam - topSel->obj->obbox.lty;
6549    if (ltx < 0) ltx = 0;
6550    if (lty < 0) lty = 0;
6551    if (rbx >= image_w) rbx = image_w;
6552    if (rby >= image_h) rby = image_h;
6553    pBBox->ltx = ltx; pBBox->lty = lty;
6554    pBBox->rbx = rbx; pBBox->rby = rby;
6555    pFromPt->x -= topSel->obj->obbox.ltx;
6556    pFromPt->y -= topSel->obj->obbox.lty;
6557    pToPt->x = to_pt.x - topSel->obj->obbox.ltx;
6558    pToPt->y = to_pt.y - topSel->obj->obbox.lty;
6559    if (pToPt->x == pFromPt->x) {
6560       if (pToPt->y == pFromPt->y) {
6561          Msg(TgLoadString(STID_NO_WARPING));
6562          return FALSE;
6563       }
6564       gpTrapMapSpec = gCenterTrapMapSpec;
6565    } else if (pToPt->x < pFromPt->x) {
6566       gpTrapMapSpec = gLeftTrapMapSpec;
6567    } else {
6568       gpTrapMapSpec = gRightTrapMapSpec;
6569    }
6570    w = pBBox->rbx-pBBox->ltx;
6571    h = pBBox->rby-pBBox->lty;
6572 
6573    gpSegYIntersects = (int**)malloc(4*sizeof(int*));
6574    gpExtraYIntersects = (int**)malloc(4*sizeof(int*));
6575    if (gpSegYIntersects == NULL || gpExtraYIntersects == NULL) {
6576       FailAllocMessage();
6577       if (gpSegYIntersects != NULL) free(gpSegYIntersects);
6578       if (gpExtraYIntersects != NULL) free(gpExtraYIntersects);
6579       return FALSE;
6580    }
6581    for (i=0; i < 4; i++) {
6582       gpSegYIntersects[i] = (int*)malloc(w*sizeof(int));
6583       gpExtraYIntersects[i] = (int*)malloc(w*sizeof(int));
6584       if (gpSegYIntersects[i] == NULL || gpExtraYIntersects[i] == NULL) {
6585          FailAllocMessage();
6586          return FALSE;
6587       }
6588       memset(gpSegYIntersects[i], 0, w*sizeof(int));
6589       memset(gpExtraYIntersects[i], 0, w*sizeof(int));
6590    }
6591 
6592    /* sets L, T, R, B, and C points */
6593    gaTrapMapPt[TRAP_PT_L].x = pBBox->ltx - pBBox->ltx;
6594    gaTrapMapPt[TRAP_PT_L].y = pFromPt->y - pBBox->lty;
6595    gaTrapMapPt[TRAP_PT_T].x = pFromPt->x - pBBox->ltx;
6596    gaTrapMapPt[TRAP_PT_T].y = pBBox->lty - pBBox->lty;
6597    gaTrapMapPt[TRAP_PT_R].x = pBBox->rbx - pBBox->ltx;
6598    gaTrapMapPt[TRAP_PT_R].y = pFromPt->y - pBBox->lty;
6599    gaTrapMapPt[TRAP_PT_B].x = pFromPt->x - pBBox->ltx;
6600    gaTrapMapPt[TRAP_PT_B].y = pBBox->rby - pBBox->lty;
6601    gaTrapMapPt[TRAP_PT_C].x = pToPt->x - pBBox->ltx;
6602    gaTrapMapPt[TRAP_PT_C].y = pToPt->y - pBBox->lty;
6603 
6604    /* sets sL, sT, sR, and sB line segments */
6605    SetTrapMapSegValue(TRAP_SEG_L, TRAP_PT_L, w);
6606    SetTrapMapSegValue(TRAP_SEG_T, TRAP_PT_T, w);
6607    SetTrapMapSegValue(TRAP_SEG_R, TRAP_PT_R, w);
6608    SetTrapMapSegValue(TRAP_SEG_B, TRAP_PT_B, w);
6609 
6610    /* sets sL, sT, sR, and sB line segments */
6611    SetTrapMapExtraValue(TRAP_LEAF_LT, 0, 0, w);
6612    SetTrapMapExtraValue(TRAP_LEAF_RT, w, 0, w);
6613    SetTrapMapExtraValue(TRAP_LEAF_LB, 0, h, w);
6614    SetTrapMapExtraValue(TRAP_LEAF_RB, w, h, w);
6615 
6616    /* sets lt, rt, lb, rb quardrants */
6617    gaTrapMapLeaf[TRAP_LEAF_LT] = TRAP_LEAF_LT;
6618    gaTrapMapLeaf[TRAP_LEAF_RT] = TRAP_LEAF_RT;
6619    gaTrapMapLeaf[TRAP_LEAF_LB] = TRAP_LEAF_LB;
6620    gaTrapMapLeaf[TRAP_LEAF_RB] = TRAP_LEAF_RB;
6621 
6622    return TRUE;
6623 }
6624 
6625 static
GetQuadrant(pTrapMap,x,y)6626 int GetQuadrant(pTrapMap, x, y)
6627    TrapMap *pTrapMap;
6628    int x, y;
6629    /* returns one of the TRAP_LEAF_*'s */
6630 {
6631    switch (pTrapMap->type) {
6632    case TRAP_NODE_LEAF:
6633       return pTrapMap->detail.leaf.which;
6634    case TRAP_NODE_PT:
6635       if (x < pTrapMap->detail.pt.data->x) {
6636          return GetQuadrant(pTrapMap->detail.pt.left, x, y);
6637       }
6638       return GetQuadrant(pTrapMap->detail.pt.right, x, y);
6639    case TRAP_NODE_SEG:
6640       if (y < gpSegYIntersects[pTrapMap->detail.seg.which][x]) {
6641          return GetQuadrant(pTrapMap->detail.seg.above, x, y);
6642       }
6643       return GetQuadrant(pTrapMap->detail.seg.below, x, y);
6644    }
6645    return (-1);
6646 }
6647 
6648 /*
6649  *     w                 w      dw
6650  *  +-----+           +-----+------+
6651  *  |  o  |  warp     |      \
6652  * h|(x,y)| ======>  h|   o   \
6653  *  |     |           | (X,Y)  \      X is also known as x_hat
6654  *  +-----+           +__       \     Y is also known as y_hat
6655  *                    |  \__     \
6656  *                  dh|     \__   \
6657  *                    +        \___\
6658  *
6659  * w -> x_off, w+dw -> d_new_w, W -> d_bbox_w, x_hat -> new_x
6660  * h -> y_off, h+dh -> d_new_h, H -> d_bbox_h, y_hat -> new_y
6661  */
6662 
6663 static
ComputeVectorWarpData(pFromPt,dx,dy)6664 int ComputeVectorWarpData(pFromPt, dx, dy)
6665    IntPoint *pFromPt;
6666    int dx, dy;
6667 {
6668    struct XPmRec *xpm_ptr=topSel->obj->detail.xpm;
6669    int i, image_w=xpm_ptr->image_w, image_h=xpm_ptr->image_h;
6670    int bbox_w, bbox_h, x_off, y_off, ppm6=FALSE;
6671    double d_bbox_w, d_bbox_h, d_x_off, d_y_off, d_new_w, d_new_h, ddw, ddh;
6672    Pixmap pixmap=xpm_ptr->pixmap;
6673    XImage *image=NULL;
6674    struct BBRec bbox; /* only points within bbox need to be warped */
6675    IntPoint to_pt;
6676    XColor **xcolors=NULL, **bg_xcolors=NULL;
6677    ProgressInfo pi;
6678 
6679    if (!CheckVectorWarpCoords(pFromPt, &to_pt, dx, dy, image_w, image_h,
6680          &bbox)) {
6681       return FALSE;
6682    }
6683    ppm6 = DoPpm6(xpm_ptr);
6684    /*
6685     * w -> x_off, w+dw -> new_w, W -> bbox_w, x_hat -> new_x
6686     * h -> y_off, h+dh -> new_h, H -> bbox_h, y_hat -> new_y
6687     */
6688    bbox_w = bbox.rbx - bbox.ltx; d_bbox_w = (double)bbox_w;
6689    bbox_h = bbox.rby - bbox.lty; d_bbox_h = (double)bbox_h;
6690    x_off = pFromPt->x - bbox.ltx; d_x_off = (double)x_off;
6691    y_off = pFromPt->y - bbox.lty; d_y_off = (double)y_off;
6692    d_new_w = (double)(to_pt.x - bbox.ltx);
6693    d_new_h = (double)(to_pt.y - bbox.lty);
6694    ddw = d_new_w - ((double)x_off);
6695    ddh = d_new_h - ((double)y_off);
6696 
6697    if (!BuildTrapMap(gpTrapMapSpec)) {
6698       return FALSE;
6699    }
6700    image = XGetImage(mainDisplay, pixmap, 0, 0, image_w, image_h, AllPlanes,
6701          ZPixmap);
6702    if (image == NULL) {
6703       FailAllocMessage();
6704       return FALSE;
6705    }
6706    if (ppm6) {
6707       if (!InitTrueColorInfo(image, &gTrueColorInfo, image_w)) {
6708          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
6709                image_w, image_h);
6710          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6711          XDestroyImage(image);
6712          return FALSE;
6713       }
6714       if (!SetConvExtraInfo(NULL, image_w, image_h, image, NULL)) {
6715          XDestroyImage(image);
6716          return FALSE;
6717       }
6718       xcolors = gConvExtraInfo.xcolors;
6719       bg_xcolors = gConvExtraInfo.bg_xcolors;
6720    } else {
6721       if (!CreatePixelToIndexMapping()) {
6722          XDestroyImage(image);
6723          return FALSE;
6724       }
6725       gnVectorWarpImageSrcIndex = (int**)malloc(image_h*sizeof(int*));
6726       gnVectorWarpImageDestIndex = (int**)malloc(image_h*sizeof(int*));
6727       if (gnVectorWarpImageSrcIndex == NULL ||
6728             gnVectorWarpImageDestIndex == NULL) {
6729          if (gnVectorWarpImageSrcIndex != NULL) free(gnVectorWarpImageSrcIndex);
6730          if (gnVectorWarpImageDestIndex != NULL) {
6731             free(gnVectorWarpImageDestIndex);
6732          }
6733          FailAllocMessage();
6734          XDestroyImage(image);
6735          CleanUpIndexOfPixel();
6736 
6737          return FALSE;
6738       }
6739       memset(gnVectorWarpImageSrcIndex, 0, image_h*sizeof(int*));
6740       memset(gnVectorWarpImageDestIndex, 0, image_h*sizeof(int*));
6741       BeginProgress(&pi, image_h);
6742       for (i=0; i < image_h; i++) {
6743          int j;
6744 
6745          UpdateProgress(&pi, i);
6746          gnVectorWarpImageSrcIndex[i] = (int*)malloc(image_w*sizeof(int));
6747          gnVectorWarpImageDestIndex[i] = (int*)malloc(image_w*sizeof(int));
6748          if (gnVectorWarpImageSrcIndex[i] == NULL ||
6749                gnVectorWarpImageDestIndex[i] == NULL) {
6750             FailAllocMessage();
6751             for (j=0; j < i; j++) {
6752                if (gnVectorWarpImageSrcIndex[j] != NULL) {
6753                   free(gnVectorWarpImageSrcIndex[j]);
6754                }
6755             }
6756             free(gnVectorWarpImageSrcIndex);
6757             gnVectorWarpImageSrcIndex = NULL;
6758             for (j=0; j < i; j++) {
6759                if (gnVectorWarpImageDestIndex[j] != NULL) {
6760                   free(gnVectorWarpImageDestIndex[j]);
6761                }
6762             }
6763             free(gnVectorWarpImageDestIndex);
6764             gnVectorWarpImageDestIndex = NULL;
6765             XDestroyImage(image);
6766             CleanUpIndexOfPixel();
6767 
6768             return FALSE;
6769          }
6770          for (j=0; j < image_w; j++) {
6771             gnVectorWarpImageSrcIndex[i][j] = gnVectorWarpImageDestIndex[i][j] =
6772                   GetIndexOfPixel(XGetPixel(image,j,i));
6773          }
6774       }
6775    }
6776    BeginProgress(&pi, image_h);
6777    srand(0);
6778    for (i=0; i < image_h; i++) {
6779       int j;
6780 
6781       UpdateProgress(&pi, i);
6782       if (i < bbox.lty || i >= bbox.rby) continue;
6783       for (j=0; j < image_w; j++) {
6784          int quadrant, new_x, new_y;
6785 
6786          if (j < bbox.ltx || j >= bbox.rbx) continue;
6787 
6788          new_x = j - bbox.ltx;
6789          new_y = i - bbox.lty;
6790          quadrant = GetQuadrant(gpTrapMap, new_x, new_y);
6791          if (quadrant != -1) {
6792             double d_new_x=(double)new_x, d_new_y=(double)new_y;
6793             double ddx=(double)0.0, ddy=(double)0.0, frac;
6794             int x, y, above;
6795 
6796             above = (new_y < gpExtraYIntersects[quadrant][new_x]);
6797 
6798             switch (quadrant) {
6799             case TRAP_LEAF_LT:
6800                if (above) {
6801                   ddx = d_new_x - ddw*d_new_y/d_new_h;
6802                   ddy = d_y_off*d_new_y/d_new_h;
6803                } else {
6804                   ddx = d_x_off*d_new_x/d_new_w;
6805                   ddy = d_new_y - ddh*d_new_x/d_new_w;
6806                }
6807                break;
6808             case TRAP_LEAF_RT:
6809                if (above) {
6810                   ddx = d_new_x - ddw*d_new_y/d_new_h;
6811                   ddy = d_y_off*d_new_y/d_new_h;
6812                } else {
6813                   frac = (d_new_x-d_bbox_w)/(d_new_w-d_bbox_w);
6814                   ddx = d_bbox_w - (d_bbox_w-d_x_off)*frac;
6815                   ddy = d_new_y - ddh*frac;
6816                }
6817                break;
6818             case TRAP_LEAF_LB:
6819                if (above) {
6820                   ddx = d_x_off*d_new_x/d_new_w;
6821                   ddy = d_new_y - ddh*d_new_x/d_new_w;
6822                } else {
6823                   frac = (d_new_y-d_bbox_h)/(d_new_h-d_bbox_h);
6824                   ddx = d_new_x - ddw*frac;
6825                   ddy = d_bbox_h - (d_bbox_h-d_y_off)*frac;
6826                }
6827                break;
6828             case TRAP_LEAF_RB:
6829                if (above) {
6830                   frac = (d_new_x-d_bbox_w)/(d_new_w-d_bbox_w);
6831                   ddx = d_bbox_w - (d_bbox_w-d_x_off)*frac;
6832                   ddy = d_new_y - ddh*frac;
6833                } else {
6834                   frac = (d_new_y-d_bbox_h)/(d_new_h-d_bbox_h);
6835                   ddx = d_new_x - ddw*frac;
6836                   ddy = d_bbox_h - (d_bbox_h-d_y_off)*frac;
6837                }
6838                break;
6839             }
6840             x = round(ddx);
6841             y = round(ddy);
6842             x += bbox.ltx;
6843             y += bbox.lty;
6844             if (x < bbox.ltx) x = bbox.ltx;
6845             if (x >= bbox.rbx) x = bbox.rbx-1;
6846             if (y < bbox.lty) y = bbox.lty;
6847             if (y >= bbox.rby) y = bbox.rby-1;
6848             if (ppm6) {
6849                xcolors[i][j] = bg_xcolors[y][x];
6850             } else {
6851                gnVectorWarpImageDestIndex[i][j] =
6852                      gnVectorWarpImageSrcIndex[y][x];
6853             }
6854          }
6855       }
6856    }
6857    XDestroyImage(image);
6858    CleanUpIndexOfPixel();
6859 
6860    return TRUE;
6861 }
6862 
6863 static
SpecifyLineSeg(pnFromAbsX,pnFromAbsY,pnToAbsX,pnToAbsY)6864 int SpecifyLineSeg(pnFromAbsX, pnFromAbsY, pnToAbsX, pnToAbsY)
6865    int *pnFromAbsX, *pnFromAbsY, *pnToAbsX, *pnToAbsY;
6866 {
6867    int started=FALSE, done=FALSE, orig_x=0, orig_y=0, grid_x=0, grid_y=0;
6868    int root_x, root_y, saved_snap_on=snapOn;
6869    char buf[80];
6870    unsigned int status;
6871    Window root_win, child_win;
6872 
6873    snapOn = FALSE;
6874    *buf = '\0';
6875    SetMouseStatus(TgLoadString(STID_START_LINE_SEG_DOTS),
6876          TgLoadCachedString(CSTID_FINISH), TgLoadCachedString(CSTID_FINISH));
6877    XGrabPointer(mainDisplay, drawWindow, FALSE,
6878          PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
6879          GrabModeAsync, GrabModeAsync, None, handCursor, CurrentTime);
6880    XQueryPointer(mainDisplay, drawWindow, &root_win, &child_win,
6881          &root_x, &root_y, &orig_x, &orig_y, &status);
6882    GridXY(orig_x, orig_y, &grid_x, &grid_y);
6883    MarkRulers(grid_x, grid_y);
6884    orig_x = grid_x;
6885    orig_y = grid_y;
6886    while (!done) {
6887       XEvent input;
6888 
6889       XNextEvent(mainDisplay, &input);
6890       switch (input.type) {
6891       case Expose: ExposeEventHandler(&input, TRUE); break;
6892       case VisibilityNotify: ExposeEventHandler(&input, TRUE); break;
6893       case ButtonPress:
6894          if (input.xbutton.button == Button1) {
6895             MarkRulers(grid_x, grid_y);
6896             SetMouseStatus(TgLoadString(STID_ENDT_LINE_SEG_DOTS), "", "");
6897             GridXY(input.xbutton.x, input.xbutton.y, &grid_x, &grid_y);
6898             orig_x = grid_x;
6899             orig_y = grid_y;
6900             XDrawLine(mainDisplay, drawWindow, revDefaultGC, orig_x, orig_y,
6901                   grid_x, grid_y);
6902             MarkRulers(grid_x, grid_y);
6903             started = TRUE;
6904          } else {
6905             XUngrabPointer(mainDisplay, CurrentTime);
6906             XSync(mainDisplay, False);
6907             done = TRUE;
6908             started = FALSE;
6909          }
6910          break;
6911       case MotionNotify:
6912          MarkRulers(grid_x, grid_y);
6913          if (started) {
6914             XDrawLine(mainDisplay, drawWindow, revDefaultGC, orig_x, orig_y,
6915                   grid_x, grid_y);
6916          }
6917          GridXY(input.xmotion.x, input.xmotion.y, &grid_x, &grid_y);
6918          if (started) {
6919             XDrawLine(mainDisplay, drawWindow, revDefaultGC, orig_x, orig_y,
6920                   grid_x, grid_y);
6921          }
6922          MarkRulers(grid_x, grid_y);
6923          break;
6924       case ButtonRelease:
6925          XUngrabPointer(mainDisplay, CurrentTime);
6926          XSync(mainDisplay, False);
6927          done = TRUE;
6928          MarkRulers(grid_x, grid_y);
6929          XDrawLine(mainDisplay, drawWindow, revDefaultGC, orig_x, orig_y,
6930                grid_x, grid_y);
6931       }
6932    }
6933    snapOn = saved_snap_on;
6934    if (started && !(orig_x == grid_x && orig_y == grid_y)) {
6935       *pnFromAbsX = ABS_X(orig_x);
6936       *pnFromAbsY = ABS_Y(orig_y);
6937       *pnToAbsX = ABS_X(grid_x);
6938       *pnToAbsY = ABS_Y(grid_y);
6939       return TRUE;
6940    }
6941    return FALSE;
6942 }
6943 
VectorWarp()6944 void VectorWarp()
6945 {
6946    int ok=TRUE;
6947    IntPoint from_pt, to_pt;
6948 
6949    if (!CheckSelectionForImageProc(CMDID_VECTORWARP)) {
6950       return;
6951    }
6952    memset(&from_pt, 0, sizeof(IntPoint));
6953    memset(&to_pt, 0, sizeof(IntPoint));
6954    SaveStatusStrings();
6955    while (ok) {
6956       if (somethingHighLighted) HighLightReverse();
6957       ok = SpecifyLineSeg(&from_pt.x, &from_pt.y, &to_pt.x, &to_pt.y);
6958       if (!somethingHighLighted) HighLightForward();
6959 
6960       if (!ok) break;
6961 
6962       gpConvolveCmdID = CMDID_VECTORWARP;
6963       if (!ComputeVectorWarpData(&from_pt, to_pt.x-from_pt.x,
6964             to_pt.y-from_pt.y)) {
6965          CleanTrapMap();
6966          gpConvolveCmdID = (-1);
6967          break;
6968       }
6969       CleanTrapMap();
6970 
6971       gpConvolveFunc = (NLFN*)ConvolveToVectorWarp;
6972       gpConvolveCmdID = CMDID_VECTORWARP;
6973       gnConvolving = TRUE;
6974       DoImageProc(NULL);
6975       gnConvolving = FALSE;
6976       gpConvolveFunc = NULL;
6977       gpConvolveCmdID = (-1);
6978 
6979       CleanUpVectorWarpData();
6980    }
6981    RestoreStatusStrings();
6982 }
6983 
6984 /* ======================= Non-Image Processing ======================= */
6985 
6986 /* ----------------------- RunBggen ----------------------- */
6987 
6988 static
GetBggenImageSize(p_image_w,p_image_h)6989 int GetBggenImageSize(p_image_w, p_image_h)
6990    int *p_image_w, *p_image_h;
6991 {
6992    char spec[MAXSTRING+1], *dup_spec=NULL, *part1=NULL, *part2=NULL;
6993    int ok=TRUE;
6994 
6995    *spec = '\0';
6996    if (Dialog(TgLoadString(STID_ENTER_IMAGE_SIZE_IN_PIX_RC),
6997          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), spec)==INVALID) {
6998       return FALSE;
6999    }
7000    UtilTrimBlanks(spec);
7001    if (*spec == '\0') return FALSE;
7002    if ((dup_spec=UtilStrDup(spec)) == NULL) {
7003       FailAllocMessage();
7004       return FALSE;
7005    }
7006    if ((part1=strtok(dup_spec, " ,xX[]")) != NULL &&
7007          (part2=strtok(NULL, " ,xX[]")) != NULL) {
7008       *p_image_w = atoi(part1);
7009       *p_image_h = atoi(part2);
7010       if ((*p_image_w) <= 0 || (*p_image_h) <= 0) {
7011          ok = FALSE;
7012       }
7013    } else {
7014       ok = FALSE;
7015    }
7016    if (!ok) {
7017       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), dup_spec);
7018       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7019    }
7020    free(dup_spec);
7021    return ok;
7022 }
7023 
7024 static
BggenGenerateXpm(image_w,image_h,sz_spec,sz_path,path_buf_sz)7025 int BggenGenerateXpm(image_w, image_h, sz_spec, sz_path, path_buf_sz)
7026    int image_w, image_h, path_buf_sz;
7027    char *sz_spec, *sz_path;
7028 {
7029    char *psz_cmd, sz_geom[MAXSTRING+1];
7030    FILE *pFile, *pPipe;
7031    int bytes_read;
7032 
7033    if (MkTempFile(sz_path, path_buf_sz, tmpDir, TOOL_NAME) == NULL) {
7034       return FALSE;
7035    }
7036    sprintf(sz_geom, "%1dx%1d", image_w, image_h);
7037    if (fullTrueColorMode && HasZlibSupport()) {
7038       sprintf(gszMsgBox, bggenToPpm6Cmd, sz_spec, sz_geom);
7039    } else {
7040       sprintf(gszMsgBox, bggenToXpmCmd, sz_spec, sz_geom);
7041    }
7042    if ((psz_cmd=UtilStrDup(gszMsgBox)) == NULL) {
7043       return FailAllocMessage();
7044    }
7045    if (!FindProgramInPath(psz_cmd, NULL, FALSE)) {
7046       free(psz_cmd);
7047       return FALSE;
7048    }
7049    if ((pFile=fopen(sz_path,"w")) == NULL) {
7050       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_WRITING),
7051             sz_path);
7052       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7053       free(psz_cmd);
7054       return FALSE;
7055    }
7056    Msg("Executing:");
7057    sprintf(gszMsgBox, "    %s", psz_cmd);
7058    Msg(gszMsgBox);
7059    sprintf(gszMsgBox, "Executing '%s'...", psz_cmd);
7060    SetStringStatus(gszMsgBox);
7061    XSync(mainDisplay, False);
7062    if ((pPipe=(FILE*)popen(psz_cmd,"r")) == NULL) {
7063       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_EXECUTE_CMD), psz_cmd);
7064       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7065       free(psz_cmd);
7066       fclose(pFile);
7067       unlink(sz_path);
7068       return FALSE;
7069    }
7070    writeFileFailed = FALSE;
7071    while ((bytes_read=fread(gszMsgBox, sizeof(char), sizeof(gszMsgBox),
7072          pPipe)) > 0) {
7073       if ((int)fwrite(gszMsgBox, sizeof(char), bytes_read, pFile) <= 0) {
7074          writeFileFailed = TRUE;
7075          break;
7076       }
7077    }
7078    pclose(pPipe);
7079    SetStringStatus(TgLoadCachedString(CSTID_DOTS_DONE));
7080    free(psz_cmd);
7081    fclose(pFile);
7082    if (writeFileFailed) {
7083       FailToWriteFileMessage(sz_path);
7084       unlink(sz_path);
7085       return FALSE;
7086    }
7087    return TRUE;
7088 }
7089 
RunBggen()7090 void RunBggen()
7091 {
7092    int image_w=0, image_h=0, w, h, short_name, rc, use_obj_pos=FALSE;
7093    int ltx=0, lty=0;
7094    int ncolors=0, chars_per_pixel=0, first_pixel_is_bg=0, *pixels=NULL;
7095    char szSpec[MAXSTRING+1], szPath[MAXPATHLENGTH+1], *rest;
7096    char *color_char=NULL, **color_str=NULL, *xpm_data=NULL;
7097    struct ObjRec *obj_ptr;
7098    Pixmap pixmap=None, bitmap=None;
7099    XImage *image=NULL, *bitmap_image=NULL;
7100 
7101    if (curChoice != NOTHING || topSel == NULL) {
7102       MakeQuiescent();
7103       if (!GetBggenImageSize(&image_w, &image_h)) {
7104          return;
7105       }
7106    } else if (!CheckSelectionForImageProc(CMDID_RUNBGGEN)) {
7107       return;
7108    } else {
7109       obj_ptr = topSel->obj;
7110       ltx = obj_ptr->obbox.ltx;
7111       lty = obj_ptr->obbox.lty;
7112       image_w = obj_ptr->obbox.rbx - ltx;
7113       image_h = obj_ptr->obbox.rby - lty;
7114       use_obj_pos = TRUE;
7115       HighLightReverse();
7116    }
7117    *szSpec = '\0';
7118    Dialog(TgLoadString(STID_ENTER_CMD_OP_FOR_BGGEN),
7119          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
7120    UtilTrimBlanks(szSpec);
7121    if (*szSpec == '\0') {
7122       if (use_obj_pos) HighLightForward();
7123       return;
7124    }
7125    SaveStatusStrings();
7126    if (!BggenGenerateXpm(image_w, image_h, szSpec, szPath, sizeof(szPath))) {
7127       RestoreStatusStrings();
7128       if (use_obj_pos) HighLightForward();
7129       return;
7130    }
7131    RestoreStatusStrings();
7132 
7133    if (fullTrueColorMode && HasZlibSupport()) {
7134       char deflated_fname[MAXPATHLENGTH+1];
7135       struct XPmRec *xpm_ptr=NULL;
7136 
7137       ResetPngHeaderInfo(&gPngHeaderInfo);
7138       obj_ptr = CreatePpmTrueObjFromFile(szPath);
7139       if (obj_ptr != NULL &&
7140             MkTempFile(deflated_fname, sizeof(deflated_fname), tmpDir,
7141             TOOL_NAME) != NULL &&
7142             DeflateFile(szPath, deflated_fname)) {
7143          /* good */
7144       } else {
7145          FreeObj(obj_ptr);
7146 
7147          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_GIVEN_PPM), szPath);
7148          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7149          unlink(szPath);
7150          return;
7151       }
7152       xpm_ptr = obj_ptr->detail.xpm;
7153       xpm_ptr->real_type = PPM_TRUE;
7154       xpm_ptr->ppm_data_compress = PPM_DATA_DEFLATED;
7155       xpm_ptr->ppm_data = ReadFileIntoBuf(deflated_fname,
7156             &xpm_ptr->ppm_data_size);
7157       xpm_ptr->ppm_mask_data = NULL;
7158       xpm_ptr->ppm_mask_size = 0;
7159       unlink(deflated_fname);
7160    } else {
7161       SetWatchCursor(drawWindow);
7162       SetWatchCursor(mainWindow);
7163       rc = MyReadPixmapFile(szPath, &image_w, &image_h, &w, &h, &pixmap,
7164             &image, &bitmap, &bitmap_image, &ncolors, &chars_per_pixel,
7165             &first_pixel_is_bg, &color_char, &color_str, &pixels, &xpm_data);
7166       SetDefaultCursor(mainWindow);
7167       ShowCursor();
7168 
7169       if ((short_name=IsPrefix(bootDir, szPath, &rest))) ++rest;
7170       if (rc != BitmapSuccess) {
7171          if (use_obj_pos) HighLightForward();
7172          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_XPM_FILE),
7173                (short_name ? rest : szPath));
7174          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7175          unlink(szPath);
7176          return;
7177       }
7178       obj_ptr = CreateXPmObj(image_w, image_h, w, h, pixmap, image, bitmap,
7179             bitmap_image, ncolors, chars_per_pixel, first_pixel_is_bg,
7180             color_char, color_str, pixels, xpm_data);
7181    }
7182    unlink(szPath);
7183    AddObj(NULL, topObj, obj_ptr);
7184    if (use_obj_pos) {
7185       RemoveAllSel();
7186       MoveObj(obj_ptr, ltx-obj_ptr->obbox.ltx, lty-obj_ptr->obbox.lty);
7187       numRedrawBBox = 0;
7188       obj_ptr->tmp_parent = NULL;
7189       DrawObj(drawWindow, obj_ptr);
7190    } else {
7191       PlaceTopObj(obj_ptr, NULL, NULL);
7192    }
7193    SelectTopObj();
7194    RecordNewObjCmd();
7195    SetFileModified(TRUE);
7196    justDupped = FALSE;
7197    if (!PRTGIF && colorLayers && needToRedrawColorWindow) {
7198       RedrawColorWindow();
7199    }
7200    sprintf(gszMsgBox, TgLoadString(STID_NEW_XPM_WH_GENERATED),
7201          image_w, image_h);
7202    Msg(gszMsgBox);
7203 }
7204 
7205 /* ----------------------- CircularBggen ----------------------- */
7206 
7207 static
CircularBggenGenerateXpm(image_w,image_h,ncolors,sz_path,path_buf_sz)7208 int CircularBggenGenerateXpm(image_w, image_h, ncolors, sz_path, path_buf_sz)
7209    int image_w, image_h, ncolors, path_buf_sz;
7210    char *sz_path;
7211 {
7212    FILE *pFile=NULL;
7213    int i=0, cx=0, cy=0;
7214    float fval=(float)0, finc=65535.0/((float)(ncolors-1));
7215    double max_dist=(double)0;
7216    ProgressInfo pi;
7217 
7218    if (MkTempFile(sz_path, path_buf_sz, tmpDir, TOOL_NAME) == NULL) {
7219       return FALSE;
7220    }
7221    gpHistogram = (XColor*)malloc(ncolors*sizeof(XColor));
7222    if (gpHistogram == NULL) return FailAllocMessage();
7223    memset(gpHistogram, 0, ncolors*sizeof(XColor));
7224 
7225    for (i=0, fval=0.0; i < ncolors; i++, fval+=finc) {
7226       int ival;
7227 
7228       ival = round(fval);
7229       HISTOGRAMRED(i) = HISTOGRAMGREEN(i) = HISTOGRAMBLUE(i) =
7230             (unsigned int)ival;
7231    }
7232    i--;
7233    HISTOGRAMRED(i) = HISTOGRAMGREEN(i) = HISTOGRAMBLUE(i) = 65535;
7234 
7235    cx = (image_w>>1);
7236    cy = (image_h>>1);
7237    max_dist=(double)sqrt((double)(cx*cx+cy*cy));
7238 
7239    if (fullTrueColorMode && HasZlibSupport()) {
7240       if ((pFile=fopen(sz_path,"w")) == NULL) {
7241          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_WRITING),
7242                sz_path);
7243          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7244          free(gpHistogram);
7245          gpHistogram = NULL;
7246          return FALSE;
7247       }
7248       writeFileFailed = FALSE;
7249       if (fprintf(pFile, "P6\n%1d %1d\n255\n", image_w, image_h) == EOF) {
7250          writeFileFailed = TRUE;
7251       }
7252    } else {
7253       gnFinalImageIndex = (int**)malloc(image_h*sizeof(int*));
7254       if (gnFinalImageIndex == NULL) {
7255          free(gpHistogram);
7256          gpHistogram = NULL;
7257          return FailAllocMessage();
7258       }
7259       memset(gnFinalImageIndex, 0, image_h*sizeof(int*));
7260    }
7261    BeginProgress(&pi, image_h);
7262    for (i=0; i < image_h; i++) {
7263       int j, dy2=(i-cy)*(i-cy);
7264 
7265       UpdateProgress(&pi, i);
7266       if (fullTrueColorMode && HasZlibSupport()) {
7267          /* don't allocate */
7268       } else {
7269          if ((gnFinalImageIndex[i]=(int*)malloc(image_w*sizeof(int))) == NULL) {
7270             for (j=0; j < i; j++) free(gnFinalImageIndex[j]);
7271             free(gnFinalImageIndex);
7272             free(gpHistogram);
7273             gnFinalImageIndex = NULL;
7274             gpHistogram = NULL;
7275             return FailAllocMessage();
7276          }
7277       }
7278       for (j=0; j < image_w; j++) {
7279          int dx2=(j-cx)*(j-cx);
7280          double dist=(double)sqrt((double)(dx2+dy2));
7281          double dgray=((double)ncolors)*dist/max_dist+0.5;
7282          int index=round(dgray);
7283 
7284          if (index < 0) index = 0;
7285          if (index >= ncolors) index = ncolors-1;
7286          if (fullTrueColorMode && HasZlibSupport()) {
7287             unsigned int gray=0;
7288             unsigned char buf[3];
7289 
7290             gray = (unsigned int)HISTOGRAMRED(ncolors-1-index);
7291             gray >>= 8;
7292             buf[0] = buf[1] = buf[2] = (unsigned char)gray;
7293             if ((int)fwrite(buf, sizeof(char), 3, pFile) <= 0) {
7294                writeFileFailed = TRUE;
7295             }
7296          } else {
7297             gnFinalImageIndex[i][j] = ncolors-1-index;
7298          }
7299       }
7300    }
7301    gnTransparentIndex = (-1);
7302    if (fullTrueColorMode && HasZlibSupport()) {
7303       fclose(pFile);
7304    } else {
7305       if ((pFile=fopen(sz_path,"w")) == NULL) {
7306          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_WRITING),
7307                sz_path);
7308          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7309          for (i=0; i < image_h; i++) free(gnFinalImageIndex[i]);
7310          free(gnFinalImageIndex);
7311          free(gpHistogram);
7312          gnFinalImageIndex = NULL;
7313          gpHistogram = NULL;
7314          return FALSE;
7315       }
7316       gnImageW = image_w;
7317       gnImageH = image_h;
7318       gnHistogramEntries = ncolors;
7319       writeFileFailed = FALSE;
7320       DumpConvolution(pFile);
7321       fclose(pFile);
7322       for (i=0; i < image_h; i++) free(gnFinalImageIndex[i]);
7323       free(gnFinalImageIndex);
7324    }
7325    free(gpHistogram);
7326    gnFinalImageIndex = NULL;
7327    gpHistogram = NULL;
7328    if (writeFileFailed) {
7329       FailToWriteFileMessage(sz_path);
7330       unlink(sz_path);
7331       return FALSE;
7332    }
7333    return TRUE;
7334 }
7335 
CircularBggen()7336 void CircularBggen()
7337 {
7338    int image_w=0, image_h=0, w, h, short_name, rc, use_obj_pos=FALSE;
7339    int ltx=0, lty=0;
7340    int ncolors=0, chars_per_pixel=0, first_pixel_is_bg=0, *pixels=NULL;
7341    char szSpec[MAXSTRING+1], szPath[MAXPATHLENGTH+1], *rest;
7342    char *color_char=NULL, **color_str=NULL, *xpm_data=NULL;
7343    struct ObjRec *obj_ptr;
7344    Pixmap pixmap=None, bitmap=None;
7345    XImage *image=NULL, *bitmap_image=NULL;
7346 
7347    if (curChoice != NOTHING || topSel == NULL) {
7348       MakeQuiescent();
7349       if (!GetBggenImageSize(&image_w, &image_h)) {
7350          return;
7351       }
7352    } else if (!CheckSelectionForImageProc(CMDID_CIRCULARBGGEN)) {
7353       return;
7354    } else {
7355       obj_ptr = topSel->obj;
7356       ltx = obj_ptr->obbox.ltx;
7357       lty = obj_ptr->obbox.lty;
7358       image_w = obj_ptr->obbox.rbx - ltx;
7359       image_h = obj_ptr->obbox.rby - lty;
7360       use_obj_pos = TRUE;
7361       HighLightReverse();
7362    }
7363    *szSpec = '\0';
7364    Dialog(TgLoadString(STID_ENTER_GRAY_LEVELS_222),
7365          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
7366    UtilTrimBlanks(szSpec);
7367    if (*szSpec == '\0') {
7368       if (use_obj_pos) HighLightForward();
7369       return;
7370    }
7371    ncolors = atoi(szSpec);
7372    if (ncolors < 2 || ncolors > 222) {
7373       sprintf(gszMsgBox, TgLoadString(STID_INVALID_GIVEN_VALUE_ENTERED),
7374             szSpec);
7375       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7376       if (use_obj_pos) HighLightForward();
7377       return;
7378    }
7379    SaveStatusStrings();
7380    if (!CircularBggenGenerateXpm(image_w, image_h, ncolors, szPath,
7381          sizeof(szPath))) {
7382       RestoreStatusStrings();
7383       if (use_obj_pos) HighLightForward();
7384       return;
7385    }
7386    RestoreStatusStrings();
7387 
7388    if (fullTrueColorMode && HasZlibSupport()) {
7389       char deflated_fname[MAXPATHLENGTH+1];
7390       struct XPmRec *xpm_ptr=NULL;
7391 
7392       ResetPngHeaderInfo(&gPngHeaderInfo);
7393       obj_ptr = CreatePpmTrueObjFromFile(szPath);
7394       if (obj_ptr != NULL &&
7395             MkTempFile(deflated_fname, sizeof(deflated_fname), tmpDir,
7396             TOOL_NAME) != NULL &&
7397             DeflateFile(szPath, deflated_fname)) {
7398          /* good */
7399       } else {
7400          FreeObj(obj_ptr);
7401 
7402          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_GIVEN_PPM), szPath);
7403          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7404          unlink(szPath);
7405          return;
7406       }
7407       xpm_ptr = obj_ptr->detail.xpm;
7408       xpm_ptr->real_type = PPM_TRUE;
7409       xpm_ptr->ppm_data_compress = PPM_DATA_DEFLATED;
7410       xpm_ptr->ppm_data = ReadFileIntoBuf(deflated_fname,
7411             &xpm_ptr->ppm_data_size);
7412       xpm_ptr->ppm_mask_data = NULL;
7413       xpm_ptr->ppm_mask_size = 0;
7414       unlink(deflated_fname);
7415    } else {
7416       SetWatchCursor(drawWindow);
7417       SetWatchCursor(mainWindow);
7418       rc = MyReadPixmapFile(szPath, &image_w, &image_h, &w, &h, &pixmap,
7419             &image, &bitmap, &bitmap_image, &ncolors, &chars_per_pixel,
7420             &first_pixel_is_bg, &color_char, &color_str, &pixels, &xpm_data);
7421       SetDefaultCursor(mainWindow);
7422       ShowCursor();
7423 
7424       if ((short_name=IsPrefix(bootDir, szPath, &rest))) ++rest;
7425       if (rc != BitmapSuccess) {
7426          if (use_obj_pos) HighLightForward();
7427          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_XPM_FILE),
7428                (short_name ? rest : szPath));
7429          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7430          unlink(szPath);
7431          return;
7432       }
7433       obj_ptr = CreateXPmObj(image_w, image_h, w, h, pixmap, image, bitmap,
7434             bitmap_image, ncolors, chars_per_pixel, first_pixel_is_bg,
7435             color_char, color_str, pixels, xpm_data);
7436    }
7437    unlink(szPath);
7438    AddObj(NULL, topObj, obj_ptr);
7439    if (use_obj_pos) {
7440       RemoveAllSel();
7441       MoveObj(obj_ptr, ltx-obj_ptr->obbox.ltx, lty-obj_ptr->obbox.lty);
7442       numRedrawBBox = 0;
7443       obj_ptr->tmp_parent = NULL;
7444       DrawObj(drawWindow, obj_ptr);
7445    } else {
7446       PlaceTopObj(obj_ptr, NULL, NULL);
7447    }
7448    SelectTopObj();
7449    RecordNewObjCmd();
7450    SetFileModified(TRUE);
7451    justDupped = FALSE;
7452    if (!PRTGIF && colorLayers && needToRedrawColorWindow) {
7453       RedrawColorWindow();
7454    }
7455    sprintf(gszMsgBox, TgLoadString(STID_NEW_XPM_WH_GENERATED),
7456          image_w, image_h);
7457    Msg(gszMsgBox);
7458 }
7459 
7460 /* ----------------------- SimpleRectBggen ----------------------- */
7461 
7462 static
SimpleRectBggenGenerateXpm(image_w,image_h,pxcolor,sz_path,path_buf_sz)7463 int SimpleRectBggenGenerateXpm(image_w, image_h, pxcolor, sz_path, path_buf_sz)
7464    int image_w, image_h, path_buf_sz;
7465    XColor *pxcolor;
7466    char *sz_path;
7467 {
7468    FILE *pFile=NULL;
7469    int i=0;
7470    ProgressInfo pi;
7471 
7472    if (MkTempFile(sz_path, path_buf_sz, tmpDir, TOOL_NAME) == NULL) {
7473       return FALSE;
7474    }
7475    gpHistogram = (XColor*)malloc(sizeof(XColor));
7476    if (gpHistogram == NULL) return FailAllocMessage();
7477    memset(gpHistogram, 0, sizeof(XColor));
7478 
7479    HISTOGRAMRED(0) = pxcolor->red;
7480    HISTOGRAMGREEN(0) = pxcolor->green;
7481    HISTOGRAMBLUE(0) = pxcolor->blue;
7482 
7483    if (fullTrueColorMode && HasZlibSupport()) {
7484       if ((pFile=fopen(sz_path,"w")) == NULL) {
7485          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_WRITING),
7486                sz_path);
7487          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7488          free(gpHistogram);
7489          gpHistogram = NULL;
7490          return FALSE;
7491       }
7492       writeFileFailed = FALSE;
7493       if (fprintf(pFile, "P6\n%1d %1d\n255\n", image_w, image_h) == EOF) {
7494          writeFileFailed = TRUE;
7495       }
7496    } else {
7497       gnFinalImageIndex = (int**)malloc(image_h*sizeof(int*));
7498       if (gnFinalImageIndex == NULL) {
7499          free(gpHistogram);
7500          gpHistogram = NULL;
7501          return FailAllocMessage();
7502       }
7503       memset(gnFinalImageIndex, 0, image_h*sizeof(int*));
7504    }
7505    BeginProgress(&pi, image_h);
7506    for (i=0; i < image_h; i++) {
7507       int j;
7508 
7509       UpdateProgress(&pi, i);
7510       if (fullTrueColorMode && HasZlibSupport()) {
7511          unsigned char buf[3];
7512          unsigned int r=0, g=0, b=0;
7513 
7514          r = (unsigned int)(pxcolor->red);
7515          g = (unsigned int)(pxcolor->green);
7516          b = (unsigned int)(pxcolor->blue);
7517          buf[0] = (unsigned char)((r>>8)&0x0ff);
7518          buf[1] = (unsigned char)((g>>8)&0x0ff);
7519          buf[2] = (unsigned char)((b>>8)&0x0ff);
7520          for (j=0; j < image_w; j++) {
7521             if ((int)fwrite(buf, sizeof(char), 3, pFile) <= 0) {
7522                writeFileFailed = TRUE;
7523             }
7524          }
7525       } else {
7526          if ((gnFinalImageIndex[i]=(int*)malloc(image_w*sizeof(int))) == NULL) {
7527             for (j=0; j < i; j++) free(gnFinalImageIndex[j]);
7528             free(gnFinalImageIndex);
7529             free(gpHistogram);
7530             gnFinalImageIndex = NULL;
7531             gpHistogram = NULL;
7532             return FailAllocMessage();
7533          }
7534          memset(gnFinalImageIndex[i], 0, image_w*sizeof(int));
7535       }
7536    }
7537    gnTransparentIndex = (-1);
7538    if (fullTrueColorMode && HasZlibSupport()) {
7539       fclose(pFile);
7540    } else {
7541       if ((pFile=fopen(sz_path,"w")) == NULL) {
7542          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_WRITING),
7543                sz_path);
7544          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7545          for (i=0; i < image_h; i++) free(gnFinalImageIndex[i]);
7546          free(gnFinalImageIndex);
7547          free(gpHistogram);
7548          gnFinalImageIndex = NULL;
7549          gpHistogram = NULL;
7550          return FALSE;
7551       }
7552       gnImageW = image_w;
7553       gnImageH = image_h;
7554       gnHistogramEntries = 1;
7555       writeFileFailed = FALSE;
7556       DumpConvolution(pFile);
7557       fclose(pFile);
7558       for (i=0; i < image_h; i++) free(gnFinalImageIndex[i]);
7559       free(gnFinalImageIndex);
7560    }
7561    free(gpHistogram);
7562    gnFinalImageIndex = NULL;
7563    gpHistogram = NULL;
7564    if (writeFileFailed) {
7565       FailToWriteFileMessage(sz_path);
7566       unlink(sz_path);
7567       return FALSE;
7568    }
7569    return TRUE;
7570 }
7571 
SimpleRectBggen()7572 void SimpleRectBggen()
7573 {
7574    int image_w=0, image_h=0, w, h, short_name, rc, use_obj_pos=FALSE;
7575    int ltx=0, lty=0;
7576    int ncolors=0, chars_per_pixel=0, first_pixel_is_bg=0, *pixels=NULL;
7577    char szSpec[MAXSTRING+1], szPath[MAXPATHLENGTH+1], *rest;
7578    char *color_char=NULL, **color_str=NULL, *xpm_data=NULL;
7579    struct ObjRec *obj_ptr;
7580    XColor xcolor;
7581    Pixmap pixmap=None, bitmap=None;
7582    XImage *image=NULL, *bitmap_image=NULL;
7583 
7584    if (curChoice != NOTHING || topSel == NULL) {
7585       MakeQuiescent();
7586       if (!GetBggenImageSize(&image_w, &image_h)) {
7587          return;
7588       }
7589    } else if (!CheckSelectionForImageProc(CMDID_CIRCULARBGGEN)) {
7590       return;
7591    } else {
7592       obj_ptr = topSel->obj;
7593       ltx = obj_ptr->obbox.ltx;
7594       lty = obj_ptr->obbox.lty;
7595       image_w = obj_ptr->obbox.rbx - ltx;
7596       image_h = obj_ptr->obbox.rby - lty;
7597       use_obj_pos = TRUE;
7598       HighLightReverse();
7599    }
7600    *szSpec = '\0';
7601    Dialog(TgLoadString(STID_ENTER_COLOR_FOR_RECT_BGGEN),
7602          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), szSpec);
7603    UtilTrimBlanks(szSpec);
7604    if (*szSpec == '\0') {
7605       if (use_obj_pos) HighLightForward();
7606       return;
7607    }
7608    if (!TgifParseColor(szSpec, &xcolor)) {
7609       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_PARSE_NAMED_COLOR), szSpec);
7610       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7611       if (use_obj_pos) HighLightForward();
7612       return;
7613    }
7614    SaveStatusStrings();
7615    if (!SimpleRectBggenGenerateXpm(image_w, image_h, &xcolor, szPath,
7616          sizeof(szPath))) {
7617       RestoreStatusStrings();
7618       if (use_obj_pos) HighLightForward();
7619       return;
7620    }
7621    RestoreStatusStrings();
7622 
7623    if (fullTrueColorMode && HasZlibSupport()) {
7624       char deflated_fname[MAXPATHLENGTH+1];
7625       struct XPmRec *xpm_ptr=NULL;
7626 
7627       ResetPngHeaderInfo(&gPngHeaderInfo);
7628       obj_ptr = CreatePpmTrueObjFromFile(szPath);
7629       if (obj_ptr != NULL &&
7630             MkTempFile(deflated_fname, sizeof(deflated_fname), tmpDir,
7631             TOOL_NAME) != NULL &&
7632             DeflateFile(szPath, deflated_fname)) {
7633          /* good */
7634       } else {
7635          FreeObj(obj_ptr);
7636 
7637          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_GIVEN_PPM), szPath);
7638          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7639          unlink(szPath);
7640          return;
7641       }
7642       xpm_ptr = obj_ptr->detail.xpm;
7643       xpm_ptr->real_type = PPM_TRUE;
7644       xpm_ptr->ppm_data_compress = PPM_DATA_DEFLATED;
7645       xpm_ptr->ppm_data = ReadFileIntoBuf(deflated_fname,
7646             &xpm_ptr->ppm_data_size);
7647       xpm_ptr->ppm_mask_data = NULL;
7648       xpm_ptr->ppm_mask_size = 0;
7649       unlink(deflated_fname);
7650    } else {
7651       SetWatchCursor(drawWindow);
7652       SetWatchCursor(mainWindow);
7653       rc = MyReadPixmapFile(szPath, &image_w, &image_h, &w, &h, &pixmap,
7654             &image, &bitmap, &bitmap_image, &ncolors, &chars_per_pixel,
7655             &first_pixel_is_bg, &color_char, &color_str, &pixels, &xpm_data);
7656       SetDefaultCursor(mainWindow);
7657       ShowCursor();
7658 
7659       if ((short_name=IsPrefix(bootDir, szPath, &rest))) ++rest;
7660       if (rc != BitmapSuccess) {
7661          if (use_obj_pos) HighLightForward();
7662          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_XPM_FILE),
7663                (short_name ? rest : szPath));
7664          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7665          unlink(szPath);
7666          return;
7667       }
7668       obj_ptr = CreateXPmObj(image_w, image_h, w, h, pixmap, image, bitmap,
7669             bitmap_image, ncolors, chars_per_pixel, first_pixel_is_bg,
7670             color_char, color_str, pixels, xpm_data);
7671    }
7672    unlink(szPath);
7673    AddObj(NULL, topObj, obj_ptr);
7674    if (use_obj_pos) {
7675       RemoveAllSel();
7676       MoveObj(obj_ptr, ltx-obj_ptr->obbox.ltx, lty-obj_ptr->obbox.lty);
7677       numRedrawBBox = 0;
7678       obj_ptr->tmp_parent = NULL;
7679       DrawObj(drawWindow, obj_ptr);
7680    } else {
7681       PlaceTopObj(obj_ptr, NULL, NULL);
7682    }
7683    SelectTopObj();
7684    RecordNewObjCmd();
7685    SetFileModified(TRUE);
7686    justDupped = FALSE;
7687    if (!PRTGIF && colorLayers && needToRedrawColorWindow) {
7688       RedrawColorWindow();
7689    }
7690    sprintf(gszMsgBox, TgLoadString(STID_NEW_XPM_WH_GENERATED),
7691          image_w, image_h);
7692    Msg(gszMsgBox);
7693 }
7694 
7695 /* ----------------------- RegenerateImage ----------------------- */
7696 
7697 static
RegenerateImageFile(pszPath,xpm_ptr)7698 int RegenerateImageFile(pszPath, xpm_ptr)
7699    char *pszPath;
7700    struct XPmRec *xpm_ptr;
7701 {
7702    int saved_colordump=colorDump, saved_left=leftExportPixelTrim;
7703    int saved_top=topExportPixelTrim, saved_right=rightExportPixelTrim;
7704    int saved_bottom=bottomExportPixelTrim, saved_where_to_print=whereToPrint;
7705    int dump_ppm_preferred=FALSE;
7706 
7707    leftExportPixelTrim = topExportPixelTrim = rightExportPixelTrim =
7708          bottomExportPixelTrim = 0;
7709    *gszImageProcXPmFile = '\0';
7710    gnConvolving = FALSE;
7711 
7712    colorDump = TRUE;
7713    if (xpm_ptr == NULL) {
7714       /* called from CreatePixmapFromSelected() */
7715       if (fullTrueColorMode && HasZlibSupport()) {
7716          whereToPrint = PPM_FILE;
7717          dump_ppm_preferred = TRUE;
7718       } else {
7719          whereToPrint = XBM_FILE;
7720       }
7721    } else {
7722       /* called from RegenerateImage() or CropImage() */
7723       if (DoPpm6(xpm_ptr)) {
7724          whereToPrint = PPM_FILE;
7725          dump_ppm_preferred = TRUE;
7726       } else if (xpm_ptr->real_type == XPM_XPM &&
7727             fullTrueColorMode && HasZlibSupport()) {
7728          whereToPrint = PPM_FILE;
7729          dump_ppm_preferred = TRUE;
7730       } else {
7731          whereToPrint = XBM_FILE;
7732       }
7733    }
7734    gnInImageProc = TRUE;
7735    gpImageMapColorFunc = NULL;
7736 
7737    SetWatchCursor(drawWindow);
7738    SetWatchCursor(mainWindow);
7739    DumpXBitmapFile(gnInImageProc, dump_ppm_preferred, FALSE);
7740    SetDefaultCursor(mainWindow);
7741    ShowCursor();
7742 
7743    gnInImageProc = FALSE;
7744    whereToPrint = saved_where_to_print;
7745    colorDump = saved_colordump;
7746    leftExportPixelTrim = saved_left;
7747    topExportPixelTrim = saved_top;
7748    rightExportPixelTrim = saved_right;
7749    bottomExportPixelTrim = saved_bottom;
7750    if (*gszImageProcXPmFile == '\0') return FALSE;
7751    strcpy(pszPath, gszImageProcXPmFile);
7752    return TRUE;
7753 }
7754 
RegenerateImage()7755 void RegenerateImage()
7756 {
7757    int image_w=0, image_h=0, w, h, short_name, rc;
7758    int ltx=0, lty=0, ppm6=FALSE;
7759    int ncolors=0, chars_per_pixel=0, first_pixel_is_bg=0, *pixels=NULL;
7760    char *color_char=NULL, **color_str=NULL, *xpm_data=NULL, *rest;
7761    char szPath[MAXPATHLENGTH+1];
7762    struct ObjRec *obj_ptr=NULL, *saved_top_obj=NULL, *saved_bot_obj=NULL;
7763    struct SelRec *top_sel_ptr=NULL, *bot_sel_ptr=NULL;
7764    Pixmap pixmap=None, bitmap=None;
7765    XImage *image=NULL, *bitmap_image=NULL;
7766    struct XPmRec *xpm_ptr=NULL;
7767 
7768    if (!CheckSelectionForImageProc(CMDID_REGENERATEIMAGE)) {
7769       return;
7770    }
7771    obj_ptr = topSel->obj;
7772    xpm_ptr = obj_ptr->detail.xpm;
7773    if (obj_ptr->ctm == NULL) {
7774       if (MsgBox(TgLoadString(STID_XPM_NOT_XFORMED_REGEN_ANYWAY), TOOL_NAME,
7775             YNC_MB) != MB_ID_YES) {
7776          return;
7777       }
7778    }
7779    if (DoPpm6(xpm_ptr)) {
7780       ppm6 = TRUE;
7781    } else if (xpm_ptr->real_type == XPM_XPM &&
7782          fullTrueColorMode && HasZlibSupport()) {
7783       ppm6 = TRUE;
7784    }
7785    ltx = obj_ptr->obbox.ltx;
7786    lty = obj_ptr->obbox.lty;
7787 
7788    HighLightReverse();
7789    PrepareToReplaceAnObj(obj_ptr);
7790    PushPageInfo();
7791    saved_top_obj = topObj;
7792    saved_bot_obj = botObj;
7793 
7794    JustDupSelObj(&top_sel_ptr, &bot_sel_ptr);
7795    curPage->top = topObj = top_sel_ptr->obj;
7796    curPage->bot = botObj = bot_sel_ptr->obj;
7797    CopyObjId(topSel->obj, topObj);
7798    CopyObjLocks(topSel->obj, topObj);
7799 
7800    rc = RegenerateImageFile(szPath, topObj->detail.xpm);
7801 
7802    DelAllObj();
7803    free(top_sel_ptr);
7804    PopPageInfo();
7805    curPage->top = topObj = saved_top_obj;
7806    curPage->bot = botObj = saved_bot_obj;
7807    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
7808          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
7809    if (!rc) {
7810       HighLightForward();
7811       AbortPrepareCmd(CMD_REPLACE);
7812       return;
7813    }
7814    UnlinkObj(obj_ptr);
7815    FreeObj(obj_ptr);
7816    RemoveAllSel();
7817 
7818    if (ppm6) {
7819       char deflated_fname[MAXPATHLENGTH+1];
7820       struct XPmRec *xpm_ptr=NULL;
7821 
7822       ResetPngHeaderInfo(&gPngHeaderInfo);
7823       obj_ptr = CreatePpmTrueObjFromFile(szPath);
7824       if (obj_ptr != NULL &&
7825             MkTempFile(deflated_fname, sizeof(deflated_fname), tmpDir,
7826             TOOL_NAME) != NULL &&
7827             DeflateFile(szPath, deflated_fname)) {
7828          /* good */
7829       } else {
7830          FreeObj(obj_ptr);
7831 
7832          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_GIVEN_PPM), szPath);
7833          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7834          unlink(szPath);
7835          AbortPrepareCmd(CMD_REPLACE);
7836 
7837          return;
7838       }
7839       xpm_ptr = obj_ptr->detail.xpm;
7840       xpm_ptr->real_type = PPM_TRUE;
7841       xpm_ptr->ppm_data_compress = PPM_DATA_DEFLATED;
7842       xpm_ptr->ppm_data = ReadFileIntoBuf(deflated_fname,
7843             &xpm_ptr->ppm_data_size);
7844       xpm_ptr->ppm_mask_data = NULL;
7845       xpm_ptr->ppm_mask_size = 0;
7846       unlink(deflated_fname);
7847    } else {
7848       SetWatchCursor(drawWindow);
7849       SetWatchCursor(mainWindow);
7850       rc = MyReadPixmapFile(szPath, &image_w, &image_h, &w, &h, &pixmap,
7851             &image, &bitmap, &bitmap_image, &ncolors, &chars_per_pixel,
7852             &first_pixel_is_bg, &color_char, &color_str, &pixels, &xpm_data);
7853       SetDefaultCursor(mainWindow);
7854       ShowCursor();
7855 
7856       if ((short_name=IsPrefix(bootDir, szPath, &rest))) ++rest;
7857       if (rc != BitmapSuccess) {
7858          AbortPrepareCmd(CMD_REPLACE);
7859          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_XPM_FILE),
7860                (short_name ? rest : szPath));
7861          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7862          unlink(szPath);
7863          return;
7864       }
7865       obj_ptr = CreateXPmObj(image_w, image_h, w, h, pixmap, image, bitmap,
7866             bitmap_image, ncolors, chars_per_pixel, first_pixel_is_bg,
7867             color_char, color_str, pixels, xpm_data);
7868       if (obj_ptr == NULL) return;
7869    }
7870    unlink(szPath);
7871    AddObj(NULL, topObj, obj_ptr);
7872    MoveObj(obj_ptr, ltx-obj_ptr->obbox.ltx, lty-obj_ptr->obbox.lty);
7873    numRedrawBBox = 0;
7874    obj_ptr->tmp_parent = NULL;
7875    DrawObj(drawWindow, obj_ptr);
7876    SelectTopObj();
7877    RecordReplaceAnObj(topObj);
7878    SetFileModified(TRUE);
7879    justDupped = FALSE;
7880    if (!PRTGIF && colorLayers && needToRedrawColorWindow) {
7881       RedrawColorWindow();
7882    }
7883    sprintf(gszMsgBox, TgLoadString(STID_NEW_XPM_WH_GENERATED),
7884          image_w, image_h);
7885    Msg(gszMsgBox);
7886 }
7887 
7888 /* ----------------------- CreatePixmapFromSelected ----------------------- */
7889 
CreatePixmapFromSelected()7890 void CreatePixmapFromSelected()
7891 {
7892    int image_w=0, image_h=0, w=0, h=0, short_name=FALSE, rc=0;
7893    int ltx=0, lty=0, ppm6=FALSE;
7894    int ncolors=0, chars_per_pixel=0, first_pixel_is_bg=0, *pixels=NULL;
7895    char *color_char=NULL, **color_str=NULL, *xpm_data=NULL, *rest;
7896    char szPath[MAXPATHLENGTH+1];
7897    struct ObjRec *obj_ptr=NULL, *saved_top_obj=NULL, *saved_bot_obj=NULL;
7898    struct SelRec *top_sel_ptr=NULL, *bot_sel_ptr=NULL, *sel_ptr=NULL;
7899    struct SelRec *next_sel=NULL;
7900    Pixmap pixmap=None, bitmap=None;
7901    XImage *image=NULL, *bitmap_image=NULL;
7902 
7903    if (topSel == NULL) {
7904       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
7905       return;
7906    }
7907    if (topSel == botSel && topSel->obj->type == OBJ_XPM) {
7908       RegenerateImage();
7909       return;
7910    }
7911    if (fullTrueColorMode && HasZlibSupport()) {
7912       ppm6 = TRUE;
7913    }
7914    ltx = selLtX;
7915    lty = selLtY;
7916 
7917    HighLightReverse();
7918    PushPageInfo();
7919    saved_top_obj = topObj;
7920    saved_bot_obj = botObj;
7921 
7922    JustDupSelObj(&top_sel_ptr, &bot_sel_ptr);
7923    curPage->top = topObj = top_sel_ptr->obj;
7924    curPage->bot = botObj = bot_sel_ptr->obj;
7925    for (sel_ptr=topSel, obj_ptr=topObj; obj_ptr!=NULL;
7926          sel_ptr=sel_ptr->next, obj_ptr=obj_ptr->next) {
7927       CopyObjId(sel_ptr->obj, obj_ptr);
7928       CopyObjLocks(sel_ptr->obj, obj_ptr);
7929    }
7930    rc = RegenerateImageFile(szPath, NULL);
7931 
7932    DelAllObj();
7933    for (sel_ptr=top_sel_ptr; sel_ptr != NULL; sel_ptr=next_sel) {
7934       next_sel = sel_ptr->next;
7935       free(sel_ptr);
7936    }
7937    PopPageInfo();
7938    curPage->top = topObj = saved_top_obj;
7939    curPage->bot = botObj = saved_bot_obj;
7940    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
7941          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
7942    if (!rc) {
7943       HighLightForward();
7944       return;
7945    }
7946    RemoveAllSel();
7947 
7948    if (ppm6) {
7949       char deflated_fname[MAXPATHLENGTH+1];
7950       struct XPmRec *xpm_ptr=NULL;
7951 
7952       ResetPngHeaderInfo(&gPngHeaderInfo);
7953       obj_ptr = CreatePpmTrueObjFromFile(szPath);
7954       if (obj_ptr != NULL &&
7955             MkTempFile(deflated_fname, sizeof(deflated_fname), tmpDir,
7956             TOOL_NAME) != NULL &&
7957             DeflateFile(szPath, deflated_fname)) {
7958          /* good */
7959       } else {
7960          FreeObj(obj_ptr);
7961 
7962          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_GIVEN_PPM), szPath);
7963          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7964          unlink(szPath);
7965          AbortPrepareCmd(CMD_REPLACE);
7966 
7967          return;
7968       }
7969       xpm_ptr = obj_ptr->detail.xpm;
7970       xpm_ptr->real_type = PPM_TRUE;
7971       xpm_ptr->ppm_data_compress = PPM_DATA_DEFLATED;
7972       xpm_ptr->ppm_data = ReadFileIntoBuf(deflated_fname,
7973             &xpm_ptr->ppm_data_size);
7974       xpm_ptr->ppm_mask_data = NULL;
7975       xpm_ptr->ppm_mask_size = 0;
7976       unlink(deflated_fname);
7977    } else {
7978       SetWatchCursor(drawWindow);
7979       SetWatchCursor(mainWindow);
7980       rc = MyReadPixmapFile(szPath, &image_w, &image_h, &w, &h, &pixmap,
7981             &image, &bitmap, &bitmap_image, &ncolors, &chars_per_pixel,
7982             &first_pixel_is_bg, &color_char, &color_str, &pixels, &xpm_data);
7983       SetDefaultCursor(mainWindow);
7984       ShowCursor();
7985 
7986       if ((short_name=IsPrefix(bootDir, szPath, &rest))) ++rest;
7987       if (rc != BitmapSuccess) {
7988          AbortPrepareCmd(CMD_REPLACE);
7989          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_XPM_FILE),
7990                (short_name ? rest : szPath));
7991          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
7992          unlink(szPath);
7993          return;
7994       }
7995       obj_ptr = CreateXPmObj(image_w, image_h, w, h, pixmap, image, bitmap,
7996             bitmap_image, ncolors, chars_per_pixel, first_pixel_is_bg,
7997             color_char, color_str, pixels, xpm_data);
7998       if (obj_ptr == NULL) return;
7999    }
8000    unlink(szPath);
8001    AddObj(NULL, topObj, obj_ptr);
8002    MoveObj(obj_ptr, ltx-obj_ptr->obbox.ltx, lty-obj_ptr->obbox.lty);
8003    numRedrawBBox = 0;
8004    obj_ptr->tmp_parent = NULL;
8005    DrawObj(drawWindow, obj_ptr);
8006    SelectTopObj();
8007    RecordNewObjCmd();
8008    SetFileModified(TRUE);
8009    justDupped = FALSE;
8010    if (!PRTGIF && colorLayers && needToRedrawColorWindow) {
8011       RedrawColorWindow();
8012    }
8013    sprintf(gszMsgBox, TgLoadString(STID_NEW_XPM_WH_GENERATED),
8014          image_w, image_h);
8015    Msg(gszMsgBox);
8016 }
8017 
8018 /* ----------------------- CropImage ----------------------- */
8019 
8020 static
ContinueCrop(nObjAbsLtX,nObjAbsLtY,nStartXOff,nStartYOff,pnEndXOff,pnEndYOff)8021 void ContinueCrop(nObjAbsLtX, nObjAbsLtY, nStartXOff, nStartYOff,
8022       pnEndXOff, pnEndYOff)
8023    int nObjAbsLtX, nObjAbsLtY, nStartXOff, nStartYOff, *pnEndXOff, *pnEndYOff;
8024 {
8025    int end_x, end_y, cropping=TRUE;
8026    char buf[80], w_buf[80], h_buf[80], x_buf[80], y_buf[80];
8027 
8028    end_x = nStartXOff;
8029    end_y = nStartYOff;
8030    SelBox(drawWindow, revDefaultGC, nStartXOff, nStartYOff, end_x, end_y);
8031    PixelToMeasurementUnit(w_buf, 0);
8032    PixelToMeasurementUnit(h_buf, 0);
8033    PixelToMeasurementUnit(x_buf, ABS_X(end_x)-nObjAbsLtX);
8034    PixelToMeasurementUnit(y_buf, ABS_Y(end_y)-nObjAbsLtY);
8035    sprintf(buf, "w=%s\nh=%s\nx=%s\ny=%s", w_buf, h_buf, x_buf, y_buf);
8036    StartShowMeasureCursor(end_x, end_y, buf, TRUE);
8037    XGrabPointer(mainDisplay, drawWindow, False,
8038          PointerMotionMask | ButtonReleaseMask,
8039          GrabModeAsync, GrabModeAsync, None, handCursor, CurrentTime);
8040 
8041    while (cropping) {
8042       XEvent input;
8043 
8044       XNextEvent(mainDisplay, &input);
8045 
8046       if (input.type == Expose || input.type == VisibilityNotify) {
8047          ExposeEventHandler(&input, TRUE);
8048       } else if (input.type == ButtonRelease) {
8049          XUngrabPointer(mainDisplay, CurrentTime);
8050          PixelToMeasurementUnit(w_buf, ABS_SIZE(abs(end_x-nStartXOff)));
8051          PixelToMeasurementUnit(h_buf, ABS_SIZE(abs(end_y-nStartYOff)));
8052          PixelToMeasurementUnit(x_buf, ABS_X(end_x)-nObjAbsLtX);
8053          PixelToMeasurementUnit(y_buf, ABS_Y(end_y)-nObjAbsLtY);
8054          sprintf(buf, "w=%s\nh=%s\nx=%s\ny=%s", w_buf, h_buf, x_buf, y_buf);
8055          EndShowMeasureCursor(end_x, end_y, buf, TRUE);
8056          SelBox(drawWindow, revDefaultGC, nStartXOff, nStartYOff, end_x, end_y);
8057          cropping = FALSE;
8058       } else if (input.type == MotionNotify) {
8059          int new_end_x, new_end_y;
8060          XMotionEvent *motion_ev;
8061          XEvent ev;
8062 
8063          motion_ev = &(input.xmotion);
8064          new_end_x = motion_ev->x;
8065          new_end_y = motion_ev->y;
8066 
8067          PixelToMeasurementUnit(w_buf, ABS_SIZE(abs(end_x-nStartXOff)));
8068          PixelToMeasurementUnit(h_buf, ABS_SIZE(abs(end_y-nStartYOff)));
8069          PixelToMeasurementUnit(x_buf, ABS_X(end_x)-nObjAbsLtX);
8070          PixelToMeasurementUnit(y_buf, ABS_Y(end_y)-nObjAbsLtY);
8071          sprintf(buf, "w=%s\nh=%s\nx=%s\ny=%s", w_buf, h_buf, x_buf, y_buf);
8072          ShowMeasureCursor(end_x, end_y, buf, TRUE);
8073          SelBox(drawWindow, revDefaultGC, nStartXOff, nStartYOff, end_x, end_y);
8074          end_x = new_end_x; end_y = new_end_y;
8075          SelBox(drawWindow, revDefaultGC, nStartXOff, nStartYOff, end_x, end_y);
8076          PixelToMeasurementUnit(w_buf, ABS_SIZE(abs(end_x-nStartXOff)));
8077          PixelToMeasurementUnit(h_buf, ABS_SIZE(abs(end_y-nStartYOff)));
8078          PixelToMeasurementUnit(x_buf, ABS_X(end_x)-nObjAbsLtX);
8079          PixelToMeasurementUnit(y_buf, ABS_Y(end_y)-nObjAbsLtY);
8080          sprintf(buf, "w=%s\nh=%s\nx=%s\ny=%s", w_buf, h_buf, x_buf, y_buf);
8081          ShowMeasureCursor(end_x, end_y, buf, TRUE);
8082 
8083          MarkRulers(end_x, end_y);
8084          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
8085       }
8086    }
8087    *pnEndXOff = end_x;
8088    *pnEndYOff = end_y;
8089 }
8090 
8091 static
GetCropArea(obj_abs_ltx,obj_abs_lty,pbbox)8092 int GetCropArea(obj_abs_ltx, obj_abs_lty, pbbox)
8093    int obj_abs_ltx, obj_abs_lty;
8094    struct BBRec *pbbox;
8095 {
8096    unsigned int button;
8097    int mouse_x=0, mouse_y=0, rc=TRUE;
8098 
8099    SaveStatusStrings();
8100    Msg(TgLoadString(STID_DRAG_A_RECT_TO_CROP));
8101    SetMouseStatus(TgLoadString(STID_START_CROP_IMAGE),
8102          TgLoadString(STID_ABORT_CROP_IMAGE),
8103          TgLoadString(STID_ABORT_CROP_IMAGE));
8104    button = DrawWindowLoop(&mouse_x, &mouse_y, cornerCursor, FALSE);
8105    if (button == Button1) {
8106       int end_x=mouse_x, end_y=mouse_y;
8107 
8108       ContinueCrop(obj_abs_ltx, obj_abs_lty, mouse_x, mouse_y, &end_x, &end_y);
8109       if (pbbox != NULL) {
8110          CalcBBox(mouse_x, mouse_y, end_x, end_y,
8111                &pbbox->ltx, &pbbox->lty, &pbbox->rbx, &pbbox->rby);
8112       }
8113    } else {
8114       rc = FALSE;
8115    }
8116    Msg("");
8117    RestoreStatusStrings();
8118    return rc;
8119 }
8120 
CropImage()8121 void CropImage()
8122 {
8123    int image_w=0, image_h=0, w, h, short_name, rc, ppm6=FALSE;
8124    int ltx=0, lty=0, crop_x=0, crop_y=0, crop_w=0, crop_h=0;
8125    int ncolors=0, chars_per_pixel=0, first_pixel_is_bg=0, *pixels=NULL;
8126    int saved_ltx=selLtX, saved_lty=selLtY, saved_rbx=selRbX, saved_rby=selRbY;
8127    char *color_char=NULL, **color_str=NULL, *xpm_data=NULL, *rest;
8128    char szPath[MAXPATHLENGTH+1];
8129    struct ObjRec *obj_ptr=NULL, *saved_top_obj=NULL, *saved_bot_obj=NULL;
8130    struct SelRec *top_sel_ptr=NULL, *bot_sel_ptr=NULL;
8131    Pixmap pixmap=None, bitmap=None;
8132    XImage *image=NULL, *bitmap_image=NULL;
8133    struct AttrRec *saved_fattr=NULL, *saved_lattr=NULL;
8134    struct BBRec crop_bbox;
8135 
8136    if (!CheckSelectionForImageProc(CMDID_CROPIMAGE)) {
8137       return;
8138    }
8139    obj_ptr = topSel->obj;
8140    if (DoPpm6(obj_ptr->detail.xpm)) {
8141       ppm6 = TRUE;
8142    }
8143    ltx = obj_ptr->obbox.ltx;
8144    lty = obj_ptr->obbox.lty;
8145 
8146    HighLightReverse();
8147    XSync(mainDisplay, False);
8148    if (!GetCropArea(ltx, lty, &crop_bbox)) {
8149       return;
8150    }
8151    if (somethingHighLighted) {
8152       HighLightReverse();
8153    }
8154    XSync(mainDisplay, False);
8155    crop_bbox.ltx = ABS_X(crop_bbox.ltx); crop_bbox.lty = ABS_Y(crop_bbox.lty);
8156    crop_bbox.rbx = ABS_X(crop_bbox.rbx); crop_bbox.rby = ABS_Y(crop_bbox.rby);
8157    if (!BBoxIntersect(crop_bbox, obj_ptr->obbox)) {
8158       HighLightForward();
8159       MsgBox(TgLoadString(STID_SEL_AREA_NOT_INTERSECT_IMAGE), TOOL_NAME,
8160             INFO_MB);
8161       return;
8162    } else {
8163       crop_bbox.ltx = max(crop_bbox.ltx, obj_ptr->obbox.ltx);
8164       crop_bbox.lty = max(crop_bbox.lty, obj_ptr->obbox.lty);
8165       crop_bbox.rbx = min(crop_bbox.rbx, obj_ptr->obbox.rbx);
8166       crop_bbox.rby = min(crop_bbox.rby, obj_ptr->obbox.rby);
8167       crop_x = crop_bbox.ltx-obj_ptr->obbox.ltx;
8168       crop_y = crop_bbox.lty-obj_ptr->obbox.lty;
8169       crop_w = crop_bbox.rbx-crop_bbox.ltx;
8170       crop_h = crop_bbox.rby-crop_bbox.lty;
8171       if (crop_x == 0 && crop_y == 0 &&
8172             crop_w == (obj_ptr->obbox.rbx-obj_ptr->obbox.ltx) &&
8173             crop_h == (obj_ptr->obbox.rby-obj_ptr->obbox.lty)) {
8174          HighLightForward();
8175          return;
8176       }
8177       if (crop_w == 0 || crop_h == 0) {
8178          MsgBox(TgLoadString(STID_XPM_CANT_HAVE_0_W_OR_H), TOOL_NAME, INFO_MB);
8179          HighLightForward();
8180          return;
8181       }
8182    }
8183    if (obj_ptr->ctm == NULL) {
8184       HighLightForward();
8185       CutXPixmap(NULL, &crop_x, &crop_y, &crop_w, &crop_h);
8186       return;
8187    }
8188    PrepareToReplaceAnObj(obj_ptr);
8189    PushPageInfo();
8190    saved_top_obj = topObj;
8191    saved_bot_obj = botObj;
8192 
8193    JustDupSelObj(&top_sel_ptr, &bot_sel_ptr);
8194    curPage->top = topObj = top_sel_ptr->obj;
8195    curPage->bot = botObj = bot_sel_ptr->obj;
8196    CopyObjId(topSel->obj, topObj);
8197    CopyObjLocks(topSel->obj, topObj);
8198 
8199    rc = RegenerateImageFile(szPath, topObj->detail.xpm);
8200 
8201    DelAllObj();
8202    free(top_sel_ptr);
8203    PopPageInfo();
8204    curPage->top = topObj = saved_top_obj;
8205    curPage->bot = botObj = saved_bot_obj;
8206    RedrawAnArea(botObj, saved_ltx-GRID_ABS_SIZE(1),
8207          saved_lty-GRID_ABS_SIZE(1), saved_rbx+GRID_ABS_SIZE(1),
8208          saved_rby+GRID_ABS_SIZE(1));
8209    if (!rc) {
8210       HighLightForward();
8211       AbortPrepareCmd(CMD_REPLACE);
8212       return;
8213    }
8214    if (ppm6) {
8215       char deflated_fname[MAXPATHLENGTH+1];
8216       struct XPmRec *xpm_ptr=NULL;
8217       struct ObjRec *tmp_obj_ptr=NULL;
8218 
8219       ResetPngHeaderInfo(&gPngHeaderInfo);
8220       tmp_obj_ptr = CreatePpmTrueObjFromFile(szPath);
8221       if (tmp_obj_ptr != NULL &&
8222             MkTempFile(deflated_fname, sizeof(deflated_fname), tmpDir,
8223             TOOL_NAME) != NULL &&
8224             DeflateFile(szPath, deflated_fname)) {
8225          /* good */
8226       } else {
8227          FreeObj(tmp_obj_ptr);
8228 
8229          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_GIVEN_PPM), szPath);
8230          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
8231          unlink(szPath);
8232          AbortPrepareCmd(CMD_REPLACE);
8233 
8234          return;
8235       }
8236       xpm_ptr = tmp_obj_ptr->detail.xpm;
8237       xpm_ptr->real_type = PPM_TRUE;
8238       xpm_ptr->ppm_data_compress = PPM_DATA_DEFLATED;
8239       xpm_ptr->ppm_data = ReadFileIntoBuf(deflated_fname,
8240             &xpm_ptr->ppm_data_size);
8241       xpm_ptr->ppm_mask_data = NULL;
8242       xpm_ptr->ppm_mask_size = 0;
8243       unlink(deflated_fname);
8244 
8245       if (tmp_obj_ptr == NULL) {
8246          return;
8247       } else {
8248          struct ObjRec *new_obj_ptr=CutXPixmap(tmp_obj_ptr, &crop_x, &crop_y,
8249                &crop_w, &crop_h);
8250 
8251          if (new_obj_ptr == NULL || new_obj_ptr == tmp_obj_ptr) {
8252             FreeObj(tmp_obj_ptr);
8253             HighLightForward();
8254             AbortPrepareCmd(CMD_REPLACE);
8255             return;
8256          }
8257          FreeObj(tmp_obj_ptr);
8258 
8259          UnlinkObj(obj_ptr);
8260          FreeObj(obj_ptr);
8261          RemoveAllSel();
8262          obj_ptr = new_obj_ptr;
8263       }
8264       /* drop through */
8265    } else {
8266       SetWatchCursor(drawWindow);
8267       SetWatchCursor(mainWindow);
8268       rc = MyReadPixmapFile(szPath, &image_w, &image_h, &w, &h, &pixmap,
8269             &image, &bitmap, &bitmap_image, &ncolors, &chars_per_pixel,
8270             &first_pixel_is_bg, &color_char, &color_str, &pixels, &xpm_data);
8271       SetDefaultCursor(mainWindow);
8272       ShowCursor();
8273       if (rc == BitmapSuccess) {
8274          Pixmap dest_pixmap=None, dest_bitmap=None;
8275          XImage *dest_image=NULL, *dest_bitmap_image=NULL;
8276          int ok;
8277 
8278          ok = ExtractPixmap(pixmap, image, bitmap,
8279                bitmap_image, crop_x, crop_y, crop_w, crop_h,
8280                &dest_pixmap, &dest_image, &dest_bitmap, &dest_bitmap_image);
8281          if (ok) {
8282             XFreePixmap(mainDisplay, pixmap);
8283             XFreePixmap(mainDisplay, bitmap);
8284             XDestroyImage(image);
8285             XDestroyImage(bitmap_image);
8286             pixmap = dest_pixmap;
8287             bitmap = dest_bitmap;
8288             image = dest_image;
8289             bitmap_image = dest_bitmap_image;
8290          } else {
8291             rc = BitmapFileInvalid;
8292          }
8293       }
8294       if (rc == BitmapSuccess) {
8295          saved_fattr = obj_ptr->fattr;
8296          saved_lattr = obj_ptr->lattr;
8297          obj_ptr->fattr = obj_ptr->lattr = NULL;
8298          UnlinkObj(obj_ptr);
8299          FreeObj(obj_ptr);
8300          RemoveAllSel();
8301       }
8302       if ((short_name=IsPrefix(bootDir, szPath, &rest))) ++rest;
8303       if (rc != BitmapSuccess) {
8304          AbortPrepareCmd(CMD_REPLACE);
8305          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_IMPORT_XPM_FILE),
8306                (short_name ? rest : szPath));
8307          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
8308          unlink(szPath);
8309          return;
8310       }
8311       obj_ptr = CreateXPmObj(crop_w, crop_h, crop_w, crop_h, pixmap, image,
8312             bitmap, bitmap_image, ncolors, chars_per_pixel, first_pixel_is_bg,
8313             color_char, color_str, pixels, xpm_data);
8314    }
8315    unlink(szPath);
8316    AddObj(NULL, topObj, obj_ptr);
8317    MoveObj(obj_ptr, ltx-obj_ptr->obbox.ltx+crop_x,
8318          lty-obj_ptr->obbox.lty+crop_y);
8319    if (saved_fattr != NULL) {
8320       obj_ptr->fattr = saved_fattr;
8321       obj_ptr->lattr = saved_lattr;
8322    }
8323    RecordReplaceAnObj(topObj);
8324    RedrawAnArea(botObj, saved_ltx-GRID_ABS_SIZE(1), saved_lty-GRID_ABS_SIZE(1),
8325             saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1));
8326    SelectTopObj();
8327    SetFileModified(TRUE);
8328    justDupped = FALSE;
8329    if (!PRTGIF && colorLayers && needToRedrawColorWindow) {
8330       RedrawColorWindow();
8331    }
8332    sprintf(gszMsgBox, TgLoadString(STID_NEW_XPM_WH_GENERATED),
8333          image_w, image_h);
8334    Msg(gszMsgBox);
8335 }
8336 
8337 /* ----------------------- GetColor ----------------------- */
8338 
8339 static
SetCurrentColor(xpm_ptr,image,bitmap_image,image_x,image_y)8340 void SetCurrentColor(xpm_ptr, image, bitmap_image, image_x, image_y)
8341    struct XPmRec *xpm_ptr;
8342    XImage *image, *bitmap_image;
8343    int image_x, image_y;
8344 {
8345    int pixel=(-1);
8346 
8347    if (bitmap_image == NULL) {
8348       pixel = XGetPixel(image, image_x, image_y);
8349    } else {
8350       if (XGetPixel(bitmap_image, image_x, image_y) == 0) {
8351          /* transparent */
8352       } else {
8353          pixel = XGetPixel(image, image_x, image_y);
8354       }
8355    }
8356    if (pixel == (-1)) {
8357    } else if (DoPpm6(xpm_ptr)) {
8358       uint32_t pix=(uint32_t)(unsigned int)pixel;
8359       unsigned int r=0, g=0, b=0;
8360       double dr=(double)0, dg=(double)0, db=(double)0;
8361       char color_s[40];
8362       int color_index=0, new_alloc=FALSE;
8363 
8364       r = (pix & gTrueColorInfo.r_mask) >> gTrueColorInfo.r_shift;
8365       g = (pix & gTrueColorInfo.g_mask) >> gTrueColorInfo.g_shift;
8366       b = (pix & gTrueColorInfo.b_mask) >> gTrueColorInfo.b_shift;
8367       dr = ((double)r) / gTrueColorInfo.dr_maxval * ((double)256);
8368       dg = ((double)g) / gTrueColorInfo.dg_maxval * ((double)256);
8369       db = ((double)b) / gTrueColorInfo.db_maxval * ((double)256);
8370       r = (dr < ((double)0)) ? 0 : ((unsigned int)dr);
8371       g = (dg < ((double)0)) ? 0 : ((unsigned int)dg);
8372       b = (db < ((double)0)) ? 0 : ((unsigned int)db);
8373       if (r > 255) r = 255;
8374       if (g > 255) g = 255;
8375       if (b > 255) b = 255;
8376       sprintf(color_s, "#%02x%02x%02x", r, g, b);
8377       color_index = QuickFindColorIndex(NULL, color_s, &new_alloc, FALSE);
8378       if (color_index != INVALID) {
8379          ChangeAllSelColor(color_index, FALSE);
8380       }
8381    } else {
8382       int i;
8383 
8384       for (i=0; i < maxColors; i++) {
8385          if (colorPixels[i] == pixel) {
8386             struct SelRec *saved_top_sel=topSel, *saved_bot_sel=botSel;
8387 
8388             topSel = botSel = NULL;
8389             ChangeAllSelColor(i, FALSE);
8390             topSel = saved_top_sel;
8391             botSel = saved_bot_sel;
8392             break;
8393          }
8394       }
8395    }
8396 }
8397 
8398 static
DoGetColor(obj_ptr)8399 void DoGetColor(obj_ptr)
8400    struct ObjRec *obj_ptr;
8401 {
8402    int image_w, image_h, done=FALSE;
8403    struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
8404    XImage *image, *bitmap_image;
8405 
8406    image_w = xpm_ptr->image_w;
8407    image_h = xpm_ptr->image_h;
8408    image = xpm_ptr->image;
8409    bitmap_image = xpm_ptr->bitmap_image;
8410    if (image == NULL) {
8411       image = xpm_ptr->image = XGetImage(mainDisplay, xpm_ptr->pixmap, 0, 0,
8412             image_w, image_h, AllPlanes, ZPixmap);
8413       if (image == NULL) FailAllocMessage();
8414    }
8415    if (xpm_ptr->bitmap != None && bitmap_image == NULL) {
8416       bitmap_image = xpm_ptr->bitmap_image = XGetImage(mainDisplay,
8417             xpm_ptr->bitmap, 0, 0, image_w, image_h, AllPlanes, ZPixmap);
8418       if (bitmap_image == NULL) FailAllocMessage();
8419    }
8420    if (DoPpm6(xpm_ptr)) {
8421       if (!InitTrueColorInfo(image, &gTrueColorInfo, image_w)) {
8422          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE),
8423                image_w, image_h);
8424          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
8425          return;
8426       }
8427    }
8428    SaveStatusStrings();
8429    Msg(TgLoadString(STID_SEL_A_COLOR_TO_USE_AS_CUR));
8430    SetMouseStatus(TgLoadString(STID_SELECT_A_COLOR),
8431          TgLoadCachedString(CSTID_FINISH), TgLoadCachedString(CSTID_FINISH));
8432    while (!done) {
8433       int mouse_x=0, mouse_y=0;
8434       unsigned int button=PickAPoint(&mouse_x, &mouse_y, dripCursor);
8435 
8436       if (button == Button1) {
8437          int abs_x=ABS_X(mouse_x), abs_y=ABS_Y(mouse_y), found=FALSE;
8438          int image_x=0, image_y=0;
8439 
8440          if (obj_ptr->ctm == NULL) {
8441             if (abs_x >= obj_ptr->obbox.ltx && abs_y >= obj_ptr->obbox.lty &&
8442                   abs_x < obj_ptr->obbox.rbx && abs_y < obj_ptr->obbox.rby) {
8443                image_x = abs_x-obj_ptr->obbox.ltx;
8444                image_y = abs_y-obj_ptr->obbox.lty;
8445                found = TRUE;
8446             } else {
8447                SetStringStatus(TgLoadString(STID_SEL_PT_NOT_ON_IMAGE));
8448             }
8449          } else {
8450             struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
8451             double tmp_dx=(double)0, tmp_dy=(double)0;
8452 
8453             ReverseTransformDoublePointThroughCTM(
8454                   ((double)(abs_x-obj_ptr->x)+0.5),
8455                   ((double)(abs_y-obj_ptr->y)+0.5),
8456                   obj_ptr->ctm, &tmp_dx, &tmp_dy);
8457             tmp_dx += (double)(obj_ptr->x-obj_ptr->orig_obbox.ltx);
8458             tmp_dy += (double)(obj_ptr->y-obj_ptr->orig_obbox.lty);
8459             if (tmp_dx >= ((double)0) && tmp_dx < ((double)xpm_ptr->image_w) &&
8460                   tmp_dy >= ((double)0) &&
8461                   tmp_dy < ((double)xpm_ptr->image_h)) {
8462                int tmp_x=(int)tmp_dx, tmp_y=(int)tmp_dy;
8463 
8464                if (tmp_x < 0) tmp_x = 0;
8465                if (tmp_x >= xpm_ptr->image_w) tmp_x = xpm_ptr->image_w-1;
8466                if (tmp_y < 0) tmp_y = 0;
8467                if (tmp_y >= xpm_ptr->image_h) tmp_y = xpm_ptr->image_h-1;
8468                image_x = tmp_x;
8469                image_y = tmp_y;
8470                found = TRUE;
8471             } else {
8472                SetStringStatus(TgLoadString(STID_SEL_PT_NOT_ON_IMAGE));
8473             }
8474          }
8475          if (found) {
8476             SetCurrentColor(xpm_ptr, image, bitmap_image, image_x, image_y);
8477          }
8478       } else {
8479          done = TRUE;
8480       }
8481    }
8482    RestoreStatusStrings();
8483 }
8484 
GetColor()8485 void GetColor()
8486 {
8487    int i, pixel;
8488    char szBuf[MAXSTRING+1];
8489    struct ObjRec *obj_ptr;
8490 
8491    strcpy(szBuf, GetImageProcName(CMDID_GETCOLOR));
8492    if (!(curChoice == NOTHING || curChoice == ROTATEMODE) || topSel == NULL ||
8493          topSel != botSel) {
8494       sprintf(gszMsgBox, TgLoadString(STID_ONE_PRIM_FOR_IMAGEPROC_CMD), szBuf);
8495       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
8496       return;
8497    }
8498    obj_ptr = topSel->obj;
8499    switch (obj_ptr->type) {
8500    case OBJ_GROUP:
8501    case OBJ_ICON:
8502    case OBJ_SYM:
8503    case OBJ_PIN:
8504       sprintf(gszMsgBox, TgLoadString(STID_ONE_PRIM_FOR_IMAGEPROC_CMD), szBuf);
8505       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
8506       break;
8507    case OBJ_XPM:
8508       HighLightReverse();
8509       XSync(mainDisplay, False);
8510       DoGetColor(obj_ptr);
8511       if (!somethingHighLighted) HighLightForward();
8512       break;
8513    default:
8514       pixel = colorPixels[obj_ptr->color];
8515       for (i=0; i < maxColors; i++) {
8516          if (colorPixels[i] == pixel) {
8517             struct SelRec *saved_top_sel=topSel, *saved_bot_sel=botSel;
8518 
8519             topSel = botSel = NULL;
8520             ChangeAllSelColor(i, FALSE);
8521             topSel = saved_top_sel;
8522             botSel = saved_bot_sel;
8523             break;
8524          }
8525       }
8526       break;
8527    }
8528 }
8529 
8530 /* ----------------------- ReplaceColor ----------------------- */
8531 
8532 typedef struct tagReplaceColorInfo {
8533    int cmdid;
8534    int has_pixel_for_trans;
8535    int pixel_for_trans;
8536    int index_for_trans;
8537    struct XPmRec *xpm_ptr;
8538    TrueColorInfo tci;
8539 } ReplaceColorInfo;
8540 
8541 static ReplaceColorInfo gReplaceColorInfo;
8542 
8543 static
FillReplacePickAPoint(OrigX,OrigY,EndX,EndY,AllowDrag,cursor)8544 unsigned int FillReplacePickAPoint(OrigX, OrigY, EndX, EndY, AllowDrag, cursor)
8545    int *OrigX, *OrigY, *EndX, *EndY, AllowDrag;
8546    /* if AllowDrag is TRUE, EndX and EndY must not be NULL */
8547    Cursor cursor;
8548 {
8549    unsigned int button=(unsigned int)(-1);
8550    int dragging=FALSE;
8551    XEvent input;
8552 
8553    if (!debugNoPointerGrab) {
8554       XGrabPointer(mainDisplay, drawWindow, False,
8555             PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
8556             GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
8557    }
8558    for (;;) {
8559       XNextEvent(mainDisplay, &input);
8560 
8561       if (TgWindowIsPinnedMenu(input.xany.window, MENU_COLOR)) {
8562          SaveStatusStrings();
8563          TgHandlePinnedMenuEvent(input.xany.window, MENU_COLOR, &input);
8564          RestoreStatusStrings();
8565       } else if (TgIsCmdEvent(&input)) {
8566          TgHandleCmdEvent(&input);
8567       } else if (input.type == Expose || input.type == VisibilityNotify) {
8568          ExposeEventHandler(&input, TRUE);
8569       } else if (input.type == ButtonPress) {
8570          button = input.xbutton.button;
8571          *OrigX = input.xbutton.x;
8572          *OrigY = input.xbutton.y;
8573          if (AllowDrag && button == Button3) {
8574             *EndX = *OrigX;
8575             *EndY = *OrigY;
8576             SelBox(drawWindow, revDefaultGC, *OrigX, *OrigY, *EndX, *EndY);
8577             dragging = TRUE;
8578             SaveStatusStrings();
8579             SetStringStatus(TgLoadString(STID_DARG_TO_FILL_AN_AREA));
8580          } else {
8581             XUngrabPointer(mainDisplay, CurrentTime);
8582             XSync(mainDisplay, False);
8583             return button;
8584          }
8585       } else if (AllowDrag && dragging && input.type == ButtonRelease) {
8586          SelBox(drawWindow, revDefaultGC, *OrigX, *OrigY, *EndX, *EndY);
8587          XUngrabPointer(mainDisplay, CurrentTime);
8588          XSync(mainDisplay, False);
8589          *EndX = input.xbutton.x;
8590          *EndY = input.xbutton.y;
8591          RestoreStatusStrings();
8592          XSync(mainDisplay, False);
8593          return button;
8594       } else if (AllowDrag && dragging && input.type == MotionNotify) {
8595          SelBox(drawWindow, revDefaultGC, *OrigX, *OrigY, *EndX, *EndY);
8596          *EndX = input.xmotion.x;
8597          *EndY = input.xmotion.y;
8598          SelBox(drawWindow, revDefaultGC, *OrigX, *OrigY, *EndX, *EndY);
8599       } else if (input.type == KeyPress) {
8600          if (KeyPressEventIsEscape(&input.xkey)) {
8601             if (AllowDrag && dragging) {
8602                SelBox(drawWindow, revDefaultGC, *OrigX, *OrigY, *EndX, *EndY);
8603                RestoreStatusStrings();
8604             }
8605             XUngrabPointer(mainDisplay, CurrentTime);
8606             XSync(mainDisplay, False);
8607             return (unsigned int)(-1);
8608          }
8609       }
8610    }
8611 }
8612 
8613 static
DoReplaceAColor(obj_ptr,image,bitmap_image,image_x,image_y,image_w,image_h)8614 void DoReplaceAColor(obj_ptr, image, bitmap_image, image_x, image_y,
8615       image_w, image_h)
8616    struct ObjRec *obj_ptr;
8617    XImage *image, *bitmap_image;
8618    int image_x, image_y, image_w, image_h;
8619 {
8620    struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
8621    int pixel=(-1);
8622    XColor pixel_color;
8623 
8624    memset(&pixel_color, 0, sizeof(XColor));
8625    if (bitmap_image == NULL) {
8626       pixel = XGetPixel(image, image_x, image_y);
8627    } else {
8628       if (XGetPixel(bitmap_image, image_x, image_y) == 0) {
8629          /* transparent */
8630       } else {
8631          pixel = XGetPixel(image, image_x, image_y);
8632       }
8633    }
8634    if (pixel != (-1) && gThreshFillReplaceInfo.use_thresholding) {
8635       if (!SetupThreshFillReplaceInfo(&gThreshFillReplaceInfo, pixel, &pixel_color)) {
8636          return;
8637       }
8638    }
8639    SetWatchCursor(drawWindow);
8640    SetWatchCursor(mainWindow);
8641    if (pixel == (-1)) {
8642    } else {
8643       int i, r, *pixels=xpm_ptr->pixels, ncolors=xpm_ptr->ncolors;
8644       char **color_str=xpm_ptr->color_str;
8645 
8646       if (gThreshFillReplaceInfo.use_thresholding) {
8647          if (fullTrueColorMode) {
8648             for (r=0; r < image_h; r++) {
8649                int c;
8650 
8651                for (c=0; c < image_w; c++) {
8652                   int pix=XGetPixel(image, c, r);
8653 
8654                   if (pix != (-1) && TrueColorPixelWithinRange(pix,
8655                         &gThreshFillReplaceInfo)) {
8656                      if (gReplaceColorInfo.cmdid == CMDID_REPLACECOLORWITHTRANS) {
8657                         XPutPixel(image, c, r, gReplaceColorInfo.pixel_for_trans);
8658                         XPutPixel(bitmap_image, c, r, 0);
8659                      } else {
8660                         XPutPixel(image, c, r, colorPixels[colorIndex]);
8661                      }
8662                   }
8663                }
8664             }
8665          } else {
8666             for (r=0; r < image_h; r++) {
8667                int c;
8668 
8669                for (c=0; c < image_w; c++) {
8670                   int pix=XGetPixel(image, c, r), global_color_index=(-1);
8671 
8672                   if (pix != (-1) && HashLookUpInt(
8673                         &gThreshFillReplaceInfo.hash_table, (char*)(&pix),
8674                         sizeof(int), &global_color_index)) {
8675                      if (global_color_index != (-1) &&
8676                            gThreshFillReplaceInfo.within_threshold[global_color_index]) {
8677                         if (gReplaceColorInfo.cmdid == CMDID_REPLACECOLORWITHTRANS) {
8678                            XPutPixel(image, c, r, colorPixels[gReplaceColorInfo.index_for_trans]);
8679                            XPutPixel(bitmap_image, c, r, 0);
8680                         } else {
8681                            XPutPixel(image, c, r, colorPixels[colorIndex]);
8682                         }
8683                      }
8684                   }
8685                }
8686             }
8687             if (gThreshFillReplaceInfo.within_threshold != NULL) {
8688                free(gThreshFillReplaceInfo.within_threshold);
8689                gThreshFillReplaceInfo.within_threshold = NULL;
8690             }
8691          }
8692       } else {
8693          if (fullTrueColorMode) {
8694             for (r=0; r < image_h; r++) {
8695                int c;
8696 
8697                for (c=0; c < image_w; c++) {
8698                   if (XGetPixel(image, c, r) == pixel) {
8699                      if (gReplaceColorInfo.cmdid == CMDID_REPLACECOLORWITHTRANS) {
8700                         XPutPixel(image, c, r, gReplaceColorInfo.pixel_for_trans);
8701                         XPutPixel(bitmap_image, c, r, 0);
8702                      } else {
8703                         XPutPixel(image, c, r, colorPixels[colorIndex]);
8704                      }
8705                   }
8706                }
8707             }
8708          } else {
8709             for (r=0; r < image_h; r++) {
8710                int c;
8711 
8712                for (c=0; c < image_w; c++) {
8713                   if (XGetPixel(image, c, r) == pixel) {
8714                      if (gReplaceColorInfo.cmdid == CMDID_REPLACECOLORWITHTRANS) {
8715                         XPutPixel(image, c, r, colorPixels[gReplaceColorInfo.index_for_trans]);
8716                         XPutPixel(bitmap_image, c, r, 0);
8717                      } else {
8718                         XPutPixel(image, c, r, colorPixels[colorIndex]);
8719                      }
8720                   }
8721                }
8722             }
8723          }
8724          for (i=0; i < ncolors; i++) {
8725             if (pixels[i] == pixel) {
8726                pixels[i] = colorPixels[colorIndex];
8727                if (color_str[i] != NULL) free(color_str[i]);
8728                color_str[i] = UtilStrDup(colorMenuItems[colorIndex]);
8729                if (color_str[i] == NULL) FailAllocMessage();
8730             }
8731          }
8732       }
8733       if (xpm_ptr->data != NULL) {
8734 #ifdef _TGIF_DBG /* debug, do not translate */
8735          fprintf(stderr,
8736                "In ReplaceAColor(), unexpected xpm_ptr->data != NULL.\n");
8737 #endif /* _TGIF_DBG */
8738       }
8739    }
8740    SetDefaultCursor(mainWindow);
8741    ShowCursor();
8742 
8743    XPutImage(mainDisplay, xpm_ptr->pixmap, xpmGC, image, 0, 0, 0, 0,
8744       image_w, image_h);
8745    if (bitmap_image != NULL) {
8746       XPutImage(mainDisplay, xpm_ptr->bitmap, xbmGC, bitmap_image, 0, 0, 0, 0,
8747          image_w, image_h);
8748    }
8749    if (xpm_ptr->cached_pixmap != None) {
8750       XFreePixmap(mainDisplay, xpm_ptr->cached_pixmap);
8751       xpm_ptr->cached_pixmap = None;
8752    }
8753    if (xpm_ptr->cached_bitmap != None) {
8754       XFreePixmap(mainDisplay, xpm_ptr->cached_bitmap);
8755       xpm_ptr->cached_bitmap = None;
8756    }
8757    if ((xpm_ptr->real_type == XPM_JPEG ||
8758          xpm_ptr->real_type == PPM_TRUE) &&
8759          fullTrueColorMode && HasZlibSupport()) {
8760       unsigned int ppm_data_size=0;
8761       char tmp_fname[MAXPATHLENGTH], ext[MAXPATHLENGTH];
8762       char *ppm_data=NULL;
8763 
8764       *tmp_fname = *ext = '\0';
8765       if (MkTempFile(tmp_fname, sizeof(tmp_fname), tmpDir,
8766             TOOL_NAME) == NULL) {
8767          /* print error message? */
8768          return;
8769       }
8770       if (!DumpXImageToFile(xpm_ptr->image, xpm_ptr->image_w,
8771             xpm_ptr->image_h, tmp_fname, ext)) {
8772          /* print error message? */
8773          return;
8774       }
8775       if (strcmp(ext, ".ppm.z") == 0) {
8776          char deflated_fname[MAXPATHLENGTH];
8777 
8778          snprintf(deflated_fname, sizeof(deflated_fname), "%s%s",
8779                tmp_fname, ext);
8780          ppm_data = ReadFileIntoBuf(deflated_fname, &ppm_data_size);
8781          unlink(deflated_fname);
8782       }
8783       if (ppm_data != NULL) {
8784          if (xpm_ptr->ppm_data != NULL) free(xpm_ptr->ppm_data);
8785          if (xpm_ptr->ppm_mask_data != NULL) {
8786             free(xpm_ptr->ppm_mask_data);
8787          }
8788          xpm_ptr->real_type = PPM_TRUE;
8789          xpm_ptr->ppm_data = ppm_data;
8790          xpm_ptr->ppm_data_size = ppm_data_size;
8791          xpm_ptr->ppm_mask_data = NULL;
8792          xpm_ptr->ppm_mask_size = 0;
8793          xpm_ptr->ppm_data_compress = PPM_DATA_DEFLATED;
8794       }
8795    }
8796    AdjObjCache(obj_ptr);
8797    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
8798          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
8799    SetFileModified(TRUE);
8800    justDupped = FALSE;
8801 }
8802 
8803 static
ContinueReplaceColor(obj_ptr)8804 int ContinueReplaceColor(obj_ptr)
8805    struct ObjRec *obj_ptr;
8806 {
8807    int done=FALSE, image_w, image_h, changed=FALSE;
8808    struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
8809    XImage *image, *bitmap_image;
8810 
8811    xpm_ptr = obj_ptr->detail.xpm;
8812    image_w = xpm_ptr->image_w;
8813    image_h = xpm_ptr->image_h;
8814    image = xpm_ptr->image;
8815    bitmap_image = xpm_ptr->bitmap_image;
8816    if (image == NULL) {
8817       image = xpm_ptr->image = XGetImage(mainDisplay, xpm_ptr->pixmap, 0, 0,
8818             image_w, image_h, AllPlanes, ZPixmap);
8819       if (image == NULL) FailAllocMessage();
8820    }
8821    if (xpm_ptr->bitmap != None && bitmap_image == NULL) {
8822       bitmap_image = xpm_ptr->bitmap_image = XGetImage(mainDisplay,
8823             xpm_ptr->bitmap, 0, 0, image_w, image_h, AllPlanes, ZPixmap);
8824       if (bitmap_image == NULL) FailAllocMessage();
8825    }
8826    SaveStatusStrings();
8827    Msg(TgLoadString(STID_SEL_A_COLOR_TO_BE_REPLACED));
8828    SetMouseStatus(TgLoadString(STID_SEL_A_COLOR_TO_REPLACE),
8829          TgLoadCachedString(CSTID_FINISH), TgLoadCachedString(CSTID_FINISH));
8830    while (!done) {
8831       int mouse_x=0, mouse_y=0;
8832       unsigned int button=FillReplacePickAPoint(&mouse_x, &mouse_y, NULL, NULL,
8833             FALSE, floodCursor);
8834 
8835       if (button == Button1) {
8836          int abs_x=ABS_X(mouse_x), abs_y=ABS_Y(mouse_y), found=FALSE;
8837          int image_x=0, image_y=0;
8838 
8839          if (obj_ptr->ctm == NULL) {
8840             if (abs_x >= obj_ptr->obbox.ltx && abs_y >= obj_ptr->obbox.lty &&
8841                   abs_x < obj_ptr->obbox.rbx && abs_y < obj_ptr->obbox.rby) {
8842                image_x = abs_x-obj_ptr->obbox.ltx;
8843                image_y = abs_y-obj_ptr->obbox.lty;
8844                if (image_x >= 0 && image_y >= 0 &&
8845                      image_x < image_w && image_y < image_h) {
8846                   found = TRUE;
8847                }
8848             }
8849          } else {
8850             double image_dx=(double)0, image_dy=(double)0;
8851 
8852             ReverseTransformDoublePointThroughCTM(
8853                   ((double)(abs_x-obj_ptr->x)+0.5),
8854                   ((double)(abs_y-obj_ptr->y)+0.5),
8855                   obj_ptr->ctm, &image_dx, &image_dy);
8856             image_dx += (double)(obj_ptr->x-obj_ptr->orig_obbox.ltx);
8857             image_dy += (double)(obj_ptr->y-obj_ptr->orig_obbox.lty);
8858             if (image_dx >= ((double)0) && image_dy >= ((double)0) &&
8859                   image_dx < ((double)image_w) &&
8860                   image_dy < ((double)image_h)) {
8861                image_x = (int)image_dx;
8862                image_y = (int)image_dy;
8863                if (image_x < 0) image_x = 0;
8864                if (image_x >= image_w) image_x = image_w-1;
8865                if (image_y < 0) image_y = 0;
8866                if (image_y >= image_h) image_y = image_h-1;
8867                found = TRUE;
8868             }
8869          }
8870          if (found) {
8871             changed = TRUE;
8872             if (somethingHighLighted) HighLightReverse();
8873             DoReplaceAColor(obj_ptr, image, bitmap_image, image_x, image_y,
8874                   image_w, image_h);
8875             if (!somethingHighLighted) HighLightForward();
8876          } else {
8877             SetStringStatus(TgLoadString(STID_SEL_PT_NOT_ON_IMAGE));
8878          }
8879       } else {
8880          done = TRUE;
8881       }
8882    }
8883    RestoreStatusStrings();
8884    return changed;
8885 }
8886 
ReplaceColor()8887 void ReplaceColor()
8888 {
8889    struct ObjRec *obj_ptr=NULL;
8890    unsigned char trans_color_r='\0', trans_color_g='\0', trans_color_b='\0';
8891 
8892    if (!CheckSelectionForImageProc(CMDID_REPLACECOLOR)) {
8893       return;
8894    }
8895    obj_ptr = topSel->obj;
8896    if (ObjHasTrueColorTransPixel(obj_ptr, &trans_color_r, &trans_color_g,
8897          &trans_color_b)) {
8898       if (CurColorIsTranscolor(trans_color_r, trans_color_g, trans_color_b)) {
8899          sprintf(gszMsgBox, TgLoadString(STID_CUR_COLOR_IS_TRANS_PIXEL),
8900                colorMenuItems[colorIndex],
8901                GetImageProcName(CMDID_REPLACECOLOR));
8902          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
8903          return;
8904       }
8905    }
8906    memset(&gReplaceColorInfo, 0, sizeof(ReplaceColorInfo));
8907    gReplaceColorInfo.cmdid = CMDID_REPLACECOLOR;
8908 
8909    if (somethingHighLighted) HighLightReverse();
8910    XSync(mainDisplay, False);
8911    PrepareToReplaceAnObj(obj_ptr);
8912 
8913    ResetThreshFillReplaceInfo();
8914    if (!ContinueReplaceColor(obj_ptr)) {
8915       AbortPrepareCmd(CMD_REPLACE);
8916    } else {
8917       RecordReplaceAnObj(obj_ptr);
8918    }
8919    if (!somethingHighLighted) HighLightForward();
8920 }
8921 
8922 /* ----------------------- ReplaceColorWithTrans ----------------------- */
8923 
8924 static
SetupReplaceColorInfo(obj_ptr,prci)8925 int SetupReplaceColorInfo(obj_ptr, prci)
8926    struct ObjRec *obj_ptr;
8927    ReplaceColorInfo *prci;
8928 {
8929    struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
8930    int i=0, ncolors=xpm_ptr->ncolors, index=(-1);
8931    int image_w=xpm_ptr->image_w, image_h=xpm_ptr->image_h;
8932    unsigned char trans_color_r='\0', trans_color_g='\0', trans_color_b='\0';
8933    XImage *image=NULL;
8934    TrueColorInfo *ptci=(&prci->tci);
8935 
8936    prci->xpm_ptr = xpm_ptr;
8937 
8938    switch (xpm_ptr->real_type) {
8939    case XPM_XPM:
8940       if (!ObjHasIndexedTransPixel(obj_ptr, &index)) {
8941          int chars_per_pixel=xpm_ptr->chars_per_pixel;
8942 
8943          xpm_ptr->pixels = (int*)realloc(xpm_ptr->pixels, sizeof(int)*(ncolors+1));
8944          if (xpm_ptr->pixels == NULL) FailAllocMessage();
8945          xpm_ptr->pixels[ncolors] = (-1);
8946 
8947          xpm_ptr->color_str = (char**)realloc(xpm_ptr->color_str, sizeof(char*)*(ncolors+1));
8948          if (xpm_ptr->color_str == NULL) FailAllocMessage();
8949          xpm_ptr->color_str[ncolors] = UtilStrDup("None");
8950          if (xpm_ptr->color_str[ncolors] == NULL) FailAllocMessage();
8951 
8952          if (chars_per_pixel == 1 && ncolors >= 20) {
8953             /* needs to go from 1 chars_per_pixel to 2 chars_per_pixel */
8954             char *color_char=(char*)malloc(((ncolors+1)<<1)*sizeof(char));
8955 
8956             if (color_char == NULL) FailAllocMessage();
8957             for (i=0; i < ncolors+1; i++) {
8958                if (i == 0 && xpm_ptr->color_char[0] == '`') {
8959                   color_char[i<<1] = color_char[(i<<1)+1] = '`';
8960                } else {
8961                   color_char[i<<1] = (char)(((int)('a'))+(int)(i/10));
8962                   color_char[(i<<1)+1] = (char)(((int)('0'))+(int)(i%10));
8963                }
8964             }
8965             free(xpm_ptr->color_char);
8966             xpm_ptr->color_char = color_char;
8967             xpm_ptr->chars_per_pixel = 2;
8968          } else {
8969             char *color_char=NULL;
8970 
8971             xpm_ptr->color_char = color_char =
8972                   (char*)realloc(xpm_ptr->color_char,
8973                   sizeof(char)*chars_per_pixel*(ncolors+1));
8974             if (color_char == NULL) FailAllocMessage();
8975             if (chars_per_pixel == 1) {
8976                for (i=0; i < ncolors+1; i++) {
8977                   if (i == 0 && xpm_ptr->color_char[0] == '`') {
8978                      color_char[i] = '`';
8979                   } else {
8980                      color_char[i] = (char)(((int)('a'))+i-1);
8981                   }
8982                }
8983             } else {
8984                for (i=0; i < ncolors+1; i++) {
8985                   if (i == 0 && xpm_ptr->color_char[0] == '`' &&
8986                         xpm_ptr->color_char[1] == '`') {
8987                      color_char[i<<1] = color_char[(i<<1)+1] = '`';
8988                   } else {
8989                      color_char[i<<1] = (char)(((int)('a'))+(int)(i/10));
8990                      color_char[(i<<1)+1] = (char)(((int)('0'))+(int)(i%10));
8991                   }
8992                }
8993             }
8994          }
8995          xpm_ptr->ncolors++;
8996 
8997          index = ncolors;
8998       }
8999       prci->index_for_trans = index;
9000       prci->has_pixel_for_trans = TRUE;
9001       break;
9002    case XPM_JPEG: break;
9003    case PPM_TRUE:
9004       image = XGetImage(mainDisplay, xpm_ptr->pixmap, 0, 0, image_w, image_h, AllPlanes, ZPixmap);
9005 
9006       if (image == NULL || !InitTrueColorInfo(image, &prci->tci, image_w)) {
9007          sprintf(gszMsgBox, TgLoadString(STID_CANNOT_GET_IMAGE_OF_SIZE), image_w, image_h);
9008          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
9009          XDestroyImage(image);
9010          return FALSE;
9011       }
9012       if (ObjHasTrueColorTransPixel(obj_ptr, &trans_color_r, &trans_color_g, &trans_color_b)) {
9013          unsigned int r=(unsigned int)trans_color_r;
9014          unsigned int g=(unsigned int)trans_color_g;
9015          unsigned int b=(unsigned int)trans_color_b;
9016 
9017          prci->pixel_for_trans = ((r << ptci->r_shift) & mainVisual->red_mask) |
9018                  ((g << ptci->g_shift) & mainVisual->green_mask) |
9019                  ((b << ptci->b_shift) & mainVisual->blue_mask) ;
9020       } else {
9021          unsigned char has_r[256], has_g[256], has_b[256];
9022          unsigned char can_have_r[256], can_have_g[256], can_have_b[256];
9023          int row=0, found=FALSE;
9024          XImage *image=XGetImage(mainDisplay, xpm_ptr->pixmap, 0, 0, image_w, image_h, AllPlanes, ZPixmap);
9025          unsigned int r_maxval=((ptci->r_mask)>>ptci->r_shift);
9026          unsigned int g_maxval=((ptci->g_mask)>>ptci->g_shift);
9027          unsigned int b_maxval=((ptci->b_mask)>>ptci->b_shift);
9028 
9029          memset(has_r, 0, sizeof(256*sizeof(unsigned char)));
9030          memset(has_g, 0, sizeof(256*sizeof(unsigned char)));
9031          memset(has_b, 0, sizeof(256*sizeof(unsigned char)));
9032          memset(can_have_r, 0, sizeof(256*sizeof(unsigned char)));
9033          memset(can_have_g, 0, sizeof(256*sizeof(unsigned char)));
9034          memset(can_have_b, 0, sizeof(256*sizeof(unsigned char)));
9035          for (i=0; i < r_maxval; i++) {
9036             double dval=((double)i)/ptci->dr_maxval_div255;
9037             unsigned int uval=round(dval);
9038 
9039             if (uval > 255) uval = 255;
9040             can_have_r[uval] = 1;
9041          }
9042          for (i=0; i < g_maxval; i++) {
9043             double dval=((double)i)/ptci->dg_maxval_div255;
9044             unsigned int uval=round(dval);
9045 
9046             if (uval > 255) uval = 255;
9047             can_have_g[uval] = 1;
9048          }
9049          for (i=0; i < b_maxval; i++) {
9050             double dval=((double)i)/ptci->db_maxval_div255;
9051             unsigned int uval=round(dval);
9052 
9053             if (uval > 255) uval = 255;
9054             can_have_b[uval] = 1;
9055          }
9056          for (row=0; row < image_h; row++) {
9057             int col=0;
9058 
9059             for (col=0; col < image_w; col++) {
9060                int pixel=XGetPixel(image, col, row);
9061                uint32_t pix=(uint32_t)pixel;
9062                unsigned int r=0, g=0, b=0;
9063                double dr=(double)0, dg=(double)0, db=(double)0;
9064 
9065                r = (pix & ptci->r_mask) >> ptci->r_shift;
9066                g = (pix & ptci->g_mask) >> ptci->g_shift;
9067                b = (pix & ptci->b_mask) >> ptci->b_shift;
9068                dr = ((double)r) / ptci->dr_maxval_div255;
9069                dg = ((double)g) / ptci->dg_maxval_div255;
9070                db = ((double)b) / ptci->db_maxval_div255;
9071                r = round(dr);
9072                g = round(dg);
9073                b = round(db);
9074                if (r > 255) r = 255;
9075                if (g > 255) g = 255;
9076                if (b > 255) b = 255;
9077                if (r == 255 && g == 255) has_b[b] = 1;
9078                if (r == 255 && b == 255) has_g[g] = 1;
9079                if (g == 255 && b == 255) has_r[r] = 1;
9080             }
9081          }
9082          XDestroyImage(image);
9083 
9084          for (i=255; i >= 0 && !found; i--) {
9085             if (can_have_r[i] && !has_r[i]) {
9086                prci->pixel_for_trans = ((i << ptci->r_shift) & mainVisual->red_mask) |
9087                     ((255 << ptci->g_shift) & mainVisual->green_mask) |
9088                     ((255 << ptci->b_shift) & mainVisual->blue_mask) ;
9089                found = TRUE;
9090             }
9091          }
9092          for (i=255; i >= 0 && !found; i--) {
9093             if (can_have_g[i] && !has_g[i]) {
9094                prci->pixel_for_trans = ((i << ptci->g_shift) & mainVisual->green_mask);
9095                found = TRUE;
9096             }
9097          }
9098          for (i=255; i >= 0 && !found; i--) {
9099             if (can_have_b[i] && !has_b[i]) {
9100                prci->pixel_for_trans = ((i << ptci->b_shift) & mainVisual->blue_mask);
9101                found = TRUE;
9102             }
9103          }
9104          if (!found) {
9105             snprintf(gszMsgBox, sizeof(gszMsgBox), TgLoadString(STID_CANNOT_FIND_GOOD_TRANSPIX));
9106             MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
9107             return FALSE;
9108          }
9109       }
9110       prci->has_pixel_for_trans = TRUE;
9111       break;
9112    }
9113    if (!prci->has_pixel_for_trans) {
9114       return FALSE;
9115    }
9116 #ifdef _TGIF_DBG /* debug, do not translate */
9117 #ifdef NOT_DEFINED
9118    if (prci->xpm_ptr->real_type == XPM_XPM) {
9119       fprintf(stderr, "index_for_trans = %1d\n", prci->index_for_trans);
9120    } else {
9121       fprintf(stderr, "pixel_for_trans = %08x\n", prci->pixel_for_trans);
9122    }
9123 #endif /* NOT_DEFINED */
9124 #endif /* _TGIF_DBG */
9125    return TRUE;
9126 }
9127 
ReplaceColorWithTrans()9128 void ReplaceColorWithTrans()
9129 {
9130    struct ObjRec *obj_ptr=NULL;
9131 
9132    if (!CheckSelectionForImageProc(CMDID_REPLACECOLORWITHTRANS)) {
9133       return;
9134    }
9135    obj_ptr = topSel->obj;
9136 
9137    memset(&gReplaceColorInfo, 0, sizeof(ReplaceColorInfo));
9138    gReplaceColorInfo.cmdid = CMDID_REPLACECOLORWITHTRANS;
9139    if (!SetupReplaceColorInfo(obj_ptr, &gReplaceColorInfo)) {
9140       return;
9141    }
9142    if (somethingHighLighted) HighLightReverse();
9143    XSync(mainDisplay, False);
9144    PrepareToReplaceAnObj(obj_ptr);
9145 
9146    ResetThreshFillReplaceInfo();
9147    if (!ContinueReplaceColor(obj_ptr)) {
9148       AbortPrepareCmd(CMD_REPLACE);
9149    } else {
9150       RecordReplaceAnObj(obj_ptr);
9151    }
9152    if (!somethingHighLighted) HighLightForward();
9153 }
9154 
9155 /* ----------------------- FloodFill ----------------------- */
9156 
9157 #define DIR_NONE 0
9158 #define DIR_UP 1
9159 #define DIR_RIGHT 2
9160 #define DIR_DOWN 3
9161 #define DIR_LEFT 4
9162 
9163 /*
9164  *      1
9165  *      ^
9166  *      |
9167  * 4 <- 0 -> 2
9168  *      |
9169  *      v
9170  *      3
9171  */
9172 
9173 static int gnPixelToFill=(-1);
9174 
9175 typedef struct tagCell {
9176    int row;
9177    int col;
9178    int dir; /* direction where it came from */
9179 } Cell;
9180 
9181 #define CELL_NOTVISITED(map,c,r,image_w) (map[(c)+(r)*(image_w)]==0)
9182 
9183 static
SelectThisPixelForFloodFill(c,r,pixel,image)9184 int SelectThisPixelForFloodFill(c, r, pixel, image)
9185    int c, r, pixel;
9186    XImage *image;
9187 {
9188    int pix=XGetPixel(image, c, r);
9189 
9190    if (gThreshFillReplaceInfo.use_thresholding) {
9191       if (fullTrueColorMode) {
9192          if (pix != (-1) && TrueColorPixelWithinRange(pix,
9193                &gThreshFillReplaceInfo)) {
9194             /* skip */
9195          } else {
9196             return FALSE;
9197          }
9198       } else {
9199          int global_color_index=(-1);
9200 
9201          if (pix != (-1) && HashLookUpInt(
9202                &gThreshFillReplaceInfo.hash_table, (char*)(&pix),
9203                sizeof(int), &global_color_index)) {
9204             if (global_color_index != (-1) &&
9205                   gThreshFillReplaceInfo.within_threshold[global_color_index]) {
9206                /* skip */
9207             } else {
9208                return FALSE;
9209             }
9210          } else {
9211             return FALSE;
9212          }
9213       }
9214    } else {
9215       if (pix != pixel) {
9216          return FALSE;
9217       }
9218    }
9219    return TRUE;
9220 }
9221 
9222 static
FloodFillAddToQueue(plist,c,r,dir,pbbox)9223 void FloodFillAddToQueue(plist, c, r, dir, pbbox)
9224    CVList *plist;
9225    int c, r, dir;
9226    struct BBRec *pbbox;
9227 {
9228    Cell *cell=(Cell*)malloc(sizeof(Cell));
9229 
9230    if (cell == NULL) FailAllocMessage();
9231    memset(cell, 0, sizeof(Cell));
9232    cell->row = r;
9233    cell->col = c;
9234    cell->dir = dir;
9235 
9236    ListAppend(plist, cell);
9237    if (c < pbbox->ltx) pbbox->ltx = c;
9238    if (c > pbbox->rbx) pbbox->rbx = c;
9239    if (r < pbbox->lty) pbbox->lty = r;
9240    if (r > pbbox->rby) pbbox->rby = r;
9241 }
9242 
9243 static
FloodFillBFS(image,pixel,image_w,image_h,plist,map,pbbox)9244 void FloodFillBFS(image, pixel, image_w, image_h, plist, map, pbbox)
9245    XImage *image;
9246    int pixel, image_w, image_h;
9247    CVList *plist;
9248    unsigned char *map;
9249    struct BBRec *pbbox;
9250 {
9251    while (!ListEmpty(plist)) {
9252       CVListElem *elem=ListFirst(plist);
9253       Cell *cell=(Cell*)(elem->obj);
9254       int c=cell->col, r=cell->row; /* this pixel must be already selected */
9255       int dir=cell->dir;
9256       int next_r=0, next_c=0, next_dir=0;
9257 
9258       ListUnlink(plist, elem);
9259       free(elem);
9260       free(cell);
9261 
9262       if (dir != DIR_DOWN && r-1 >= 0) {
9263          next_c = c;
9264          next_r = r-1;
9265          next_dir = DIR_UP;
9266          if (CELL_NOTVISITED(map,next_c,next_r,image_w)) {
9267              if (SelectThisPixelForFloodFill(next_c, next_r, pixel, image)) {
9268                 map[next_c+(next_r*image_w)] = 1;
9269                 FloodFillAddToQueue(plist, next_c, next_r, next_dir, pbbox);
9270              } else {
9271                 map[next_c+(next_r*image_w)] = (-1);
9272              }
9273          }
9274       }
9275       if (dir != DIR_LEFT && c+1 < image_w) {
9276          next_c = c+1;
9277          next_r = r;
9278          next_dir = DIR_RIGHT;
9279          if (CELL_NOTVISITED(map,next_c,next_r,image_w)) {
9280              if (SelectThisPixelForFloodFill(next_c, next_r, pixel, image)) {
9281                 map[next_c+(next_r*image_w)] = 1;
9282                 FloodFillAddToQueue(plist, next_c, next_r, next_dir, pbbox);
9283              } else {
9284                 map[next_c+(next_r*image_w)] = (-1);
9285              }
9286          }
9287       }
9288       if (dir != DIR_UP && r+1 < image_h) {
9289          next_c = c;
9290          next_r = r+1;
9291          next_dir = DIR_DOWN;
9292          if (CELL_NOTVISITED(map,next_c,next_r,image_w)) {
9293              if (SelectThisPixelForFloodFill(next_c, next_r, pixel, image)) {
9294                 map[next_c+(next_r*image_w)] = 1;
9295                 FloodFillAddToQueue(plist, next_c, next_r, next_dir, pbbox);
9296              } else {
9297                 map[next_c+(next_r*image_w)] = (-1);
9298              }
9299          }
9300       }
9301       if (dir != DIR_RIGHT && c-1 >= 0) {
9302          next_c = c-1;
9303          next_r = r;
9304          next_dir = DIR_LEFT;
9305          if (CELL_NOTVISITED(map,next_c,next_r,image_w)) {
9306              if (SelectThisPixelForFloodFill(next_c, next_r, pixel, image)) {
9307                 map[next_c+(next_r*image_w)] = 1;
9308                 FloodFillAddToQueue(plist, next_c, next_r, next_dir, pbbox);
9309              } else {
9310                 map[next_c+(next_r*image_w)] = (-1);
9311              }
9312          }
9313       }
9314    }
9315 }
9316 
9317 static
CalcFloodFill(c,r,pixel,image,image_w,image_h,dir,map,pbbox)9318 void CalcFloodFill(c, r, pixel, image, image_w, image_h, dir, map, pbbox)
9319    int c, r, pixel, image_w, image_h, dir;
9320    XImage *image;
9321    unsigned char *map;
9322    struct BBRec *pbbox;
9323 {
9324    CVList list;
9325 
9326    CVListInit(&list);
9327 
9328    FloodFillAddToQueue(&list, c, r, dir, pbbox);
9329    FloodFillBFS(image, pixel, image_w, image_h, &list, map, pbbox);
9330 }
9331 
9332 static
DoFloodFill(image,image_w,map,pbbox)9333 void DoFloodFill(image, image_w, map, pbbox)
9334    XImage *image;
9335    int image_w;
9336    unsigned char *map;
9337    struct BBRec *pbbox;
9338 {
9339    int r=0;
9340 
9341    for (r=pbbox->lty; r <= pbbox->rby; r++) {
9342       int c=0;
9343 
9344       for (c=pbbox->ltx; c <= pbbox->rbx; c++) {
9345          if (map[c+r*image_w] == 1) {
9346             XPutPixel(image, c, r, gnPixelToFill);
9347          }
9348       }
9349    }
9350 }
9351 
9352 static
StartFloodFill(obj_ptr,image,bitmap_image,image_x,image_y,image_w,image_h,do_flood_fill)9353 void StartFloodFill(obj_ptr, image, bitmap_image, image_x, image_y,
9354       image_w, image_h, do_flood_fill)
9355    struct ObjRec *obj_ptr;
9356    XImage *image, *bitmap_image;
9357    int image_x, image_y, image_w, image_h, do_flood_fill;
9358 {
9359    struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
9360    int pixel=(-1);
9361    XColor pixel_color;
9362 
9363    memset(&pixel_color, 0, sizeof(XColor));
9364    if (bitmap_image == NULL) {
9365       pixel = XGetPixel(image, image_x, image_y);
9366    } else {
9367       if (XGetPixel(bitmap_image, image_x, image_y) == 0) {
9368          /* transparent */
9369       } else {
9370          pixel = XGetPixel(image, image_x, image_y);
9371       }
9372    }
9373    if (do_flood_fill && pixel != (-1) && gThreshFillReplaceInfo.use_thresholding) {
9374       if (!SetupThreshFillReplaceInfo(&gThreshFillReplaceInfo, pixel, &pixel_color)) {
9375          return;
9376       }
9377    }
9378    gnPixelToFill = colorPixels[colorIndex];
9379    if (pixel == (-1)) {
9380    } else if (gnPixelToFill != pixel) {
9381       int i, *pixels=xpm_ptr->pixels, ncolors=xpm_ptr->ncolors, found=FALSE;
9382 
9383       if (do_flood_fill) {
9384          struct BBRec bbox;
9385          unsigned char *map=(unsigned char *)malloc(image_h*image_w*sizeof(unsigned char));
9386 
9387          if (map == NULL) FailAllocMessage();
9388          /* 0 means not visited, 1 means update pixel, otherwise, don't update */
9389          memset(map, 0, image_h*image_w*sizeof(unsigned char));
9390          map[image_x+image_y*image_w] = 1;
9391          bbox.ltx = bbox.rbx = image_x;
9392          bbox.lty = bbox.rby = image_y;
9393          CalcFloodFill(image_x, image_y, pixel, image, image_w, image_h, 0, map, &bbox);
9394          DoFloodFill(image, image_w, map, &bbox);
9395          free(map);
9396 
9397          if (gThreshFillReplaceInfo.within_threshold != NULL) {
9398             free(gThreshFillReplaceInfo.within_threshold);
9399             gThreshFillReplaceInfo.within_threshold = NULL;
9400          }
9401       } else {
9402          XPutPixel(image, image_x, image_y, gnPixelToFill);
9403       }
9404       for (i=0; i < ncolors; i++) {
9405          if (pixels[i] == gnPixelToFill) {
9406             found = TRUE;
9407          }
9408       }
9409       if (!found && xpm_ptr->real_type == XPM_XPM) {
9410          int chars_per_pixel=xpm_ptr->chars_per_pixel;
9411 
9412          xpm_ptr->pixels = (int*)realloc(xpm_ptr->pixels,
9413                sizeof(int)*(ncolors+1));
9414          if (xpm_ptr->pixels == NULL) FailAllocMessage();
9415          xpm_ptr->pixels[ncolors] = colorPixels[colorIndex];
9416 
9417          xpm_ptr->color_str = (char**)realloc(xpm_ptr->color_str,
9418                sizeof(char*)*(ncolors+1));
9419          if (xpm_ptr->color_str == NULL) FailAllocMessage();
9420          xpm_ptr->color_str[ncolors] = UtilStrDup(colorMenuItems[colorIndex]);
9421          if (xpm_ptr->color_str[ncolors] == NULL) FailAllocMessage();
9422 
9423          if (chars_per_pixel == 1 && ncolors >= 20) {
9424             /* needs to go from 1 chars_per_pixel to 2 chars_per_pixel */
9425             char *color_char=(char*)malloc(((ncolors+1)<<1)*sizeof(char));
9426 
9427             if (color_char == NULL) FailAllocMessage();
9428             for (i=0; i < ncolors+1; i++) {
9429                if (i == 0 && xpm_ptr->color_char[0] == '`') {
9430                   color_char[i<<1] = color_char[(i<<1)+1] = '`';
9431                } else {
9432                   color_char[i<<1] = (char)(((int)('a'))+(int)(i/10));
9433                   color_char[(i<<1)+1] = (char)(((int)('0'))+(int)(i%10));
9434                }
9435             }
9436             free(xpm_ptr->color_char);
9437             xpm_ptr->color_char = color_char;
9438             xpm_ptr->chars_per_pixel = 2;
9439          } else {
9440             char *color_char;
9441 
9442             xpm_ptr->color_char = color_char =
9443                   (char*)realloc(xpm_ptr->color_char,
9444                   sizeof(char)*chars_per_pixel*(ncolors+1));
9445             if (color_char == NULL) FailAllocMessage();
9446             if (chars_per_pixel == 1) {
9447                for (i=0; i < ncolors+1; i++) {
9448                   if (i == 0 && xpm_ptr->color_char[0] == '`') {
9449                      color_char[i] = '`';
9450                   } else {
9451                      color_char[i] = (char)(((int)('a'))+i-1);
9452                   }
9453                }
9454             } else {
9455                for (i=0; i < ncolors+1; i++) {
9456                   if (i == 0 && xpm_ptr->color_char[0] == '`' &&
9457                         xpm_ptr->color_char[1] == '`') {
9458                      color_char[i<<1] = color_char[(i<<1)+1] = '`';
9459                   } else {
9460                      color_char[i<<1] = (char)(((int)('a'))+(int)(i/10));
9461                      color_char[(i<<1)+1] = (char)(((int)('0'))+(int)(i%10));
9462                   }
9463                }
9464             }
9465          }
9466          xpm_ptr->ncolors++;
9467       }
9468       if (xpm_ptr->data != NULL) {
9469 #ifdef _TGIF_DBG /* debug, do not translate */
9470          fprintf(stderr,
9471                "In FloodFill(), unexpected xpm_ptr->data != NULL.\n");
9472 #endif /* _TGIF_DBG */
9473       }
9474    }
9475 }
9476 
9477 static
TryFloodFill(obj_ptr,image_w,image_h,image,bitmap_image,button,mouse_x,mouse_y)9478 int TryFloodFill(obj_ptr, image_w, image_h, image, bitmap_image, button,
9479       mouse_x, mouse_y)
9480    struct ObjRec *obj_ptr;
9481    int image_w, image_h, mouse_x, mouse_y;
9482    XImage *image, *bitmap_image;
9483    unsigned int button;
9484 {
9485    int changed=FALSE, abs_x=ABS_X(mouse_x), abs_y=ABS_Y(mouse_y), found=FALSE;
9486    int image_x=0, image_y=0;
9487 
9488    if (obj_ptr->ctm == NULL) {
9489       if (abs_x >= obj_ptr->obbox.ltx && abs_y >= obj_ptr->obbox.lty &&
9490             abs_x < obj_ptr->obbox.rbx && abs_y < obj_ptr->obbox.rby) {
9491          image_x = abs_x-obj_ptr->obbox.ltx;
9492          image_y = abs_y-obj_ptr->obbox.lty;
9493          if (image_x >= 0 && image_y >= 0 &&
9494                image_x < image_w && image_y < image_h) {
9495             found = TRUE;
9496          }
9497       }
9498    } else {
9499       double image_dx=(double)0, image_dy=(double)0;
9500 
9501       ReverseTransformDoublePointThroughCTM(
9502             ((double)(abs_x-obj_ptr->x)+0.5),
9503             ((double)(abs_y-obj_ptr->y)+0.5),
9504             obj_ptr->ctm, &image_dx, &image_dy);
9505       image_dx += (double)(obj_ptr->x-obj_ptr->orig_obbox.ltx);
9506       image_dy += (double)(obj_ptr->y-obj_ptr->orig_obbox.lty);
9507       if (image_dx >= ((double)0) && image_dy >= ((double)0) &&
9508             image_dx < ((double)image_w) &&
9509             image_dy < ((double)image_h)) {
9510          image_x = (int)image_dx;
9511          image_y = (int)image_dy;
9512          if (image_x < 0) image_x = 0;
9513          if (image_x >= image_w) image_x = image_w-1;
9514          if (image_y < 0) image_y = 0;
9515          if (image_y >= image_h) image_y = image_h-1;
9516          found = TRUE;
9517       }
9518    }
9519    if (found) {
9520       changed = TRUE;
9521       if (somethingHighLighted) HighLightReverse();
9522       StartFloodFill(obj_ptr, image, bitmap_image, image_x, image_y,
9523             image_w, image_h, button==Button1);
9524       if (!somethingHighLighted) HighLightForward();
9525    }
9526    return changed;
9527 }
9528 
9529 static
GetUntransformedPoint(obj_ptr,abs_x,abs_y,v)9530 void GetUntransformedPoint(obj_ptr, abs_x, abs_y, v)
9531    struct ObjRec *obj_ptr;
9532    int abs_x, abs_y;
9533    IntPoint *v;
9534 {
9535    double image_dx=(double)0, image_dy=(double)0;
9536 
9537    ReverseTransformDoublePointThroughCTM(
9538          ((double)(abs_x-obj_ptr->x)+0.5),
9539          ((double)(abs_y-obj_ptr->y)+0.5),
9540          obj_ptr->ctm, &image_dx, &image_dy);
9541    image_dx += (double)(obj_ptr->x-obj_ptr->orig_obbox.ltx);
9542    image_dy += (double)(obj_ptr->y-obj_ptr->orig_obbox.lty);
9543    v->x = (int)image_dx;
9544    v->y = (int)image_dy;
9545 }
9546 
9547 static
ContinueFloodFill(obj_ptr)9548 int ContinueFloodFill(obj_ptr)
9549    struct ObjRec *obj_ptr;
9550 {
9551    int done=FALSE, image_w, image_h, changed=FALSE;
9552    struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
9553    XImage *image=NULL, *bitmap_image=NULL;
9554 
9555    xpm_ptr = obj_ptr->detail.xpm;
9556    image_w = xpm_ptr->image_w;
9557    image_h = xpm_ptr->image_h;
9558    image = xpm_ptr->image;
9559    bitmap_image = xpm_ptr->bitmap_image;
9560    if (image == NULL) {
9561       image = xpm_ptr->image = XGetImage(mainDisplay, xpm_ptr->pixmap, 0, 0,
9562             image_w, image_h, AllPlanes, ZPixmap);
9563       if (image == NULL) FailAllocMessage();
9564    }
9565    if (xpm_ptr->bitmap != None && bitmap_image == NULL) {
9566       bitmap_image = xpm_ptr->bitmap_image = XGetImage(mainDisplay,
9567             xpm_ptr->bitmap, 0, 0, image_w, image_h, AllPlanes, ZPixmap);
9568       if (bitmap_image == NULL) FailAllocMessage();
9569    }
9570    SaveStatusStrings();
9571    sprintf(gszMsgBox, "    %s", TgLoadString(STID_ESC_TO_FINISH));
9572    TwoLineMsg(TgLoadString(STID_BTN1_FLOODFILL_BTN3_SET_COLOR), gszMsgBox);
9573    SetMouseStatus(TgLoadString(STID_FLOOD_FILL),
9574          TgLoadCachedString(CSTID_FINISH), TgLoadString(STID_SET_A_PIXEL));
9575    while (!done) {
9576       int mouse_x=0, mouse_y=0, mouse_end_x=0, mouse_end_y=0;
9577       unsigned int button=FillReplacePickAPoint(&mouse_x, &mouse_y,
9578             &mouse_end_x, &mouse_end_y, TRUE, floodCursor);
9579 
9580       SetWatchCursor(drawWindow);
9581       SetWatchCursor(mainWindow);
9582       if (button == Button1) {
9583          changed = TryFloodFill(obj_ptr, image_w, image_h, image, bitmap_image,
9584                button, mouse_x, mouse_y);
9585       } else if (button == Button3) {
9586          if (mouse_x == mouse_end_x && mouse_y == mouse_end_y) {
9587             changed = TryFloodFill(obj_ptr, image_w, image_h, image,
9588                   bitmap_image, button, mouse_x, mouse_y);
9589          } else if (obj_ptr->ctm == NULL) {
9590             int r=0;
9591             struct BBRec bbox;
9592 
9593             SetBBRec(&bbox, mouse_x, mouse_y, mouse_end_x, mouse_end_y);
9594             for (r=bbox.lty; r < bbox.rby; r++) {
9595                int c=0;
9596 
9597                for (c=bbox.ltx; c < bbox.rbx; c++) {
9598                   if (TryFloodFill(obj_ptr, image_w, image_h, image,
9599                         bitmap_image, button, c, r)) {
9600                      changed = TRUE;
9601                   }
9602                }
9603             }
9604          } else {
9605             int r=0, abs_x=ABS_X(mouse_x), abs_y=ABS_Y(mouse_y);
9606             int abs_end_x=ABS_X(mouse_end_x), abs_end_y=ABS_Y(mouse_end_y);
9607             int ltx=0, lty=0, rbx=0, rby=0;
9608             IntPoint ivs[6];
9609 
9610             GetUntransformedPoint(obj_ptr, abs_x, abs_y, &ivs[0]);
9611             GetUntransformedPoint(obj_ptr, abs_x, abs_end_y, &ivs[1]);
9612             GetUntransformedPoint(obj_ptr, abs_end_x, abs_end_y, &ivs[2]);
9613             GetUntransformedPoint(obj_ptr, abs_end_x, abs_y, &ivs[3]);
9614             memcpy(&ivs[4], &ivs[0], sizeof(IntPoint));
9615             ltx = min(min(ivs[0].x, ivs[1].x),min(ivs[2].x, ivs[3].x));
9616             lty = min(min(ivs[0].y, ivs[1].y),min(ivs[2].y, ivs[3].y));
9617             rbx = max(max(ivs[0].x, ivs[1].x),max(ivs[2].x, ivs[3].x));
9618             rby = max(max(ivs[0].y, ivs[1].y),max(ivs[2].y, ivs[3].y));
9619 
9620             for (r=lty; r < rby; r++) {
9621                int c=0;
9622 
9623                if (r < 0 || r >= image_h) continue;
9624                for (c=ltx; c < rbx; c++) {
9625                   if (c < 0 || c >= image_w) continue;
9626 
9627                   if (PointInIntPolygon(c, r, 5, ivs)) {
9628                      StartFloodFill(obj_ptr, image, bitmap_image, c, r,
9629                            c, r, FALSE);
9630                      changed = TRUE;
9631                   }
9632                }
9633             }
9634          }
9635       } else {
9636          done = TRUE;
9637       }
9638       if (!done) {
9639          if (changed) {
9640             XPutImage(mainDisplay, xpm_ptr->pixmap, xpmGC, image, 0, 0, 0, 0,
9641                image_w, image_h);
9642             if (bitmap_image != NULL) {
9643                XPutImage(mainDisplay, xpm_ptr->bitmap, xbmGC, bitmap_image,
9644                   0, 0, 0, 0, image_w, image_h);
9645             }
9646             if (xpm_ptr->cached_pixmap != None) {
9647                XFreePixmap(mainDisplay, xpm_ptr->cached_pixmap);
9648                xpm_ptr->cached_pixmap = None;
9649             }
9650             if (xpm_ptr->cached_bitmap != None) {
9651                XFreePixmap(mainDisplay, xpm_ptr->cached_bitmap);
9652                xpm_ptr->cached_bitmap = None;
9653             }
9654             if ((xpm_ptr->real_type == XPM_JPEG ||
9655                   xpm_ptr->real_type == PPM_TRUE) &&
9656                   fullTrueColorMode && HasZlibSupport()) {
9657                unsigned int ppm_data_size=0;
9658                char tmp_fname[MAXPATHLENGTH], ext[MAXPATHLENGTH];
9659                char *ppm_data=NULL;
9660 
9661                *tmp_fname = *ext = '\0';
9662                if (MkTempFile(tmp_fname, sizeof(tmp_fname), tmpDir,
9663                      TOOL_NAME) == NULL) {
9664                   /* print error message? */
9665                   return FALSE;
9666                }
9667                if (!DumpXImageToFile(xpm_ptr->image, xpm_ptr->image_w,
9668                      xpm_ptr->image_h, tmp_fname, ext)) {
9669                   /* print error message? */
9670                   return FALSE;
9671                }
9672                if (strcmp(ext, ".ppm.z") == 0) {
9673                   char deflated_fname[MAXPATHLENGTH];
9674 
9675                   snprintf(deflated_fname, sizeof(deflated_fname), "%s%s",
9676                         tmp_fname, ext);
9677                   ppm_data = ReadFileIntoBuf(deflated_fname, &ppm_data_size);
9678                   unlink(deflated_fname);
9679                }
9680                if (ppm_data != NULL) {
9681                   if (xpm_ptr->ppm_data != NULL) free(xpm_ptr->ppm_data);
9682                   if (xpm_ptr->ppm_mask_data != NULL) {
9683                      free(xpm_ptr->ppm_mask_data);
9684                   }
9685                   xpm_ptr->real_type = PPM_TRUE;
9686                   xpm_ptr->ppm_data = ppm_data;
9687                   xpm_ptr->ppm_data_size = ppm_data_size;
9688                   xpm_ptr->ppm_mask_data = NULL;
9689                   xpm_ptr->ppm_mask_size = 0;
9690                   xpm_ptr->ppm_data_compress = PPM_DATA_DEFLATED;
9691                }
9692             }
9693             AdjObjCache(obj_ptr);
9694             if (somethingHighLighted) HighLightReverse();
9695             RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1),
9696                   selLtY-GRID_ABS_SIZE(1), selRbX+GRID_ABS_SIZE(1),
9697                   selRbY+GRID_ABS_SIZE(1));
9698             if (!somethingHighLighted) HighLightForward();
9699             SetFileModified(TRUE);
9700             justDupped = FALSE;
9701          } else {
9702             SetStringStatus(TgLoadString(STID_SEL_PT_NOT_ON_IMAGE));
9703          }
9704       }
9705       SetDefaultCursor(mainWindow);
9706       ShowCursor();
9707    }
9708    RestoreStatusStrings();
9709    return changed;
9710 }
9711 
FloodFill()9712 void FloodFill()
9713 {
9714    struct ObjRec *obj_ptr=NULL;
9715    unsigned char trans_color_r='\0', trans_color_g='\0', trans_color_b='\0';
9716 
9717    if (!CheckSelectionForImageProc(CMDID_FLOODFILL)) {
9718       return;
9719    }
9720    obj_ptr = topSel->obj;
9721    if (ObjHasTrueColorTransPixel(obj_ptr, &trans_color_r, &trans_color_g,
9722          &trans_color_b)) {
9723       if (CurColorIsTranscolor(trans_color_r, trans_color_g, trans_color_b)) {
9724          sprintf(gszMsgBox, TgLoadString(STID_CUR_COLOR_IS_TRANS_PIXEL),
9725                colorMenuItems[colorIndex],
9726                GetImageProcName(CMDID_FLOODFILL));
9727          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
9728          return;
9729       }
9730    }
9731    if (somethingHighLighted) HighLightReverse();
9732    XSync(mainDisplay, False);
9733    PrepareToReplaceAnObj(obj_ptr);
9734 
9735    ResetThreshFillReplaceInfo();
9736    if (ContinueFloodFill(obj_ptr)) {
9737       RecordReplaceAnObj(obj_ptr);
9738    } else {
9739       AbortPrepareCmd(CMD_REPLACE);
9740    }
9741    if (!somethingHighLighted) HighLightForward();
9742 }
9743 
9744 /* ----------------------- ToggleFloodReplaceColorThreshold ----------------------- */
9745 
ToggleFloodReplaceColorThreshold()9746 void ToggleFloodReplaceColorThreshold()
9747 {
9748    threshFillReplaceEnabled = !threshFillReplaceEnabled;
9749 
9750    if (threshFillReplaceEnabled) {
9751       sprintf(gszMsgBox, TgLoadString(STID_FLOOD_REPLACE_ENABLED),
9752             fillReplaceRedThresh, fillReplaceGreenThresh,
9753             fillReplaceBlueThresh);
9754       Msg(gszMsgBox);
9755    } else {
9756       sprintf(gszMsgBox, TgLoadString(STID_FLOOD_REPLACE_DISABLED));
9757       Msg(gszMsgBox);
9758    }
9759 }
9760 
9761 /* ----------------------- SetFloodReplaceColorThreshold ----------------------- */
9762 
SetFloodReplaceColorThreshold()9763 void SetFloodReplaceColorThreshold()
9764 {
9765    char spec[MAXSTRING];
9766 
9767    *spec = '\0';
9768    sprintf(gszMsgBox, TgLoadString(STID_SET_FLOOD_REPLACE_THRESH),
9769          fillReplaceRedThresh, fillReplaceGreenThresh,
9770          fillReplaceBlueThresh);
9771    if (Dialog(gszMsgBox, NULL, spec) == INVALID) return;
9772    UtilTrimBlanks(spec);
9773    if (*spec == '\0') return;
9774 
9775    if (ParseThreshFillReplaceSpec(spec)) {
9776       sprintf(gszMsgBox, TgLoadString(STID_FLOOD_REPLACE_THRESH_SET_TO),
9777             fillReplaceRedThresh, fillReplaceGreenThresh,
9778             fillReplaceBlueThresh);
9779       Msg(gszMsgBox);
9780    }
9781 }
9782 
9783 /* ----------------------- CreateContour ----------------------- */
9784 
9785 /*------------------------------------------------*/
9786 /* the contour code is adapted from Tim Feldman's */
9787 /*        contour code in Graphics Gem III        */
9788 /*------------------------------------------------*/
9789 
9790 typedef struct CDirInfo {
9791    /* 5 6 7 */
9792    /* 4   0 */
9793    /* 3 2 1 */
9794    short dir;
9795    struct CDirInfo *next;
9796 } CDirInfoPtr;
9797 
9798 static struct CDirInfo *topOfChain=NULL, *botOfChain=NULL;
9799 
9800 static int gnContourX=0, gnContourY=0;
9801 static int gnContourW=0, gnContourH=0;
9802 static unsigned short gnContourRed=0, gnContourGreen=0, gnContourBlue=0;
9803 static int **gnaContourPixels=NULL;
9804 static XImage *gContourImage=NULL, *gContourBitmapImage=NULL;
9805 static struct ObjRec *gpContourObj=NULL;
9806 
9807 static
CleanUpContour()9808 void CleanUpContour()
9809 {
9810    if (gnaContourPixels != NULL) {
9811       int i;
9812 
9813       for (i=0; i < gnContourH; i++) {
9814          if (gnaContourPixels[i] != NULL) {
9815             free(gnaContourPixels[i]);
9816          } else {
9817             break;
9818          }
9819       }
9820       free(gnaContourPixels);
9821    }
9822    gnaContourPixels = NULL;
9823 
9824    for ( ; topOfChain != NULL; topOfChain=botOfChain) {
9825       botOfChain = topOfChain->next;
9826       free(topOfChain);
9827    }
9828    topOfChain = botOfChain = NULL;
9829 }
9830 
9831 static
OnContour(x,y)9832 int OnContour(x, y)
9833    int x, y;
9834 {
9835    if (x >= 0 && x < gnContourW && y >= 0 && y < gnContourH) {
9836       int index;
9837 
9838       if (gnaContourPixels[y][x] == BAD) {
9839          gnaContourPixels[y][x] = XGetPixel(gContourImage, x, y);
9840       }
9841       index = GetIndexOfPixel(gnaContourPixels[y][x]);
9842       if (tgifColors[index].red == gnContourRed &&
9843             tgifColors[index].green == gnContourGreen &&
9844             tgifColors[index].blue == gnContourBlue) {
9845          return TRUE;
9846       }
9847    }
9848    return FALSE;
9849 }
9850 
9851 static
ProbeContour(x,y,dir,pn_new_x,pn_new_y)9852 int ProbeContour(x, y, dir, pn_new_x, pn_new_y)
9853    int x, y, dir, *pn_new_x, *pn_new_y;
9854 {
9855    switch (dir) {
9856    case 0: x++;      break;
9857    case 1: x++; y++; break;
9858    case 2:      y++; break;
9859    case 3: x--; y++; break;
9860    case 4: x--;      break;
9861    case 5: x--; y--; break;
9862    case 6:      y--; break;
9863    case 7: x++; y--; break;
9864    }
9865    *pn_new_x = x;
9866    *pn_new_y = y;
9867    return OnContour(x, y);
9868 }
9869 
9870 static
ContourNeighbor(x,y,last_dir,pn_new_x,pn_new_y)9871 int ContourNeighbor(x, y, last_dir, pn_new_x, pn_new_y)
9872    int x, y, last_dir, *pn_new_x, *pn_new_y;
9873    /*
9874     * if last vector was 0, start looking at 1
9875     * if last vector was 1, start looking at 3
9876     * if last vector was 2, start looking at 3
9877     * if last vector was 3, start looking at 5
9878     * if last vector was 4, start looking at 5
9879     * if last vector was 5, start looking at 7
9880     * if last vector was 6, start looking at 7
9881     * if last vector was 7, start looking at 1
9882     */
9883 {
9884    int i;
9885 
9886    if (last_dir & 0x1) {
9887       last_dir += 2;
9888    } else {
9889       last_dir++;
9890    }
9891    if (last_dir > 7) last_dir -= 8;
9892    for (i=0; i < 8; i++) {
9893       if (ProbeContour(x, y, last_dir, pn_new_x, pn_new_y)) {
9894          return last_dir;
9895       } else {
9896          if (--last_dir < 0) last_dir += 8;
9897       }
9898    }
9899 #ifdef _TGIF_DBG /* debug, do not translate */
9900    fprintf(stderr, "Should not have come here ContourNeighbor()!\n");
9901 #endif /* _TGIF_DBG */
9902    return (-1);
9903 }
9904 
9905 static
CreatePolyFromContour(num_pts)9906 int CreatePolyFromContour(num_pts)
9907    int num_pts;
9908 {
9909    int x=gnContourX, y=gnContourY, generate=(num_pts > 2);
9910    struct CDirInfo *pcdi=topOfChain;
9911    struct XfrmMtrxRec *ctm=NULL;
9912 
9913    if (generate) {
9914       ResetCreatePolygon();
9915       ctm = gpContourObj->ctm;
9916    } else {
9917       num_pts = 0;
9918    }
9919    while (pcdi != NULL) {
9920       int dir=pcdi->dir, n=1;
9921       struct CDirInfo *pcdi1=pcdi->next;
9922 
9923       for ( ; pcdi1 != NULL; pcdi1=pcdi1->next) {
9924          if (pcdi1->dir != dir) {
9925             break;
9926          } else {
9927             n++;
9928          }
9929       }
9930       if (generate) {
9931          if (ctm == NULL) {
9932             AddPtToCreatePolygon(gpContourObj->x+x, gpContourObj->y+y);
9933          } else {
9934             int tmp_x, tmp_y;
9935 
9936             TransformPointThroughCTM(x, y, ctm, &tmp_x, &tmp_y);
9937             AddPtToCreatePolygon(gpContourObj->x+tmp_x, gpContourObj->y+tmp_y);
9938          }
9939       } else {
9940          num_pts++;
9941       }
9942       switch (dir) {
9943       case 0: x += n;         break;
9944       case 1: x += n; y += n; break;
9945       case 2:         y += n; break;
9946       case 3: x -= n; y += n; break;
9947       case 4: x -= n;         break;
9948       case 5: x -= n; y -= n; break;
9949       case 6:         y -= n; break;
9950       case 7: x += n; y -= n; break;
9951       }
9952       pcdi = pcdi1;
9953    }
9954    if (generate) {
9955       if (ctm == NULL) {
9956          AddPtToCreatePolygon(gpContourObj->x+x, gpContourObj->y+y);
9957       } else {
9958          int tmp_x, tmp_y;
9959 
9960          TransformPointThroughCTM(x, y, ctm, &tmp_x, &tmp_y);
9961          AddPtToCreatePolygon(gpContourObj->x+tmp_x, gpContourObj->y+tmp_y);
9962       }
9963       CreatePolygonObj(num_pts, TRUE);
9964    } else {
9965       num_pts++;
9966    }
9967    if (generate) {
9968       return TRUE;
9969    }
9970    return num_pts;
9971 }
9972 
9973 static
DoCreateContour()9974 int DoCreateContour()
9975 {
9976    int x, y, dir, new_x, new_y, last_dir, num_pts=0;
9977 
9978    while (OnContour(gnContourX, gnContourY)) {
9979       gnContourX--;
9980    }
9981    gnContourX++;
9982    topOfChain = NULL;
9983 
9984    x = new_x = gnContourX;
9985    y = new_y = gnContourY;
9986    dir = 0;
9987    for (;;) {
9988       if (ProbeContour(x, y, dir, &new_x, &new_y)) {
9989          break;
9990       } else if (++dir >= 8) {
9991          MsgBox(TgLoadString(STID_NO_CONTOUR_CAN_BE_GEN_HERE), TOOL_NAME,
9992                INFO_MB);
9993          return FALSE;
9994       }
9995    }
9996    last_dir = 1;
9997    for (;;) {
9998       struct CDirInfo *pcdi;
9999 
10000       dir = ContourNeighbor(x, y, last_dir, &new_x, &new_y);
10001       pcdi = (struct CDirInfo *)malloc(sizeof(struct CDirInfo));
10002       if (pcdi == NULL) FailAllocMessage();
10003       memset(pcdi, 0, sizeof(struct CDirInfo));
10004       pcdi->dir = dir;
10005       pcdi->next = NULL;
10006       if (botOfChain == NULL) {
10007          topOfChain = pcdi;
10008       } else {
10009          botOfChain->next = pcdi;
10010       }
10011       botOfChain = pcdi;
10012       if (new_x == gnContourX && new_y == gnContourY) {
10013          break;
10014       }
10015       x = new_x;
10016       y = new_y;
10017       last_dir = dir;
10018    }
10019    num_pts = CreatePolyFromContour(0);
10020    if (num_pts > 2) {
10021       return CreatePolyFromContour(num_pts);
10022    }
10023    MsgBox(TgLoadString(STID_NO_CONTOUR_CAN_BE_GEN_HERE), TOOL_NAME, INFO_MB);
10024    return FALSE;
10025 }
10026 
10027 static
StartCreateContour(obj_ptr,image,bitmap_image,image_x,image_y,image_w,image_h)10028 int StartCreateContour(obj_ptr, image, bitmap_image, image_x, image_y,
10029       image_w, image_h)
10030    struct ObjRec *obj_ptr;
10031    XImage *image, *bitmap_image;
10032    int image_x, image_y, image_w, image_h;
10033 {
10034    int i, pixel=(-1), created=FALSE;
10035 
10036    gnContourX = image_x;
10037    gnContourY = image_y;
10038    gnContourW = image_w;
10039    gnContourH = image_h;
10040    gContourImage = image;
10041    gContourBitmapImage = bitmap_image;
10042    gpContourObj = obj_ptr;
10043 
10044    gnaContourPixels = (int**)malloc(image_h*sizeof(int*));
10045    if (gnaContourPixels == NULL) {
10046       return FailAllocMessage();
10047    }
10048    memset(gnaContourPixels, 0, image_h*sizeof(int*));
10049    for (i=0; i < image_h; i++) {
10050       int j;
10051 
10052       gnaContourPixels[i] = (int*)malloc(image_w*sizeof(int));
10053       if (gnaContourPixels[i] == NULL) {
10054          FailAllocMessage();
10055          CleanUpContour();
10056          return FALSE;
10057       }
10058       for (j=0; j < image_w; j++) gnaContourPixels[i][j] = BAD;
10059    }
10060    if (!CreatePixelToIndexMapping()) {
10061       CleanUpContour();
10062       return FALSE;
10063    }
10064    if (bitmap_image == NULL) {
10065       pixel = XGetPixel(image, image_x, image_y);
10066    } else {
10067       if (XGetPixel(bitmap_image, image_x, image_y) == 0) {
10068          /* transparent */
10069       } else {
10070          pixel = XGetPixel(image, image_x, image_y);
10071       }
10072    }
10073    SetWatchCursor(drawWindow);
10074    SetWatchCursor(mainWindow);
10075    gnPixelToFill = colorPixels[colorIndex];
10076    if (pixel == (-1)) {
10077    } else {
10078       int index=GetIndexOfPixel(pixel);
10079 
10080       gnContourRed = tgifColors[index].red;
10081       gnContourGreen = tgifColors[index].green;
10082       gnContourBlue = tgifColors[index].blue;
10083       created = DoCreateContour();
10084    }
10085    SetDefaultCursor(mainWindow);
10086    ShowCursor();
10087 
10088    CleanUpIndexOfPixel();
10089    CleanUpContour();
10090 
10091    return created;
10092 }
10093 
10094 static
ContinueCreateContour(obj_ptr)10095 int ContinueCreateContour(obj_ptr)
10096    struct ObjRec *obj_ptr;
10097 {
10098    int image_w, image_h, changed=FALSE, mouse_x=0, mouse_y=0;
10099    unsigned int button;
10100    struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
10101    XImage *image, *bitmap_image;
10102 
10103    xpm_ptr = obj_ptr->detail.xpm;
10104    image_w = xpm_ptr->image_w;
10105    image_h = xpm_ptr->image_h;
10106    image = xpm_ptr->image;
10107    bitmap_image = xpm_ptr->bitmap_image;
10108    if (image == NULL) {
10109       image = xpm_ptr->image = XGetImage(mainDisplay, xpm_ptr->pixmap, 0, 0,
10110             image_w, image_h, AllPlanes, ZPixmap);
10111       if (image == NULL) FailAllocMessage();
10112    }
10113    if (xpm_ptr->bitmap != None && bitmap_image == NULL) {
10114       bitmap_image = xpm_ptr->bitmap_image = XGetImage(mainDisplay,
10115             xpm_ptr->bitmap, 0, 0, image_w, image_h, AllPlanes, ZPixmap);
10116       if (bitmap_image == NULL) FailAllocMessage();
10117    }
10118    SaveStatusStrings();
10119    Msg(TgLoadString(STID_SEL_A_COLOR_TO_BE_TRACED));
10120    SetMouseStatus(TgLoadString(STID_START_CONTOUR),
10121          TgLoadCachedString(CSTID_FINISH), TgLoadCachedString(CSTID_FINISH));
10122 
10123    button = FillReplacePickAPoint(&mouse_x, &mouse_y, NULL, NULL, FALSE,
10124          handCursor);
10125 
10126    if (button == Button1) {
10127       int abs_x=ABS_X(mouse_x), abs_y=ABS_Y(mouse_y), found=FALSE;
10128       int image_x=0, image_y=0;
10129 
10130       if (obj_ptr->ctm == NULL) {
10131          if (abs_x >= obj_ptr->obbox.ltx && abs_y >= obj_ptr->obbox.lty &&
10132                abs_x < obj_ptr->obbox.rbx && abs_y < obj_ptr->obbox.rby) {
10133             image_x = abs_x-obj_ptr->obbox.ltx;
10134             image_y = abs_y-obj_ptr->obbox.lty;
10135             if (image_x >= 0 && image_y >= 0 &&
10136                   image_x < image_w && image_y < image_h) {
10137                found = TRUE;
10138             }
10139          }
10140       } else {
10141          double image_dx=(double)0, image_dy=(double)0;
10142 
10143          ReverseTransformDoublePointThroughCTM(
10144                ((double)(abs_x-obj_ptr->x)+0.5),
10145                ((double)(abs_y-obj_ptr->y)+0.5),
10146                obj_ptr->ctm, &image_dx, &image_dy);
10147          image_x += (double)(obj_ptr->x-obj_ptr->orig_obbox.ltx);
10148          image_y += (double)(obj_ptr->y-obj_ptr->orig_obbox.lty);
10149          if (image_dx >= ((double)0) && image_dy >= ((double)0) &&
10150                image_dx < ((double)image_w) && image_dy < ((double)image_h)) {
10151             image_x = (int)image_dx;
10152             image_y = (int)image_dy;
10153             if (image_x < 0) image_x = 0;
10154             if (image_x >= image_w) image_x = image_w-1;
10155             if (image_y < 0) image_y = 0;
10156             if (image_y >= image_h) image_y = image_h-1;
10157             found = TRUE;
10158          }
10159       }
10160       if (found) {
10161          if (somethingHighLighted) HighLightReverse();
10162          changed = StartCreateContour(obj_ptr, image, bitmap_image, image_x,
10163                image_y, image_w, image_h);
10164          if (!somethingHighLighted) HighLightForward();
10165       } else {
10166          SetStringStatus(TgLoadString(STID_SEL_PT_NOT_ON_IMAGE));
10167       }
10168    }
10169    RestoreStatusStrings();
10170    return changed;
10171 }
10172 
CreateContour()10173 void CreateContour()
10174 {
10175    struct ObjRec *obj_ptr;
10176 
10177    if (!CheckSelectionForImageProc(CMDID_CREATECONTOUR)) {
10178       return;
10179    }
10180    obj_ptr = topSel->obj;
10181 
10182    if (somethingHighLighted) HighLightReverse();
10183    XSync(mainDisplay, False);
10184    if (ContinueCreateContour(obj_ptr)) {
10185       if (somethingHighLighted) HighLightReverse();
10186       RemoveAllSel();
10187       numRedrawBBox = 0;
10188       obj_ptr->tmp_parent = NULL;
10189       DrawObj(drawWindow, topObj);
10190       SelectTopObj();
10191       RecordNewObjCmd();
10192       SetFileModified(TRUE);
10193       justDupped = FALSE;
10194    } else {
10195       if (!somethingHighLighted) HighLightForward();
10196    }
10197 }
10198 
10199 /* ----------------------- Init and Clean Up ----------------------- */
10200 
CleanUpImageProc()10201 void CleanUpImageProc()
10202 {
10203    CleanUpConvolution();
10204 }
10205 
10206 /* do not translate -- program constants */
10207 static char gszDefBggen[]="bggen %s -g %s | ppmquant 64 | ppmtoxpm";
10208 static char gszDefPpm6Bggen[]="bggen %s -g %s";
10209 static char gszDefPpmquant[]="pnmquant %d %s";
10210 static char gszDefPpmFSquant[]="pnmquant -fs %d %s";
10211 
InitImageProc()10212 void InitImageProc()
10213 {
10214    char *c_ptr=NULL, spec[MAXSTRING];
10215 
10216    memset(&gConvExtraInfo, 0, sizeof(ConvExtraInfo));
10217    memset(&gThreshFillReplaceInfo, 0, sizeof(gThreshFillReplaceInfo));
10218 
10219    gnQuantizingLevels = 222;
10220    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"QuantizingLevels")) != NULL) {
10221       gnQuantizingLevels = atoi(c_ptr);
10222       if (gnQuantizingLevels < 2 || gnQuantizingLevels > 256) {
10223          fprintf(stderr, TgLoadString(STID_INVALID_XDEF_RNG_USE_ALT_VAL),
10224                TOOL_NAME, "QuantizingLevels", c_ptr, 2, 256, 256-maxColors);
10225          gnQuantizingLevels = 256-maxColors;
10226       }
10227    }
10228    strcpy(bggenToXpmCmd, gszDefBggen);
10229    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"BggenToXpm")) != NULL) {
10230       int count=0;
10231 
10232       UtilStrCpyN(bggenToXpmCmd, sizeof(bggenToXpmCmd), c_ptr);
10233       for (c_ptr=strstr(bggenToXpmCmd,"%s"); c_ptr!=NULL;
10234             c_ptr=strstr(++c_ptr,"%s")) {
10235          count++;
10236       }
10237       if (count != 2) {
10238          sprintf(gszMsgBox, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
10239                TOOL_NAME, "BggenToXpm", bggenToXpmCmd, gszDefBggen);
10240          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
10241          strcpy(bggenToXpmCmd, gszDefBggen);
10242       }
10243    }
10244    strcpy(bggenToPpm6Cmd, gszDefPpm6Bggen);
10245    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"BggenToPpm6")) != NULL) {
10246       int count=0;
10247 
10248       UtilStrCpyN(bggenToPpm6Cmd, sizeof(bggenToPpm6Cmd), c_ptr);
10249       for (c_ptr=strstr(bggenToPpm6Cmd,"%s"); c_ptr!=NULL;
10250             c_ptr=strstr(++c_ptr,"%s")) {
10251          count++;
10252       }
10253       if (count != 2) {
10254          sprintf(gszMsgBox, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
10255                TOOL_NAME, "BggenToPpm6", bggenToPpm6Cmd, gszDefPpm6Bggen);
10256          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
10257          strcpy(bggenToPpm6Cmd, gszDefPpm6Bggen);
10258       }
10259    }
10260    strcpy(ppmquantCmd, gszDefPpmquant);
10261    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"PpmQuantize")) != NULL) {
10262       int count=0;
10263 
10264       UtilStrCpyN(ppmquantCmd, sizeof(ppmquantCmd), c_ptr);
10265       for (c_ptr=strstr(ppmquantCmd,"%s"); c_ptr!=NULL;
10266             c_ptr=strstr(++c_ptr,"%s")) {
10267          count++;
10268       }
10269       if (count != 1) {
10270          sprintf(gszMsgBox, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
10271                TOOL_NAME, "PpmQuantize", ppmquantCmd, gszDefPpmquant);
10272          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
10273          strcpy(ppmquantCmd, gszDefPpmquant);
10274       }
10275       for (c_ptr=strstr(ppmquantCmd,"%d"); c_ptr!=NULL;
10276             c_ptr=strstr(++c_ptr,"%d")) {
10277          count++;
10278       }
10279       if (count != 1) {
10280          sprintf(gszMsgBox, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
10281                TOOL_NAME, "PpmQuantize", ppmquantCmd, gszDefPpmquant);
10282          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
10283          strcpy(ppmquantCmd, gszDefPpmquant);
10284       }
10285    }
10286    strcpy(ppmFSquantCmd, gszDefPpmFSquant);
10287    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"PpmFSQuantize")) != NULL) {
10288       int count=0;
10289 
10290       UtilStrCpyN(ppmFSquantCmd, sizeof(ppmFSquantCmd), c_ptr);
10291       for (c_ptr=strstr(ppmFSquantCmd,"%s"); c_ptr!=NULL;
10292             c_ptr=strstr(++c_ptr,"%s")) {
10293          count++;
10294       }
10295       if (count != 1) {
10296          sprintf(gszMsgBox, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
10297                TOOL_NAME, "PpmQuantize", ppmFSquantCmd, gszDefPpmFSquant);
10298          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
10299          strcpy(ppmFSquantCmd, gszDefPpmFSquant);
10300       }
10301       for (c_ptr=strstr(ppmFSquantCmd,"%d"); c_ptr!=NULL;
10302             c_ptr=strstr(++c_ptr,"%d")) {
10303          count++;
10304       }
10305       if (count != 1) {
10306          sprintf(gszMsgBox, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
10307                TOOL_NAME, "PpmQuantize", ppmFSquantCmd, gszDefPpmFSquant);
10308          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
10309          strcpy(ppmFSquantCmd, gszDefPpmFSquant);
10310       }
10311    }
10312    gDefErrorDiffuseLevel.red = gDefErrorDiffuseLevel.green =
10313          gDefErrorDiffuseLevel.blue = 2;
10314    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"DefaultErrorDiffuseLevels")) !=
10315          NULL) {
10316       XColor xcolor;
10317       char *dup_buf=UtilStrDup(c_ptr);
10318 
10319       if (dup_buf == NULL) FailAllocMessage();
10320       switch (ParseDefaultColorLevels(dup_buf, &xcolor)) {
10321       case PDCL_OK:
10322          gDefErrorDiffuseLevel.red = xcolor.red;
10323          gDefErrorDiffuseLevel.green = xcolor.green;
10324          gDefErrorDiffuseLevel.blue = xcolor.blue;
10325          break;
10326       case PDCL_TOO_LARGE:
10327          fprintf(stderr, TgLoadString(STID_VAL_TOO_LARGE_IN_XDEF_USE_ALT),
10328                TOOL_NAME, "DefaultErrorDiffuseLevels", c_ptr, "2 2 2");
10329          fprintf(stderr, "\n");
10330          break;
10331       case PDCL_TOO_SMALL:
10332          fprintf(stderr, TgLoadString(STID_VAL_TOO_SMALL_IN_XDEF_USE_ALT),
10333                TOOL_NAME, "DefaultErrorDiffuseLevels", c_ptr, "2 2 2");
10334          break;
10335       case PDCL_BAD_FORMAT:
10336          fprintf(stderr, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
10337                TOOL_NAME, "DefaultErrorDiffuseLevels", c_ptr, "2 2 2");
10338          break;
10339       }
10340       free(dup_buf);
10341    }
10342    memset(gaHGBucket, 0, sizeof(gaHGBucket));
10343    memset(&gTrueColorInfo, 0, sizeof(TrueColorInfo));
10344 
10345    threshFillReplaceEnabled = FALSE;
10346    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,
10347          "EnableThresholdFloodReplaceColor")) != NULL &&
10348          UtilStrICmp(c_ptr, "true") == 0) {
10349       threshFillReplaceEnabled = TRUE;
10350    }
10351    *spec = '\0';
10352    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,
10353          "FloodReplaceColorThreshold")) != NULL) {
10354       UtilStrCpyN(spec, sizeof(spec), c_ptr);
10355       UtilTrimBlanks(spec);
10356       if (!ParseThreshFillReplaceSpec(spec)) {
10357          fprintf(stderr, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
10358                TOOL_NAME, "FloodReplaceColorThreshold", spec,
10359                "15,15,15");
10360          fprintf(stderr, "\n");
10361       }
10362    }
10363 }
10364 
10365 /* ----------------------- Menu Functions ----------------------- */
10366 
RefreshImageProcMenu(menu)10367 int RefreshImageProcMenu(menu)
10368    TgMenu *menu;
10369 {
10370    int ok=TRUE;
10371 
10372    /* Enable Threshold-based Flood and Replace Colors */
10373    ok &= TgSetMenuItemCheckById(menu, CMDID_TOGGLEFLOODREPLACECOLORTHRESH,
10374          threshFillReplaceEnabled);
10375 
10376    /* Set Threshold for Flood and Replace Colors */
10377    ok &= TgEnableMenuItemById(menu, CMDID_SETFLOODREPLACECOLORTHRESH,
10378          threshFillReplaceEnabled);
10379 
10380    return ok;
10381 }
10382 
CreateImageProcMenu(parent_menu,x,y,menu_info,status_str_xlated)10383 TgMenu *CreateImageProcMenu(parent_menu, x, y, menu_info, status_str_xlated)
10384    TgMenu *parent_menu;
10385    int x, y;
10386    TgMenuInfo *menu_info;
10387    int status_str_xlated; /* ignored, always 0 */
10388 {
10389    TgMenu *menu=TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info, FALSE);
10390 
10391    if (menu != NULL) {
10392       if (!RefreshImageProcMenu(menu)) {
10393          return TgDestroyMenu(menu, TRUE);
10394       }
10395       menu->refresh_proc = ((RefreshMenuFunc*)RefreshImageProcMenu);
10396    }
10397    return menu;
10398 }
10399 
ImageProcMenu(X,Y,TrackMenubar)10400 int ImageProcMenu(X, Y, TrackMenubar)
10401    int  X, Y, TrackMenubar;
10402 {
10403    int rc=INVALID;
10404    TgMenu *menu=(imageProcMenuInfo.create_proc)(NULL, X, Y, &imageProcMenuInfo,
10405          FALSE);
10406 
10407    activeMenu = MENU_IMAGEPROC;
10408    if (menu != NULL) {
10409       menu->track_menubar = TrackMenubar;
10410 
10411       rc = TgMenuLoop(menu);
10412       TgDestroyMenu(menu, TRUE);
10413    }
10414    return rc;
10415 }
10416 
10417